diff --git a/.config/lychee.toml b/.config/lychee.toml index 1de9fcd559dd9ea14fb603b7740efebcb893ed93..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): @@ -32,7 +35,6 @@ exclude = [ "https://github.com/paritytech/polkadot-sdk/substrate/frame/timestamp", "https://github.com/paritytech/substrate/frame/fast-unstake", "https://github.com/zkcrypto/bls12_381/blob/e224ad4ea1babfc582ccd751c2bf128611d10936/src/test-data/mod.rs", - "https://polkadot-try-runtime-node.parity-chains.parity.io/", "https://polkadot.network/the-path-of-a-parachain-block/", "https://research.web3.foundation/en/latest/polkadot/NPoS/3.%20Balancing.html", "https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model", @@ -41,6 +43,7 @@ exclude = [ "https://research.web3.foundation/en/latest/polkadot/overview/2-token-economics.html#inflation-model", "https://research.web3.foundation/en/latest/polkadot/slashing/npos.html", "https://rpc.polkadot.io/", + "https://try-runtime.polkadot.io/", "https://w3f.github.io/parachain-implementers-guide/node/approval/approval-distribution.html", "https://w3f.github.io/parachain-implementers-guide/node/index.html", "https://w3f.github.io/parachain-implementers-guide/protocol-chain-selection.html", diff --git a/.config/zepter.yaml b/.config/zepter.yaml index 24441e90b1a0510d8be95e48515c8b41371117c8..7a67ba2695cf697c886b31520431ea0ee33e14c9 100644 --- a/.config/zepter.yaml +++ b/.config/zepter.yaml @@ -27,7 +27,7 @@ workflows: ] # The umbrella crate uses more features, so we to check those too: check_umbrella: - - [ $check.0, '--features=serde,experimental,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ] + - [ $check.0, '--features=serde,experimental,riscv,runtime,with-tracing,tuples-96,with-tracing', '-p=polkadot-sdk' ] # Same as `check_*`, but with the `--fix` flag. default: - [ $check.0, '--fix' ] diff --git a/.github/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/cargo-check-runtimes/action.yml b/.github/actions/cargo-check-runtimes/action.yml new file mode 100644 index 0000000000000000000000000000000000000000..869f17661e4a23e3c07256a8f59303c09fec7d9b --- /dev/null +++ b/.github/actions/cargo-check-runtimes/action.yml @@ -0,0 +1,22 @@ +name: 'cargo check runtimes' +description: 'Runs `cargo check` for every directory in provided root.' +inputs: + root: + description: "Root directory. Expected to contain several cargo packages inside." + required: true +runs: + using: "composite" + steps: + - name: Check + shell: bash + run: | + mkdir -p ~/.forklift + cp .forklift/config.toml ~/.forklift/config.toml + cd ${{ inputs.root }} + for directory in $(echo */); do + echo "_____Running cargo check for ${directory} ______"; + cd ${directory}; + pwd; + SKIP_WASM_BUILD=1 forklift cargo check --locked; + cd ..; + done diff --git a/.github/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/command-screnshot.png b/.github/command-screnshot.png deleted file mode 100644 index 1451fabca8b975534778e8321facd261e3b803fb..0000000000000000000000000000000000000000 Binary files a/.github/command-screnshot.png and /dev/null differ diff --git a/.github/commands-readme.md b/.github/commands-readme.md deleted file mode 100644 index 793524e056f8e113b0803405f2d5a238a7c7b285..0000000000000000000000000000000000000000 --- a/.github/commands-readme.md +++ /dev/null @@ -1,265 +0,0 @@ -# Running commands - -Command bot has been migrated, it is no longer a comment parser and now it is a GitHub action that works as a [`workflow_dispatch`](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch) event. - -## How to run an action - -To run an action, you need to go to the [_actions tab_](https://github.com/paritytech/polkadot-sdk/actions) and pick the one you desire to run. - -The current available command actions are: - -- [Command FMT](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-fmt.yml) -- [Command Update UI](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-update-ui.yml) -- [Command Sync](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-sync.yml) -- [Command Bench](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-bench.yml) -- [Command Bench All](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-bench-all.yml) -- [Command Bench Overhead](https://github.com/paritytech/polkadot-sdk/actions/workflows/command-bench-overhead.yml) - -You need to select the action, and click on the dropdown that says: `Run workflow`. It is located in the upper right. - -If this dropdown is not visible, you may not have permission to run the action. Contact IT for help. - -![command screenshot](command-screnshot.png) - -Each command will have the same two required values, but it could have more. - -GitHub's official documentation: [Manually running a workflow](https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow) - -#### Running from CLI - -You can use [`gh cli`](https://cli.github.com/) to run the commands too. Refers to the [`gh workflow run`](https://cli.github.com/manual/gh_workflow_run) section from the documentation for more information. - -### Number of the Pull Request - -The number of the pull request. Required so the action can fetch the correct branch and comment if it fails. - -## Action configurations - -### FMT - -For FMT you only need the PR number. - -You can use the following [`gh cli`](https://cli.github.com/) inside the repo: - -```bash -gh workflow run command-fmt.yml -f pr=1000 -``` - -### Update UI - -For Update UI you only need the PR number. - -You can use the following [`gh cli`](https://cli.github.com/) inside the repo: - -```bash -gh workflow run command-update-ui.yml -f pr=1000 -``` - -### Bench - -Runs `benchmark pallet` or `benchmark overhead` against your PR and commits back updated weights. - -Posible combinations based on the `benchmark` dropdown. - -- `substrate-pallet`: Pallet Benchmark for Substrate for specific pallet - - Requires `Subcommand` to be `pallet` - - Requires `Runtime` to be `dev` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Target Directory` to be `substrate` -- `polkadot-pallet`: Pallet Benchmark for Polkadot for specific pallet - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `rococo` - - `westend` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Target Directory` to be `polkadot` -- `cumulus-assets`: Pallet Benchmark for Cumulus assets - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `asset-hub-westend` - - `asset-hub-rococo` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `assets` - - Requires `Target Directory` to be `cumulus` -- `cumulus-collectives`: Pallet Benchmark for Cumulus collectives - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be `collectives-westend` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `collectives` - - Requires `Target Directory` to be `cumulus` -- `cumulus-coretime`: Pallet Benchmark for Cumulus coretime - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `coretime-rococo` - - `coretime-westend` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `coretime` - - Requires `Target Directory` to be `cumulus` -- `cumulus-bridge-hubs`: Pallet Benchmark for Cumulus bridge-hubs - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `bridge-hub-rococo` - - `bridge-hub-westend` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `bridge-hub` - - Requires `Target Directory` to be `cumulus` -- `cumulus-contracts`: Pallet Benchmark for Cumulus contracts - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one `contracts-rococo` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `contracts` - - Requires `Target Directory` to be `cumulus` -- `cumulus-glutton`: Pallet Benchmark for Cumulus glutton - - Requires `Subcommand` to be `pallet` - - Requires `Runtime` to be one of the following: - - `glutton-westend` - - `glutton-westend-dev-1300` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `glutton` - - Requires `Target Directory` to be `cumulus` -- `cumulus-starters`: Pallet Benchmark for Cumulus starters - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `seedling` - - `shell` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `starters` - - Requires `Target Directory` to be `cumulus` -- `cumulus-people`: Pallet Benchmark for Cumulus people - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `people-westend` - - `people-rococo` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `people` - - Requires `Target Directory` to be `cumulus` -- `cumulus-testing`: Pallet Benchmark for Cumulus testing - - Requires `Subcommand` to be one of the following: - - `pallet` - - `xcm` - - Requires `Runtime` to be one of the following: - - `penpal` - - `rococo-parachain` - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` - - Requires `Runtime Dir` to be `testing` - - Requires `Target Directory` to be `cumulus` - -You can use the following [`gh cli`](https://cli.github.com/) inside the repo: - -```bash -gh workflow run command-bench.yml -f pr=1000 -f benchmark=polkadot-pallet -f subcommand=pallet -f runtime=rococo -f pallet=pallet_name -f target_dir=polkadot -``` - -### Bench-all - -This is a wrapper to run `bench` for all pallets. - -Posible combinations based on the `benchmark` dropdown. - -- `pallet`: Benchmark for Substrate/Polkadot/Cumulus/Trappist for specific pallet - - Requires field `Pallet` to have an input that applies to `^([a-z_]+)([:]{2}[a-z_]+)?$` -- `substrate`: Pallet + Overhead + Machine Benchmark for Substrate for all pallets - - Requires `Target Directory` to be `substrate` -- `polkadot`: Pallet + Overhead Benchmark for Polkadot - - Requires `Runtime` to be one of the following: - - `rococo` - - `westend` - - Requires `Target Directory` to be `polkadot` -- `cumulus`: Pallet Benchmark for Cumulus - - Requires `Runtime` to be one of the following: - - `rococo` - - `westend` - - `asset-hub-kusama` - - `asset-hub-polkadot` - - `asset-hub-rococo` - - `asset-hub-westend` - - `bridge-hub-kusama` - - `bridge-hub-polkadot` - - `bridge-hub-rococo` - - `bridge-hub-westend` - - `collectives-polkadot` - - `collectives-westend` - - `coretime-rococo` - - `coretime-westend` - - `contracts-rococo` - - `glutton-kusama` - - `glutton-westend` - - `people-rococo` - - `people-westend` - - Requires `Target Directory` to be `cumulus` - -You can use the following [`gh cli`](https://cli.github.com/) inside the repo: - -```bash -gh workflow run command-bench-all.yml -f pr=1000 -f benchmark=pallet -f pallet=pallet_name -f target_dir=polkadot -f runtime=rococo -``` - -### Bench-overhead - -Run benchmarks overhead and commit back results to PR. - -Posible combinations based on the `benchmark` dropdown. - -- `default`: Runs `benchmark overhead` and commits back to PR the updated `extrinsic_weights.rs` files - - Requires `Runtime` to be one of the following: - - `rococo` - - `westend` - - Requires `Target directory` to be `polkadot` -- `substrate`: Runs `benchmark overhead` and commits back to PR the updated `extrinsic_weights.rs` files - - Requires `Target directory` to be `substrate` -- `cumulus`: Runs `benchmark overhead` and commits back to PR the updated `extrinsic_weights.rs` files - - Requires `Runtime` to be one of the following: - - `asset-hub-rococo` - - `asset-hub-westend` - - Requires `Target directory` to be `cumulus` - -You can use the following [`gh cli`](https://cli.github.com/) inside the repo: - -```bash -gh workflow run command-bench-overheard.yml -f pr=1000 -f benchmark=substrate -f runtime=rococo -f target_dir=substrate -``` - -### Sync - -Run sync and commit back results to PR. - -Posible combinations based on the `benchmark` dropdown. - -- `chain` - - Requires one of the following: - - `rococo` - - `westend` -- `sync-type` - - Requires one of the following: - - `warp` - - `full` - - `fast` - - `fast-unsafe` - -You can use the following [`gh cli`](https://cli.github.com/) inside the repo: - -```bash -gh workflow run command-sync.yml -f pr=1000 -f chain=rococo -f sync-type=full -``` - -## How to modify an action - -If you want to modify an action and test it, you can do by simply pushing your changes and then selecting your branch in the `Use worflow from` option. - -This will use a file from a specified branch. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 120000 index 0000000000000000000000000000000000000000..7b6b3498755f586abec9256cbf8f455c16928ffd --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1 @@ +../docs/contributor/PULL_REQUEST_TEMPLATE.md \ No newline at end of file diff --git a/.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/_help.py b/.github/scripts/cmd/_help.py new file mode 100644 index 0000000000000000000000000000000000000000..8ad49dad8461e38a7d4bfe1d54f408071d7ae509 --- /dev/null +++ b/.github/scripts/cmd/_help.py @@ -0,0 +1,26 @@ +import argparse + +""" + +Custom help action for argparse, it prints the help message for the main parser and all subparsers. + +""" + + +class _HelpAction(argparse._HelpAction): + def __call__(self, parser, namespace, values, option_string=None): + parser.print_help() + + # retrieve subparsers from parser + subparsers_actions = [ + action for action in parser._actions + if isinstance(action, argparse._SubParsersAction)] + # there will probably only be one subparser_action, + # but better save than sorry + for subparsers_action in subparsers_actions: + # get all subparsers and print help + for choice, subparser in subparsers_action.choices.items(): + print("\n### Command '{}'".format(choice)) + print(subparser.format_help()) + + parser.exit() diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py new file mode 100755 index 0000000000000000000000000000000000000000..f7dd88df4bda42551b339c3492a29028511c3550 --- /dev/null +++ b/.github/scripts/cmd/cmd.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 + +import os +import sys +import json +import argparse +import _help +import importlib.util + +_HelpAction = _help._HelpAction + +f = open('.github/workflows/runtimes-matrix.json', 'r') +runtimesMatrix = json.load(f) + +runtimeNames = list(map(lambda x: x['name'], runtimesMatrix)) + +common_args = { + '--continue-on-fail': {"action": "store_true", "help": "Won't exit(1) on failed command and continue with next steps. "}, + '--quiet': {"action": "store_true", "help": "Won't print start/end/failed messages in PR"}, + '--clean': {"action": "store_true", "help": "Clean up the previous bot's & author's comments in PR"}, + '--image': {"help": "Override docker image '--image docker.io/paritytech/ci-unified:latest'"}, +} + +parser = argparse.ArgumentParser(prog="/cmd ", description='A command runner for polkadot-sdk repo', add_help=False) +parser.add_argument('--help', action=_HelpAction, help='help for help if you need some help') # help for help +for arg, config in common_args.items(): + parser.add_argument(arg, **config) + +subparsers = parser.add_subparsers(help='a command to run', dest='command') + +""" +BENCH +""" + +bench_example = '''**Examples**: + Runs all benchmarks + %(prog)s + + Runs benchmarks for pallet_balances and pallet_multisig for all runtimes which have these pallets. **--quiet** makes it to output nothing to PR but reactions + %(prog)s --pallet pallet_balances pallet_xcm_benchmarks::generic --quiet + + Runs bench for all pallets for westend runtime and continues even if some benchmarks fail + %(prog)s --runtime westend --continue-on-fail + + Does not output anything and cleans up the previous bot's & author command triggering comments in PR + %(prog)s --runtime westend rococo --pallet pallet_balances pallet_multisig --quiet --clean +''' + +parser_bench = subparsers.add_parser('bench', help='Runs benchmarks', epilog=bench_example, formatter_class=argparse.RawDescriptionHelpFormatter) + +for arg, config in common_args.items(): + parser_bench.add_argument(arg, **config) + +parser_bench.add_argument('--runtime', help='Runtime(s) space separated', choices=runtimeNames, nargs='*', default=runtimeNames) +parser_bench.add_argument('--pallet', help='Pallet(s) space separated', nargs='*', default=[]) + +""" +FMT +""" +parser_fmt = subparsers.add_parser('fmt', help='Formats code (cargo +nightly-VERSION fmt) and configs (taplo format)') +for arg, config in common_args.items(): + parser_fmt.add_argument(arg, **config) + +""" +Update UI +""" +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) + +def main(): + global args, unknown, runtimesMatrix + args, unknown = parser.parse_known_args() + + print(f'args: {args}') + + if args.command == 'bench': + runtime_pallets_map = {} + failed_benchmarks = {} + successful_benchmarks = {} + + profile = "release" + + print(f'Provided runtimes: {args.runtime}') + # convert to mapped dict + runtimesMatrix = list(filter(lambda x: x['name'] in args.runtime, runtimesMatrix)) + runtimesMatrix = {x['name']: x for x in runtimesMatrix} + print(f'Filtered out runtimes: {runtimesMatrix}') + + # loop over remaining runtimes to collect available pallets + for runtime in runtimesMatrix.values(): + os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features runtime-benchmarks") + print(f'-- listing pallets for benchmark for {runtime["name"]}') + wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" + output = os.popen( + f"frame-omni-bencher v1 benchmark pallet --no-csv-header --no-storage-info --no-min-squares --no-median-slopes --all --list --runtime={wasm_file}").read() + raw_pallets = output.strip().split('\n') + + all_pallets = set() + for pallet in raw_pallets: + if pallet: + all_pallets.add(pallet.split(',')[0].strip()) + + pallets = list(all_pallets) + print(f'Pallets in {runtime["name"]}: {pallets}') + runtime_pallets_map[runtime['name']] = pallets + + print(f'\n') + + # filter out only the specified pallets from collected runtimes/pallets + if args.pallet: + print(f'Pallets: {args.pallet}') + new_pallets_map = {} + # keep only specified pallets if they exist in the runtime + for runtime in runtime_pallets_map: + if set(args.pallet).issubset(set(runtime_pallets_map[runtime])): + new_pallets_map[runtime] = args.pallet + + runtime_pallets_map = new_pallets_map + + print(f'Filtered out runtimes & pallets: {runtime_pallets_map}\n') + + if not runtime_pallets_map: + if args.pallet and not args.runtime: + print(f"No pallets {args.pallet} found in any runtime") + elif args.runtime and not args.pallet: + print(f"{args.runtime} runtime does not have any pallets") + elif args.runtime and args.pallet: + print(f"No pallets {args.pallet} found in {args.runtime}") + else: + print('No runtimes found') + sys.exit(1) + + for runtime in runtime_pallets_map: + for pallet in runtime_pallets_map[runtime]: + config = runtimesMatrix[runtime] + header_path = os.path.abspath(config['header']) + template = None + + print(f'-- config: {config}') + if runtime == 'dev': + # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) + search_manifest_path = f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .manifest_path'" + print(f'-- running: {search_manifest_path}') + manifest_path = os.popen(search_manifest_path).read() + if not manifest_path: + print(f'-- pallet {pallet} not found in dev runtime') + exit(1) + package_dir = os.path.dirname(manifest_path) + print(f'-- package_dir: {package_dir}') + print(f'-- manifest_path: {manifest_path}') + output_path = os.path.join(package_dir, "src", "weights.rs") + template = config['template'] + else: + default_path = f"./{config['path']}/src/weights" + xcm_path = f"./{config['path']}/src/weights/xcm" + output_path = default_path + if pallet.startswith("pallet_xcm_benchmarks"): + template = config['template'] + output_path = xcm_path + + print(f'-- benchmarking {pallet} in {runtime} into {output_path}') + cmd = f"frame-omni-bencher v1 benchmark pallet " \ + f"--extrinsic=* " \ + f"--runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm " \ + f"--pallet={pallet} " \ + f"--header={header_path} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 " \ + f"--repeat=20 " \ + f"--heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes" + print(f'-- Running: {cmd} \n') + status = os.system(cmd) + if status != 0 and not args.continue_on_fail: + print(f'Failed to benchmark {pallet} in {runtime}') + sys.exit(1) + + # Otherwise collect failed benchmarks and print them at the end + # push failed pallets to failed_benchmarks + if status != 0: + failed_benchmarks[f'{runtime}'] = failed_benchmarks.get(f'{runtime}', []) + [pallet] + else: + successful_benchmarks[f'{runtime}'] = successful_benchmarks.get(f'{runtime}', []) + [pallet] + + if failed_benchmarks: + print('❌ Failed benchmarks of runtimes/pallets:') + for runtime, pallets in failed_benchmarks.items(): + print(f'-- {runtime}: {pallets}') + + if successful_benchmarks: + print('✅ Successful benchmarks of runtimes/pallets:') + for runtime, pallets in successful_benchmarks.items(): + print(f'-- {runtime}: {pallets}') + + elif args.command == 'fmt': + command = f"cargo +nightly fmt" + print(f'Formatting with `{command}`') + nightly_status = os.system(f'{command}') + taplo_status = os.system('taplo format --config .config/taplo.toml') + + if (nightly_status != 0 or taplo_status != 0) and not args.continue_on_fail: + print('❌ Failed to format code') + sys.exit(1) + + elif args.command == 'update-ui': + command = 'sh ./scripts/update-ui-tests.sh' + print(f'Updating ui with `{command}`') + status = os.system(f'{command}') + + if status != 0 and not args.continue_on_fail: + print('❌ Failed to format code') + sys.exit(1) + + 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..a2f29b075daed94a0c7380e031bb84e9a6889558 --- /dev/null +++ b/.github/scripts/cmd/test_cmd.py @@ -0,0 +1,339 @@ +import unittest +from unittest.mock import patch, mock_open, MagicMock, call +import json +import sys +import os +import argparse + +# Mock data for runtimes-matrix.json +mock_runtimes_matrix = [ + {"name": "dev", "package": "kitchensink-runtime", "path": "substrate/frame", "header": "substrate/HEADER-APACHE2", "template": "substrate/.maintain/frame-weight-template.hbs"}, + {"name": "westend", "package": "westend-runtime", "path": "polkadot/runtime/westend", "header": "polkadot/file_header.txt", "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs"}, + {"name": "rococo", "package": "rococo-runtime", "path": "polkadot/runtime/rococo", "header": "polkadot/file_header.txt", "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs"}, + {"name": "asset-hub-westend", "package": "asset-hub-westend-runtime", "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", "header": "cumulus/file_header.txt", "template": "cumulus/templates/xcm-bench-template.hbs"}, +] + +def get_mock_bench_output(runtime, pallets, output_path, header, template = None): + return f"frame-omni-bencher v1 benchmark pallet --extrinsic=* " \ + f"--runtime=target/release/wbuild/{runtime}-runtime/{runtime.replace('-', '_')}_runtime.wasm " \ + f"--pallet={pallets} --header={header} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 --repeat=20 --heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes" + +class TestCmd(unittest.TestCase): + + def setUp(self): + self.patcher1 = patch('builtins.open', new_callable=mock_open, read_data=json.dumps(mock_runtimes_matrix)) + self.patcher2 = patch('json.load', return_value=mock_runtimes_matrix) + self.patcher3 = patch('argparse.ArgumentParser.parse_known_args') + self.patcher4 = patch('os.system', return_value=0) + self.patcher5 = patch('os.popen') + self.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"), + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p rococo-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features runtime-benchmarks"), + + call(get_mock_bench_output('kitchensink', 'pallet_balances', './substrate/frame/balances/src/weights.rs', os.path.abspath('substrate/HEADER-APACHE2'), "substrate/.maintain/frame-weight-template.hbs")), + call(get_mock_bench_output('westend', 'pallet_balances', './polkadot/runtime/westend/src/weights', os.path.abspath('polkadot/file_header.txt'))), + # skips rococo benchmark + call(get_mock_bench_output('asset-hub-westend', 'pallet_balances', './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', os.path.abspath('cumulus/file_header.txt'))), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_normal_execution(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend'], + pallet=['pallet_balances', 'pallet_staking'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + header_path = os.path.abspath('polkadot/file_header.txt') + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\n", # Output for westend runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + + # Westend runtime calls + call(get_mock_bench_output('westend', 'pallet_balances', './polkadot/runtime/westend/src/weights', header_path)), + call(get_mock_bench_output('westend', 'pallet_staking', './polkadot/runtime/westend/src/weights', header_path)), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + + def test_bench_command_normal_execution_xcm(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend'], + pallet=['pallet_xcm_benchmarks::generic'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + header_path = os.path.abspath('polkadot/file_header.txt') + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\npallet_xcm_benchmarks::generic\n", # Output for westend runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + + # Westend runtime calls + call(get_mock_bench_output( + 'westend', + 'pallet_xcm_benchmarks::generic', + './polkadot/runtime/westend/src/weights/xcm', + header_path, + "polkadot/xcm/pallet-xcm-benchmarks/template.hbs" + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_two_runtimes_two_pallets(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend', 'rococo'], + pallet=['pallet_balances', 'pallet_staking'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_staking\npallet_balances\n", # Output for westend runtime + "pallet_staking\npallet_balances\n", # Output for rococo runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + header_path = os.path.abspath('polkadot/file_header.txt') + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features runtime-benchmarks"), + call("forklift cargo build -p rococo-runtime --profile release --features runtime-benchmarks"), + # Westend runtime calls + call(get_mock_bench_output('westend', 'pallet_staking', './polkadot/runtime/westend/src/weights', header_path)), + call(get_mock_bench_output('westend', 'pallet_balances', './polkadot/runtime/westend/src/weights', header_path)), + # Rococo runtime calls + call(get_mock_bench_output('rococo', 'pallet_staking', './polkadot/runtime/rococo/src/weights', header_path)), + call(get_mock_bench_output('rococo', 'pallet_balances', './polkadot/runtime/rococo/src/weights', header_path)), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_dev_runtime(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['dev'], + pallet=['pallet_balances'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + manifest_dir = "substrate/frame/kitchensink" + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_something", # Output for dev runtime + manifest_dir + "/Cargo.toml" # Output for manifest path in dev runtime + ] + header_path = os.path.abspath('substrate/HEADER-APACHE2') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p kitchensink-runtime --profile release --features runtime-benchmarks"), + # Westend runtime calls + call(get_mock_bench_output( + 'kitchensink', + 'pallet_balances', + manifest_dir + "/src/weights.rs", + header_path, + "substrate/.maintain/frame-weight-template.hbs" + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_cumulus_runtime(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['asset-hub-westend'], + pallet=['pallet_assets'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_assets\n", # Output for asset-hub-westend runtime + ] + header_path = os.path.abspath('cumulus/file_header.txt') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features runtime-benchmarks"), + # Asset-hub-westend runtime calls + call(get_mock_bench_output( + 'asset-hub-westend', + 'pallet_assets', + './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header_path + )), + ] + + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_cumulus_runtime_xcm(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['asset-hub-westend'], + pallet=['pallet_xcm_benchmarks::generic', 'pallet_assets'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_assets\npallet_xcm_benchmarks::generic\n", # Output for asset-hub-westend runtime + ] + header_path = os.path.abspath('cumulus/file_header.txt') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features runtime-benchmarks"), + # Asset-hub-westend runtime calls + call(get_mock_bench_output( + 'asset-hub-westend', + 'pallet_xcm_benchmarks::generic', + './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm', + header_path, + "cumulus/templates/xcm-bench-template.hbs" + )), + call(get_mock_bench_output( + 'asset-hub-westend', + 'pallet_assets', + './cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header_path + )), + ] + + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='fmt', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_fmt_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + mock_system.assert_any_call('cargo +nightly fmt') + mock_system.assert_any_call('taplo format --config .config/taplo.toml') + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='update-ui', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_update_ui_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + mock_system.assert_called_with('sh ./scripts/update-ui-tests.sh') + + @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 33ef2d3e7eda4d6b4b5b7eacc2e3ed2b1a24c29f..5361db398ae7bcaebd6717d726d50d50d5c5e301 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -299,22 +299,23 @@ function check_sha256() { } # Import GPG keys of the release team members -# This is done in parallel as it can take a while sometimes function import_gpg_keys() { - GPG_KEYSERVER=${GPG_KEYSERVER:-"keyserver.ubuntu.com"} + GPG_KEYSERVER=${GPG_KEYSERVER:-"hkps://keyserver.ubuntu.com"} SEC="9D4B2B6EB8F97156D19669A9FF0812D491B96798" EGOR="E6FC4D4782EB0FA64A4903CCDB7D3555DD3932D3" MORGAN="2E92A9D8B15D7891363D1AE8AF9E6C43F7F8C4CF" + PARITY_RELEASES="90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" - echo "Importing GPG keys from $GPG_KEYSERVER in parallel" - for key in $SEC $EGOR $MORGAN; do + echo "Importing GPG keys from $GPG_KEYSERVER" + for key in $SEC $EGOR $MORGAN $PARITY_RELEASES; do ( echo "Importing GPG key $key" gpg --no-tty --quiet --keyserver $GPG_KEYSERVER --recv-keys $key echo -e "5\ny\n" | gpg --no-tty --command-fd 0 --expert --edit-key $key trust; - ) & + ) done wait + gpg -k } # Check the GPG signature for a given binary @@ -457,3 +458,15 @@ function get_polkadot_node_version_from_code() { # Remove the semicolon sed 's/;//g' } + +validate_stable_tag() { + tag="$1" + pattern='^stable[0-9]+(-[0-9]+)?$' + + if [[ $tag =~ $pattern ]]; then + echo $tag + else + echo "The input '$tag' does not match the pattern." + exit 1 + fi +} diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py new file mode 100644 index 0000000000000000000000000000000000000000..d3b6b523ecfd32d32470381c0d7a548b2d289041 --- /dev/null +++ b/.github/scripts/generate-prdoc.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +""" +Generate the PrDoc for a Pull Request with a specific number, audience and bump level. + +It downloads and parses the patch from the GitHub API to opulate the prdoc with all modified crates. +This will delete any prdoc that already exists for the PR if `--force` is passed. + +Usage: + python generate-prdoc.py --pr 1234 --audience "TODO" --bump "TODO" +""" + +import argparse +import os +import re +import sys +import subprocess +import toml +import yaml +import requests + +from github import Github +import whatthepatch +from cargo_workspace import Workspace + +# Download the patch and pass the info into `create_prdoc`. +def from_pr_number(n, audience, bump, force): + print(f"Fetching PR '{n}' from GitHub") + g = Github() + + repo = g.get_repo("paritytech/polkadot-sdk") + pr = repo.get_pull(n) + + patch_url = pr.patch_url + patch = requests.get(patch_url).text + + create_prdoc(n, audience, pr.title, pr.body, patch, bump, force) + +def create_prdoc(pr, audience, title, description, patch, bump, force): + path = f"prdoc/pr_{pr}.prdoc" + + if os.path.exists(path): + if force == True: + print(f"Overwriting existing PrDoc for PR {pr}") + else: + print(f"PrDoc already exists for PR {pr}. Use --force to overwrite.") + sys.exit(1) + else: + print(f"No preexisting PrDoc for PR {pr}") + + prdoc = { "title": title, "doc": [{}], "crates": [] } + + prdoc["doc"][0]["audience"] = audience + prdoc["doc"][0]["description"] = description + + workspace = Workspace.from_path(".") + + modified_paths = [] + for diff in whatthepatch.parse_patch(patch): + new_path = diff.header.new_path + # Sometimes this lib returns `/dev/null` as the new path... + if not new_path.startswith("/dev"): + modified_paths.append(new_path) + + modified_crates = {} + for p in modified_paths: + # Go up until we find a Cargo.toml + p = os.path.join(workspace.path, p) + while not os.path.exists(os.path.join(p, "Cargo.toml")): + print(f"Could not find Cargo.toml in {p}") + if p == '/': + exit(1) + p = os.path.dirname(p) + + with open(os.path.join(p, "Cargo.toml")) as f: + manifest = toml.load(f) + + if not "package" in manifest: + print(f"File was not in any crate: {p}") + continue + + crate_name = manifest["package"]["name"] + if workspace.crate_by_name(crate_name).publish: + modified_crates[crate_name] = True + else: + print(f"Skipping unpublished crate: {crate_name}") + + print(f"Modified crates: {modified_crates.keys()}") + + for crate_name in modified_crates.keys(): + entry = { "name": crate_name } + + if bump == 'silent' or bump == 'ignore' or bump == 'no change': + entry["validate"] = False + else: + entry["bump"] = bump + + print(f"Adding crate {entry}") + prdoc["crates"].append(entry) + + # write the parsed PR documentation back to the file + with open(path, "w") as f: + yaml.dump(prdoc, f, sort_keys=False) + print(f"PrDoc for PR {pr} written to {path}") + +# Make the `description` a multiline string instead of escaping \r\n. +def setup_yaml(): + def yaml_multiline_string_presenter(dumper, data): + if len(data.splitlines()) > 1: + data = '\n'.join([line.rstrip() for line in data.strip().splitlines()]) + return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|') + return dumper.represent_scalar('tag:yaml.org,2002:str', data) + + yaml.add_representer(str, yaml_multiline_string_presenter) + +# parse_args is also used by cmd/cmd.py +def setup_parser(parser=None): + if parser is None: + parser = argparse.ArgumentParser() + parser.add_argument("--pr", type=int, required=True, help="The PR number to generate the PrDoc for." ) + parser.add_argument("--audience", type=str, default="TODO", help="The audience of whom the changes may concern.") + parser.add_argument("--bump", type=str, default="TODO", help="A default bump level for all crates.") + parser.add_argument("--force", type=str, help="Whether to overwrite any existing PrDoc.") + + return parser + +def main(args): + force = True if (args.force or "false").lower() == "true" else False + print(f"Args: {args}, force: {force}") + setup_yaml() + try: + from_pr_number(args.pr, args.audience, args.bump, force) + return 0 + except Exception as e: + print(f"Error generating prdoc: {e}") + return 1 + +if __name__ == "__main__": + args = setup_parser().parse_args() + main(args) \ No newline at end of file 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/generate-readmes.py b/.github/scripts/generate-readmes.py new file mode 100755 index 0000000000000000000000000000000000000000..f838eaa29a74daafe9616d779d070aff36891347 --- /dev/null +++ b/.github/scripts/generate-readmes.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 + +""" +A script to generate READMEs for all public crates, +if they do not already have one. + +It relies on functions from the `check-workspace.py` script. + +The resulting README is based on a template defined below, +and includes the crate name, description, license, +and optionally - the SDK release version. + +# Example + +```sh +python3 -m pip install toml +.github/scripts/generate-readmes.py . --sdk-version 1.15.0 +``` +""" + +import os +import toml +import importlib +import argparse + +check_workspace = importlib.import_module("check-workspace") + +README_TEMPLATE = """
+ +Polkadot logo + +# {name} + +This crate is part of the [Polkadot SDK](https://github.com/paritytech/polkadot-sdk/). + +
+ +## Description + +{description} + +## Additional Resources + +In order to learn about Polkadot SDK, head over to the [Polkadot SDK Developer Documentation](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html). + +To learn about Polkadot, visit [polkadot.com](https://polkadot.com/). + +## License + +This crate is licensed with {license}. +""" + +VERSION_TEMPLATE = """ +## Version + +This version of `{name}` is associated with Polkadot {sdk_version} release. +""" + + +def generate_readme(member, *, workspace_dir, workspace_license, sdk_version): + print(f"Loading manifest for: {member}") + manifest = toml.load(os.path.join(workspace_dir, member, "Cargo.toml")) + if manifest["package"].get("publish", True) == False: + print(f"⏩ Skipping un-published crate: {member}") + return + if os.path.exists(os.path.join(workspace_dir, member, "README.md")): + print(f"⏩ Skipping crate with an existing readme: {member}") + return + print(f"📝 Generating README for: {member}") + + license = manifest["package"]["license"] + if isinstance(license, dict): + if not license.get("workspace", False): + print( + f"❌ License for {member} is unexpectedly declared as workspace=false." + ) + # Skipping this crate as it is not clear what license it should use. + return + license = workspace_license + + name = manifest["package"]["name"] + description = manifest["package"]["description"] + description = description + "." if not description.endswith(".") else description + + filled_readme = README_TEMPLATE.format( + name=name, description=description, license=license + ) + + if sdk_version: + filled_readme += VERSION_TEMPLATE.format(name=name, sdk_version=sdk_version) + + with open(os.path.join(workspace_dir, member, "README.md"), "w") as new_readme: + new_readme.write(filled_readme) + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Generate readmes for published crates." + ) + + parser.add_argument( + "workspace_dir", + help="The directory to check", + metavar="workspace_dir", + type=str, + nargs=1, + ) + parser.add_argument( + "--sdk-version", + help="Optional SDK release version", + metavar="sdk_version", + type=str, + nargs=1, + required=False, + ) + + args = parser.parse_args() + return (args.workspace_dir[0], args.sdk_version[0] if args.sdk_version else None) + + +def main(): + (workspace_dir, sdk_version) = parse_args() + root_manifest = toml.load(os.path.join(workspace_dir, "Cargo.toml")) + workspace_license = root_manifest["workspace"]["package"]["license"] + members = check_workspace.get_members(workspace_dir, []) + for member in members: + generate_readme( + member, + workspace_dir=workspace_dir, + workspace_license=workspace_license, + sdk_version=sdk_version, + ) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/release/release_lib.sh b/.github/scripts/release/release_lib.sh new file mode 100644 index 0000000000000000000000000000000000000000..81a3c14edec84f3c069fefbb16b003768f894af3 --- /dev/null +++ b/.github/scripts/release/release_lib.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash + +# Set the new version by replacing the value of the constant given as patetrn +# in the file. +# +# input: pattern, version, file +#output: none +set_version() { + pattern=$1 + version=$2 + file=$3 + + sed -i "s/$pattern/\1\"${version}\"/g" $file + return 0 +} + +# Commit changes to git with specific message. +# "|| true" does not let script to fail with exit code 1, +# in case there is nothing to commit. +# +# input: MESSAGE (any message which should be used for the commit) +# output: none +commit_with_message() { + MESSAGE=$1 + git commit -a -m "$MESSAGE" || true +} + +# Retun list of the runtimes filterd +# input: none +# output: list of filtered runtimes +get_filtered_runtimes_list() { + grep_filters=("runtime.*" "test|template|starters|substrate") + + git grep spec_version: | grep .rs: | grep -e "${grep_filters[0]}" | grep "lib.rs" | grep -vE "${grep_filters[1]}" | cut -d: -f1 +} + +# Sets provided spec version +# input: version +set_spec_versions() { + NEW_VERSION=$1 + runtimes_list=(${@:2}) + + printf "Setting spec_version to $NEW_VERSION\n" + + for f in ${runtimes_list[@]}; do + printf " processing $f" + sed -ri "s/spec_version: [0-9]+_[0-9]+_[0-9]+,/spec_version: $NEW_VERSION,/" $f + done + + commit_with_message "Bump spec_version to $NEW_VERSION" + + git_show_log 'spec_version' +} + +# Displays formated results of the git log command +# for the given pattern which needs to be found in logs +# input: pattern, count (optional, default is 10) +git_show_log() { + PATTERN="$1" + COUNT=${2:-10} + git log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=iso-strict | \ + head -n $COUNT | grep -iE "$PATTERN" --color=always -z +} + +# Get a spec_version number from the crate version +# +# ## inputs +# - v1.12.0 or 1.12.0 +# +# ## output: +# 1_012_000 or 1_012_001 if SUFFIX is set +function get_spec_version() { + INPUT=$1 + SUFFIX=${SUFFIX:-000} #this variable makes it possible to set a specific ruuntime version like 93826 it can be intialised as sestem variable + [[ $INPUT =~ .*([0-9]+\.[0-9]+\.[0-9]{1,2}).* ]] + VERSION="${BASH_REMATCH[1]}" + MATCH="${BASH_REMATCH[0]}" + if [ -z $MATCH ]; then + return 1 + else + SPEC_VERSION="$(sed -e "s/\./_0/g" -e "s/_[^_]*\$/_$SUFFIX/" <<< $VERSION)" + echo "$SPEC_VERSION" + return 0 + fi +} + +# Reorganize the prdoc files for the release +# +# input: VERSION (e.g. v1.0.0) +# output: none +reorder_prdocs() { + VERSION="$1" + + printf "[+] ℹ️ Reordering prdocs:" + + VERSION=$(sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*$/\1/' <<< "$VERSION") #getting reed of the 'v' prefix + mkdir -p "prdoc/$VERSION" + mv prdoc/pr_*.prdoc prdoc/$VERSION + git add -A + commit_with_message "Reordering prdocs for the release $VERSION" +} + +# Bump the binary version of the polkadot-parachain binary with the +# new bumped version and commit changes. +# +# input: version e.g. 1.16.0 +set_polkadot_parachain_binary_version() { + bumped_version="$1" + cargo_toml_file="$2" + + set_version "\(^version = \)\".*\"" $bumped_version $cargo_toml_file + + cargo update --workspace --offline # we need this to update Cargo.loc with the new versions as well + + MESSAGE="Bump versions in: ${cargo_toml_file}" + commit_with_message "$MESSAGE" + git_show_log "$MESSAGE" +} diff --git a/.github/workflows/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..3a9f60761863587805d46c1a914fc8478d7f7525 --- /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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-test-parachain-${{ needs.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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-${{ needs.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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-test-collators-${{ needs.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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-malus-${{ needs.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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-substrate-${{ needs.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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-${{ needs.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: arc-runners-polkadot-sdk + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-cumulus-${{ needs.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" \ No newline at end of file diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml new file mode 100644 index 0000000000000000000000000000000000000000..16263788b8b64847ca0ec6a2de362ebc130d7ab4 --- /dev/null +++ b/.github/workflows/check-cargo-check-runtimes.yml @@ -0,0 +1,138 @@ +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] + +# Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers + +jobs: + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + + check-runtime-assets: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + timeout-minutes: 20 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/assets + + check-runtime-collectives: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [check-runtime-assets, preflight] + timeout-minutes: 20 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/collectives + + check-runtime-coretime: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-assets, preflight] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/coretime + + check-runtime-bridge-hubs: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [preflight] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/bridge-hubs + + check-runtime-contracts: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-collectives, preflight] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/contracts + + check-runtime-starters: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-assets, preflight] + 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: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-starters, preflight] + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run cargo check + uses: ./.github/actions/cargo-check-runtimes + with: + root: cumulus/parachains/runtimes/testing + + confirm-required-jobs-passed: + runs-on: ubuntu-latest + name: All check-runtime-* tests passed + # If any new job gets added, be sure to add it to this array + needs: + - check-runtime-assets + - check-runtime-collectives + - check-runtime-coretime + - check-runtime-bridge-hubs + - check-runtime-contracts + - check-runtime-starters + - check-runtime-testing + 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/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..9b01311aa69c7e7348ebe996fa7d0d785fd9187b 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,32 +16,36 @@ env: ARTIFACTS_NAME: frame-omni-bencher-artifacts jobs: - changes: + + preflight: # TODO: remove once migration is complete or this workflow is fully stable if: contains(github.event.label.name, 'GHA-migration') - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml + 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 }} + 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" + 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 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 }} + needs: [preflight] # , build-frame-omni-bencher ] + if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 30 strategy: fail-fast: false # keep running other workflows even if one fails, to see the logs of all possible failures @@ -62,7 +66,7 @@ jobs: glutton-westend-runtime, ] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: PACKAGE_NAME: ${{ matrix.runtime }} steps: @@ -81,5 +85,14 @@ jobs: runs-on: ubuntu-latest name: All benchmarks passed needs: run-frame-omni-bencher + if: always() && !cancelled() steps: - - run: echo '### Good job! All the benchmarks passed 🚀' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/check-getting-started.yml b/.github/workflows/check-getting-started.yml new file mode 100644 index 0000000000000000000000000000000000000000..b43db33c63bf12b5ad239d9c2dcfafb3ccc7a050 --- /dev/null +++ b/.github/workflows/check-getting-started.yml @@ -0,0 +1,296 @@ +name: Check the getting-started.sh script + +# This workflow aims to make sure that the `getting-started.sh` script +# is functional and allows to build the templates +# on different operating systems. +# +# There are two jobs inside. +# One for systems that can run in a docker container, and one for macOS. +# +# Each job consists of: +# 1. Some necessary prerequisites for the workflow itself. +# 2. A first pass of the script, which will install dependencies and clone a template. +# 3. A second pass of the script, to make sure the behaviour is as expected. +# 4. Building the template - making sure it's buildable and runnable. +# +# The script is interacted with using the `expect` tool, which is available on all relevant systems. +# The steps are not re-used between macOS and other systems, +# because they are very similar but a little different. +# Additionally, macOS does NOT start from scratch here - for example, we have homebrew already installed. +# +# There are many combinations of systems, shells and templates. +# We test a selected handful of combinations here. + +on: + pull_request: + paths: + - '.github/workflows/check-getting-started.yml' + - 'scripts/getting-started.sh' + schedule: + - cron: '0 5 * * *' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + check-getting-started: + strategy: + fail-fast: true + matrix: + include: + - name: ubuntu + container: ubuntu + template: minimal + shell: bash + - name: debian + container: debian + template: parachain + shell: sh + - name: arch + container: archlinux + template: solochain + shell: sh + - name: fedora + container: fedora + template: parachain + shell: sh + - name: opensuse + container: opensuse/tumbleweed + template: solochain + shell: sh + runs-on: arc-runners-polkadot-sdk-beefy + container: ${{ matrix.container }}:latest + steps: + # A minimal amount of prerequisites required before we can run the actual getting-started script, + # which will install the rest of requirements. + - name: Install ubuntu/debian prerequisites + run: apt update && apt install -y expect sudo git + if: contains(matrix.name, 'ubuntu') || contains(matrix.name, 'debian') + - name: Install arch prerequisites + run: pacman -Syu --needed --noconfirm expect sudo git + if: contains(matrix.name, 'arch') + - name: Install fedora prerequisites + run: dnf --assumeyes install expect sudo git + if: contains(matrix.name, 'fedora') + - name: Install opensuse prerequisites + run: zypper install --no-confirm expect sudo git + if: contains(matrix.name, 'opensuse') + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set additional expect flags if necessary + run: | + # Add a debug flag to expect, if github is re-run with debug logging enabled. + [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" + echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV + + - name: Check the first run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 240 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect -nocase "Detected ${{ matrix.name }}" + + expect "Rust is not installed. Install it?" { + send "y\r" + expect "Proceed with standard installation (default - just press enter)" { + send "\r" + expect "Rust is installed now" + } + } + + expect "Setup the Rust environment" { + send "y\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Check the second run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "n\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + expect "directory already exists" {} + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Compile the node outside of the script + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo build --release + timeout-minutes: 120 + + - name: Check that the binary is executable + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo run --release -- --help + timeout-minutes: 5 + + check-getting-started-macos: + strategy: + fail-fast: true + matrix: + include: + - template: parachain + shell: sh + - template: solochain + shell: bash + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set additional expect flags if necessary + run: | + # Add a debug flag to expect, if github is re-run with debug logging enabled. + [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" + echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV + + - name: Check the first run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect -nocase "Detected macOS" + + expect "Homebrew already installed" + + expect "Install cmake" { + send "y\r" + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "y\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Check the second run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect "Homebrew already installed" + + expect "Install cmake" { + send "y\r" + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "n\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + expect "directory already exists" {} + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Compile the node outside of the script + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo build --release + timeout-minutes: 120 + + - name: Check that the binary is executable + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo run --release -- --help + timeout-minutes: 5 diff --git a/.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..ddb1c452a7b8db9308acc7f1dd00878d634aa842 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,7 +20,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - uses: actions/setup-node@v4.0.3 with: node-version: "18.x" diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 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 2b963b2230fb7fea9800d5489a5d90753dc48860..8185cf171aefb22423e59bc7a3ec9dda72bae368 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -6,6 +6,9 @@ on: - master pull_request: types: [opened, synchronize, reopened, ready_for_review] + # Take a snapshot at 5am when most SDK devs are not working. + schedule: + - cron: "0 5 * * *" merge_group: workflow_dispatch: @@ -14,33 +17,26 @@ 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 }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - # rococo and westend are disabled for now (no access to parity-chains.parity.io) + + 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: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.preflight.outputs.RUNNER }} # We need to set this to rather long to allow the snapshot to be created, but the average time # should be much lower. timeout-minutes: 60 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} strategy: fail-fast: false matrix: - network: [ - # westend, - # rococo, + network: + [ + westend, + rococo, asset-hub-westend, asset-hub-rococo, bridge-hub-westend, @@ -50,18 +46,18 @@ jobs: coretime-rococo, ] include: - # - network: westend - # package: westend-runtime - # wasm: westend_runtime.compact.compressed.wasm - # uri: "wss://westend-try-runtime-node.parity-chains.parity.io:443" - # subcommand_extra_args: "--no-weight-warnings" - # command_extra_args: "" - # - network: rococo - # package: rococo-runtime - # wasm: rococo_runtime.compact.compressed.wasm - # uri: "wss://rococo-try-runtime-node.parity-chains.parity.io:443" - # subcommand_extra_args: "--no-weight-warnings" - # command_extra_args: "" + - network: westend + package: westend-runtime + wasm: westend_runtime.compact.compressed.wasm + uri: "wss://try-runtime-westend.polkadot.io:443" + subcommand_extra_args: "--no-weight-warnings" + command_extra_args: "" + - network: rococo + package: rococo-runtime + wasm: rococo_runtime.compact.compressed.wasm + uri: "wss://try-runtime-rococo.polkadot.io:443" + subcommand_extra_args: "--no-weight-warnings" + command_extra_args: "" - network: asset-hub-westend package: asset-hub-westend-runtime wasm: asset_hub_westend_runtime.compact.compressed.wasm @@ -140,3 +136,21 @@ jobs: --runtime ./target/release/wbuild/${{ matrix.package }}/${{ matrix.wasm }} \ on-runtime-upgrade --disable-spec-version-check --checks=all ${{ matrix.subcommand_extra_args }} snap -p snapshot.raw sleep 5 + # name of this job must be unique across all workflows + # otherwise GitHub will mark all these jobs as required + confirm-required-checks-passed: + runs-on: ubuntu-latest + name: All runtime migrations passed + # If any new job gets added, be sure to add it to this array + needs: [check-runtime-migration] + 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/check-semver.yml b/.github/workflows/check-semver.yml index d9d918b44a23d705835615a23c1f25296a34bd4c..b5866e0ce414241c9d89f1c71ec0cc6128274d67 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -12,22 +12,21 @@ concurrency: env: TOOLCHAIN: nightly-2024-06-01 - jobs: check-semver: runs-on: ubuntu-latest container: image: docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v20240408 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + with: + fetch-depth: 2 - name: extra git setup - env: - BASE: ${{ github.event.pull_request.base.sha }} run: | git config --global --add safe.directory '*' - git fetch --no-tags --no-recurse-submodules --depth=1 origin $BASE - git branch old $BASE + + git branch old HEAD^1 - name: Comment If Backport if: ${{ startsWith(github.event.pull_request.base.ref, 'stable') }} @@ -46,7 +45,7 @@ jobs: as to not impact downstream teams that rely on the stability of it. Some things to consider: - Backports are only for 'patch' or 'minor' changes. No 'major' or other breaking change. - Should be a legit *fix* for some bug, not adding tons of new features. - - Must either be already audited or trivial (not sure audit). + - Must either be already audited or not need an audit.
Emergency Bypass

@@ -74,7 +73,7 @@ jobs: - name: install parity-publish # Set the target dir to cache the build. - run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.8.0 -q + run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.8.0 --locked -q - name: check semver run: | diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index ee4bd62a558d48b4d57be9a9208b2e4109f7a6ff..9e36d2bcb2e9eed6266bfe4a940a43ace7216518 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -15,34 +15,25 @@ concurrency: permissions: {} jobs: - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - timeout-minutes: 20 - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + fmt: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@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 @@ -100,6 +91,8 @@ jobs: --exclude "substrate/frame/contracts/fixtures/build" "substrate/frame/contracts/fixtures/contracts/common" + "substrate/frame/revive/fixtures/build" + "substrate/frame/revive/fixtures/contracts/common" - name: deny git deps run: python3 .github/scripts/deny-git-deps.py . check-markdown: @@ -107,7 +100,7 @@ jobs: timeout-minutes: 20 steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Setup Node.js uses: actions/setup-node@v4.0.3 with: @@ -128,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 @@ -154,3 +147,54 @@ jobs: git diff exit 1 fi + check-fail-ci: + runs-on: ubuntu-latest + container: + # there's no "rg" in ci-unified, and tools is a smaller image anyway + image: "paritytech/tools:latest" + # paritytech/tools uses "nonroot" user by default, which doesn't have enough + # permissions to create GHA context + options: --user root + steps: + - name: Fetch latest code + uses: actions/checkout@v4 + - name: Check + run: | + set +e + rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? + if [ $exit_status -eq 0 ]; then + echo "$ASSERT_REGEX was found, exiting with 1"; + exit 1; + else + echo "No $ASSERT_REGEX was found, exiting with 0"; + exit 0; + fi + env: + ASSERT_REGEX: "FAIL-CI" + GIT_DEPTH: 1 + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f765d79254c856195271978942ea2eb550f3e7f6 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,94 @@ +name: Checks + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + + cargo-clippy: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + timeout-minutes: 40 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + RUSTFLAGS: "-D warnings" + SKIP_WASM_BUILD: 1 + steps: + - 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 + check-try-runtime: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + timeout-minutes: 40 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + - name: script + run: | + forklift cargo check --locked --all --features try-runtime + # this is taken from cumulus + # Check that parachain-template will compile with `try-runtime` feature flag. + forklift cargo check --locked -p parachain-template-node --features try-runtime + # add after https://github.com/paritytech/substrate/pull/14502 is merged + # experimental code may rely on try-runtime and vice-versa + forklift cargo check --locked --all --features try-runtime,experimental + # check-core-crypto-features works fast without forklift + check-core-crypto-features: + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + timeout-minutes: 30 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + - name: script + run: | + cd substrate/primitives/core + ./check-features-variants.sh + cd - + cd substrate/primitives/application-crypto + ./check-features-variants.sh + cd - + cd substrate/primitives/keyring + ./check-features-variants.sh + cd - + # name of this job must be unique across all workflows + # otherwise GitHub will mark all these jobs as required + confirm-required-checks-passed: + runs-on: ubuntu-latest + name: All checks passed + # If any new job gets added, be sure to add it to this array + needs: [cargo-clippy, check-try-runtime, check-core-crypto-features] + 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/cmd-tests.yml b/.github/workflows/cmd-tests.yml new file mode 100644 index 0000000000000000000000000000000000000000..37f1747d0b9eac72cf8e6c10534a8c31402cd905 --- /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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..5498beb50ccb16740cd3c8eae09dd85fc0ff6206 --- /dev/null +++ b/.github/workflows/cmd.yml @@ -0,0 +1,455 @@ +name: Command + +on: + issue_comment: # listen for comments on issues + types: [created] + +permissions: # allow the action to comment on the PR + contents: write + issues: write + pull-requests: write + actions: read + +jobs: + is-org-member: + if: startsWith(github.event.comment.body, '/cmd') + runs-on: ubuntu-latest + outputs: + member: ${{ steps.is-member.outputs.result }} + steps: + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.CMD_BOT_APP_ID }} + private_key: ${{ secrets.CMD_BOT_APP_KEY }} + + - name: Check if user is a member of the organization + id: is-member + uses: actions/github-script@v7 + with: + github-token: ${{ steps.generate_token.outputs.token }} + result-encoding: string + script: | + const fs = require("fs"); + try { + const org = '${{ github.event.repository.owner.login }}'; + const username = '${{ github.event.comment.user.login }}'; + + const membership = await github.rest.orgs.checkMembershipForUser({ + org: org, + username: username + }); + + console.log(membership, membership.status, membership.status === 204); + + if (membership.status === 204) { + return 'true'; + } else { + console.log(membership); + fs.appendFileSync(process.env["GITHUB_STEP_SUMMARY"], `${membership.data && membership.data.message || 'Unknown error happened, please check logs'}`); + } + } catch (error) { + console.log(error) + } + + return 'false'; + + reject-non-members: + needs: is-org-member + if: ${{ startsWith(github.event.comment.body, '/cmd') && needs.is-org-member.outputs.member != 'true' }} + runs-on: ubuntu-latest + steps: + - name: Add reaction to rejected comment + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: 'confused' + }) + + - name: Comment PR (Rejected) + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Sorry, only members of the organization ${{ github.event.repository.owner.login }} members can run commands.` + }) + + acknowledge: + needs: is-org-member + if: ${{ startsWith(github.event.comment.body, '/cmd') && needs.is-org-member.outputs.member == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Add reaction to triggered comment + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: 'eyes' + }) + + clean: + needs: is-org-member + runs-on: ubuntu-latest + steps: + - name: Clean previous comments + if: ${{ startsWith(github.event.comment.body, '/cmd') && contains(github.event.comment.body, '--clean') && needs.is-org-member.outputs.member == 'true' }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo + }).then(comments => { + for (let comment of comments.data) { + console.log(comment) + if ( + ${{ github.event.comment.id }} !== comment.id && + ( + ( + ( + comment.body.startsWith('Command') || + comment.body.startsWith('

Command') || + comment.body.startsWith('Sorry, only ') + ) && comment.user.type === 'Bot' + ) || + (comment.body.startsWith('/cmd') && comment.user.login === context.actor) + ) + ) { + github.rest.issues.deleteComment({ + comment_id: comment.id, + owner: context.repo.owner, + repo: context.repo.repo + }) + } + } + }) + help: + needs: [clean, is-org-member] + if: ${{ startsWith(github.event.comment.body, '/cmd') && contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get command + uses: actions-ecosystem/action-regex-match@v2 + id: get-pr-comment + with: + text: ${{ github.event.comment.body }} + regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples + + - name: Save output of help + id: help + env: + CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command + run: | + echo 'help<> $GITHUB_OUTPUT + python3 -m pip install -r .github/scripts/generate-prdoc.requirements.txt + python3 .github/scripts/cmd/cmd.py $CMD >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + + - name: Comment PR (Help) + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `
Command help: + + \`\`\` + ${{ steps.help.outputs.help }} + \`\`\` + +
` + }) + + - name: Add confused reaction on failure + uses: actions/github-script@v7 + if: ${{ failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: 'confused' + }) + + - name: Add 👍 reaction on success + uses: actions/github-script@v7 + if: ${{ !failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: '+1' + }) + + set-image: + needs: [clean, is-org-member] + if: ${{ startsWith(github.event.comment.body, '/cmd') && !contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} + runs-on: ubuntu-latest + outputs: + IMAGE: ${{ steps.set-image.outputs.IMAGE }} + RUNNER: ${{ steps.set-image.outputs.RUNNER }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - id: set-image + run: | + BODY=$(echo "${{ github.event.comment.body }}" | xargs) + IMAGE_OVERRIDE=$(echo $BODY | grep -oe 'docker.io/paritytech/ci-unified:.*\s' | xargs) + + cat .github/env >> $GITHUB_OUTPUT + + if [ -n "$IMAGE_OVERRIDE" ]; then + echo "IMAGE=$IMAGE_OVERRIDE" >> $GITHUB_OUTPUT + fi + + if [[ $BODY == "/cmd bench"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-benchmark" >> $GITHUB_OUTPUT + elif [[ $BODY == "/cmd update-ui"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + else + echo "RUNNER=ubuntu-latest" >> $GITHUB_OUTPUT + fi + + # Get PR branch name, because the issue_comment event does not contain the PR branch name + get-pr-branch: + needs: [set-image] + runs-on: ubuntu-latest + outputs: + pr-branch: ${{ steps.get-pr.outputs.pr_branch }} + repo: ${{ steps.get-pr.outputs.repo }} + steps: + - name: Check if the issue is a PR + id: check-pr + run: | + if [ -n "${{ github.event.issue.pull_request.url }}" ]; then + echo "This is a pull request comment" + else + echo "This is not a pull request comment" + exit 1 + fi + + - name: Get PR Branch Name and Repo + if: steps.check-pr.outcome == 'success' + id: get-pr + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + const prBranch = pr.data.head.ref; + const repo = pr.data.head.repo.full_name; + console.log(prBranch, repo) + core.setOutput('pr_branch', prBranch); + core.setOutput('repo', repo); + + - name: Use PR Branch Name and Repo + run: | + echo "The PR branch is ${{ steps.get-pr.outputs.pr_branch }}" + echo "The repository is ${{ steps.get-pr.outputs.repo }}" + + cmd: + needs: [set-image, get-pr-branch] + env: + JOB_NAME: "cmd" + runs-on: ${{ needs.set-image.outputs.RUNNER }} + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - name: Get command + uses: actions-ecosystem/action-regex-match@v2 + id: get-pr-comment + with: + text: ${{ github.event.comment.body }} + regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples + + - name: Build workflow link + if: ${{ !contains(github.event.comment.body, '--quiet') }} + id: build-link + run: | + # Get exactly the CMD job link, filtering out the other jobs + jobLink=$(curl -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs | jq '.jobs[] | select(.name | contains("${{ env.JOB_NAME }}")) | .html_url') + + runLink=$(curl -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }} | jq '.html_url') + + echo "job_url=${jobLink}" + echo "run_url=${runLink}" + echo "job_url=$jobLink" >> $GITHUB_OUTPUT + echo "run_url=$runLink" >> $GITHUB_OUTPUT + + - name: Comment PR (Start) + if: ${{ !contains(github.event.comment.body, '--quiet') }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let job_url = ${{ steps.build-link.outputs.job_url }} + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has started 🚀 [See logs here](${job_url})` + }) + + - name: Checkout + uses: actions/checkout@v4 + with: + repository: ${{ needs.get-pr-branch.outputs.repo }} + ref: ${{ needs.get-pr-branch.outputs.pr-branch }} + + - name: Install dependencies for bench + if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') + run: cargo install subweight frame-omni-bencher --locked + + - name: Run cmd + id: cmd + env: + CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command + run: | + echo "Running command: '$CMD' on '${{ needs.set-image.outputs.RUNNER }}' runner, container: '${{ needs.set-image.outputs.IMAGE }}'" + echo "RUST_NIGHTLY_VERSION: $RUST_NIGHTLY_VERSION" + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + git remote -v + python3 -m pip install -r .github/scripts/generate-prdoc.requirements.txt + python3 .github/scripts/cmd/cmd.py $CMD + git status + git diff + + - name: Commit changes + run: | + if [ -n "$(git status --porcelain)" ]; then + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + git 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 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 + + - name: Run Subweight + id: subweight + if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') + shell: bash + run: | + git fetch + result=$(subweight compare commits \ + --path-pattern "./**/weights/**/*.rs,./**/weights.rs" \ + --method asymptotic \ + --format markdown \ + --no-color \ + --change added changed \ + --ignore-errors \ + refs/remotes/origin/master refs/heads/${{ needs.get-pr-branch.outputs.pr-branch }}) + + # Save the multiline result to the output + { + echo "result<> $GITHUB_OUTPUT + + - name: Comment PR (End) + if: ${{ !failure() && !contains(github.event.comment.body, '--quiet') }} + uses: actions/github-script@v7 + env: + SUBWEIGHT: "${{ steps.subweight.outputs.result }}" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let runUrl = ${{ steps.build-link.outputs.run_url }} + let subweight = process.env.SUBWEIGHT; + + let subweightCollapsed = subweight + ? `
\n\nSubweight results:\n\n${subweight}\n\n
` + : ''; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has finished ✅ [See logs here](${runUrl})${subweightCollapsed}` + }) + + - name: Comment PR (Failure) + if: ${{ failure() && !contains(github.event.comment.body, '--quiet') }} + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + let jobUrl = ${{ steps.build-link.outputs.job_url }} + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Command "${{ steps.get-pr-comment.outputs.group2 }}" has failed ❌! [See logs here](${jobUrl})` + }) + + - name: Add 😕 reaction on failure + uses: actions/github-script@v7 + if: ${{ failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: 'confused' + }) + + - name: Add 👍 reaction on success + uses: actions/github-script@v7 + if: ${{ !failure() }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.reactions.createForIssueComment({ + comment_id: ${{ github.event.comment.id }}, + owner: context.repo.owner, + repo: context.repo.repo, + content: '+1' + }) diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml new file mode 100644 index 0000000000000000000000000000000000000000..5b32f954d0cd2a45c7f6f3aba2eda8c2280605d4 --- /dev/null +++ b/.github/workflows/command-backport.yml @@ -0,0 +1,95 @@ +name: Backport into stable + +on: + # This trigger can be problematic, see: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + # In our case it is fine since we only run it on merged Pull Requests and do not execute any of the repo code itself. + pull_request_target: + types: [ closed, labeled ] + +permissions: + contents: write # so it can comment + pull-requests: write # so it can create pull requests + issues: write + +jobs: + backport: + name: Backport pull request + runs-on: ubuntu-latest + + # The 'github.event.pull_request.merged' ensures that it got into master: + if: > + ( !startsWith(github.event.pull_request.base.ref, 'stable') ) && + ( + github.event_name == 'pull_request_target' && + github.event.pull_request.merged && + github.event.pull_request.base.ref == 'master' && + contains(github.event.pull_request.labels.*.name, 'A4-needs-backport') + ) + steps: + - uses: actions/checkout@v4 + + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.CMD_BOT_APP_ID }} + private_key: ${{ secrets.CMD_BOT_APP_KEY }} + + - name: Create backport pull requests + uses: korthout/backport-action@v3 + id: backport + with: + target_branches: stable2407 stable2409 + merge_commits: skip + github_token: ${{ steps.generate_token.outputs.token }} + pull_description: | + 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. + + + pull_title: | + [${target_branch}] Backport #${pull_number} + experimental: > + { + "conflict_resolution": "draft_commit_conflicts" + } + copy_assignees: true + + - name: Label Backports + if: ${{ steps.backport.outputs.created_pull_numbers != '' }} + uses: actions/github-script@v7 + with: + script: | + const pullNumbers = '${{ steps.backport.outputs.created_pull_numbers }}'.split(' '); + + for (const pullNumber of pullNumbers) { + await github.rest.issues.addLabels({ + issue_number: parseInt(pullNumber), + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['A3-backport'] + }); + console.log(`Added A3-backport label to PR #${pullNumber}`); + } + + - name: Request Review + if: ${{ steps.backport.outputs.created_pull_numbers != '' }} + uses: actions/github-script@v7 + with: + script: | + const pullNumbers = '${{ steps.backport.outputs.created_pull_numbers }}'.split(' '); + const reviewer = '${{ github.event.pull_request.user.login }}'; + + for (const pullNumber of pullNumbers) { + await github.pulls.createReviewRequest({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: parseInt(pullNumber), + reviewers: [ reviewer ] + }); + console.log(`Requested review from ${reviewer} for PR #${pullNumber}`); + } diff --git a/.github/workflows/command-bench-all.yml b/.github/workflows/command-bench-all.yml deleted file mode 100644 index 4128f86fb7c82f76e658fd39fad3365395a244e2..0000000000000000000000000000000000000000 --- a/.github/workflows/command-bench-all.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Command Bench All - -on: - workflow_dispatch: - inputs: - pr: - description: Number of the Pull Request - required: true - benchmark: - description: Pallet benchmark - type: choice - required: true - options: - - pallet - - substrate - - polkadot - - cumulus - pallet: - description: Pallet - required: false - type: string - default: pallet_name - target_dir: - description: Target directory - type: choice - options: - - substrate - - polkadot - - cumulus - runtime: - description: Runtime - type: choice - options: - - rococo - - westend - - asset-hub-kusama - - asset-hub-polkadot - - asset-hub-rococo - - asset-hub-westend - - bridge-hub-kusama - - bridge-hub-polkadot - - bridge-hub-rococo - - bridge-hub-westend - - collectives-polkadot - - collectives-westend - - coretime-rococo - - coretime-westend - - contracts-rococo - - glutton-kusama - - glutton-westend - - people-rococo - - people-westend - -jobs: - set-image: - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - cmd-bench-all: - needs: [set-image] - runs-on: arc-runners-polkadot-sdk-weights - container: - image: ${{ needs.set-image.outputs.IMAGE }} - permissions: - contents: write - pull-requests: write - steps: - - name: Download repo - uses: actions/checkout@v4 - - name: Install gh cli - id: gh - uses: ./.github/actions/set-up-gh - with: - pr-number: ${{ inputs.pr }} - GH_TOKEN: ${{ github.token }} - - name: Run bench all - run: | - "./scripts/bench-all.sh" "${{ inputs.benchmark }}" --runtime "${{ inputs.runtime }}" --pallet "${{ inputs.pallet }}" --target_dir "${{ inputs.target_dir }}" - - name: Report failure - if: ${{ failure() }} - run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

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

Action completed 🎉🎉

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

Command failed ❌

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

Action completed 🎉🎉

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

Command failed ❌

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

Action completed 🎉🎉

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

Command failed ❌

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

Action completed 🎉🎉

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

Please, see the documentation on how to use it' - }) + - name: Inform that the new command exist + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'We have migrated the command bot to GHA

Please, see the new usage instructions here. Soon the old commands will be disabled.' + }) \ No newline at end of file diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml new file mode 100644 index 0000000000000000000000000000000000000000..aa9de9474a7b434deaab92821ab3b9bdad4b82e6 --- /dev/null +++ b/.github/workflows/command-prdoc.yml @@ -0,0 +1,84 @@ +name: Command PrDoc + +on: + workflow_dispatch: + inputs: + pr: + type: number + description: Number of the Pull Request + required: true + bump: + type: choice + description: Default bump level for all crates + default: "TODO" + required: true + options: + - "TODO" + - "no change" + - "patch" + - "minor" + - "major" + audience: + type: choice + description: Audience of the PrDoc + default: "TODO" + required: true + options: + - "TODO" + - "Runtime Dev" + - "Runtime User" + - "Node Dev" + - "Node User" + overwrite: + type: choice + description: Overwrite existing PrDoc + default: "true" + required: true + options: + - "true" + - "false" + +concurrency: + group: command-prdoc + cancel-in-progress: true + +jobs: + preflight: + uses: ./.github/workflows/reusable-preflight.yml + + cmd-prdoc: + needs: [preflight] + runs-on: ubuntu-latest + timeout-minutes: 20 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + permissions: + contents: write + pull-requests: write + steps: + - name: Download repo + uses: actions/checkout@v4 + - name: Install gh cli + id: gh + uses: ./.github/actions/set-up-gh + with: + pr-number: ${{ inputs.pr }} + GH_TOKEN: ${{ github.token }} + - name: Generate PrDoc + run: | + python3 -m pip install -q cargo-workspace PyGithub whatthepatch pyyaml toml + + python3 .github/scripts/generate-prdoc.py --pr "${{ inputs.pr }}" --bump "${{ inputs.bump }}" --audience "${{ inputs.audience }}" --force "${{ inputs.overwrite }}" + + - name: Report failure + if: ${{ failure() }} + run: gh pr comment ${{ inputs.pr }} --body "

Command failed ❌

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

Command failed ❌

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

Action completed 🎉🎉

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

Command failed ❌

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

Action completed 🎉🎉

Run by @${{ github.actor }} for ${{ github.workflow }} completed 🎉. See logs here." - env: - RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000000000000000000000000000000000..514bac3973bf76dbab6a8c246054400d3fb1c4e9 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,130 @@ +name: Docs + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + +jobs: + preflight: + uses: ./.github/workflows/reusable-preflight.yml + + test-rustdoc: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [preflight] + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - uses: actions/checkout@v4 + - run: forklift cargo doc --workspace --all-features --no-deps + env: + SKIP_WASM_BUILD: 1 + test-doc: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [preflight] + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - uses: actions/checkout@v4 + - run: forklift cargo test --doc --workspace + env: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + + build-rustdoc: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [preflight, test-rustdoc] + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - uses: actions/checkout@v4 + - run: forklift cargo doc --all-features --workspace --no-deps + env: + SKIP_WASM_BUILD: 1 + RUSTDOCFLAGS: "-Dwarnings --default-theme=ayu --html-in-header ./docs/sdk/assets/header.html --extend-css ./docs/sdk/assets/theme.css --html-after-content ./docs/sdk/assets/after-content.html" + - run: rm -f ./target/doc/.lock + - run: mv ./target/doc ./crate-docs + - name: Inject Simple Analytics script + run: | + script_content="" + docs_dir="./crate-docs" + + inject_simple_analytics() { + find "$1" -name '*.html' | xargs -I {} -P "$(nproc)" bash -c 'file="{}"; echo "Adding Simple Analytics script to $file"; sed -i "s||'"$2"'|" "$file";' + } + + inject_simple_analytics "$docs_dir" "$script_content" + - run: echo "" > ./crate-docs/index.html + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.sha }}-doc + path: ./crate-docs/ + retention-days: 1 + if-no-files-found: error + + build-implementers-guide: + runs-on: ubuntu-latest + container: + image: paritytech/mdbook-utils:e14aae4a-20221123 + options: --user root + steps: + - uses: actions/checkout@v4 + - run: mdbook build ./polkadot/roadmap/implementers-guide + - run: mkdir -p artifacts + - run: mv polkadot/roadmap/implementers-guide/book artifacts/ + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.sha }}-guide + path: ./artifacts/ + retention-days: 1 + if-no-files-found: error + + publish-rustdoc: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + environment: subsystem-benchmarks + needs: [build-rustdoc, build-implementers-guide] + steps: + - uses: actions/checkout@v4 + with: + ref: gh-pages + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} + private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} + - name: Ensure destination dir does not exist + run: | + rm -rf book/ + rm -rf ${REF_NAME} + env: + REF_NAME: ${{ github.head_ref || github.ref_name }} + - name: Download rustdocs + uses: actions/download-artifact@v4 + with: + name: ${{ github.sha }}-doc + path: ${{ github.head_ref || github.ref_name }} + - name: Download guide + uses: actions/download-artifact@v4 + with: + name: ${{ github.sha }}-guide + path: /tmp + - run: mkdir -p book + - name: Move book files + run: mv /tmp/book/html/* book/ + - name: Push to GH-Pages branch + uses: github-actions-x/commit@v2.9 + with: + github-token: ${{ steps.app-token.outputs.token }} + push-branch: "gh-pages" + commit-message: "___Updated docs for ${{ github.head_ref || github.ref_name }}___" + force-add: "true" + files: ${{ github.head_ref || github.ref_name }}/ book/ + name: devops-parity + email: devops-team@parity.io diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index d22dc8724f3790625139762abbef29aa3781af6c..658da4451dc2e05da218848c7ad1e26dbf52ef21 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -157,7 +157,7 @@ jobs: timeout-minutes: 90 - name: Create PR on failure if: failure() && steps.check-compilation.outcome == 'failure' - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v5 + uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v5 with: path: "${{ env.template-path }}" token: ${{ steps.app_token.outputs.token }} @@ -166,9 +166,13 @@ jobs: title: "[Don't merge] Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" body: "The template has NOT been successfully built and needs to be inspected." branch: "update-template/${{ github.event.inputs.stable_release_branch }}" - - name: Push changes - run: | - git add -A . - git commit --allow-empty -m "Update to ${{ github.event.inputs.stable_release_branch }} triggered by ${{ github.event_name }}" - git push - working-directory: "${{ env.template-path }}" + - name: Create PR on success + uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 # v5 + with: + path: "${{ env.template-path }}" + token: ${{ steps.app_token.outputs.token }} + add-paths: | + ./* + title: "Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" + body: "This synchronizes the template to the ${{ github.event.inputs.stable_release_branch }} branch." + branch: "update-template/${{ github.event.inputs.stable_release_branch }}" diff --git a/.github/workflows/misc-update-wishlist-leaderboard.yml b/.github/workflows/misc-update-wishlist-leaderboard.yml index 68625e5433ca42295920e7449f8b3bed616c24db..3261687176746841a57bf2d247aeb6f596310c8b 100644 --- a/.github/workflows/misc-update-wishlist-leaderboard.yml +++ b/.github/workflows/misc-update-wishlist-leaderboard.yml @@ -11,6 +11,7 @@ permissions: jobs: update-wishlist-leaderboard: + if: github.repository == 'paritytech/polkadot-sdk' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index 9f96b92e0ce73eb1874ff7e11c9e43d820eaa649..a5af041185728a3bba5bb9379d105a44efe46b29 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -8,11 +8,15 @@ 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 + jobs: check-publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 @@ -20,7 +24,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.8.0 + run: cargo install parity-publish@0.8.0 --locked -q - name: parity-publish check run: parity-publish --color always check --allow-unpublished diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index bee709a12076ed4a5ff212796f7691650f32ad7f..f9bc6ce4daeaa8b3c2f4ce9341fe03d5f9247188 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest environment: master steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 @@ -18,7 +18,7 @@ jobs: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.8.0 + run: cargo install parity-publish@0.8.0 --locked -q - name: parity-publish claim env: diff --git a/.github/workflows/publish-subsystem-benchmarks.yml b/.github/workflows/publish-subsystem-benchmarks.yml deleted file mode 100644 index e5b9db0836f3f6a944778a30df49ea7f98b3af34..0000000000000000000000000000000000000000 --- a/.github/workflows/publish-subsystem-benchmarks.yml +++ /dev/null @@ -1,55 +0,0 @@ -# The actions takes json file as input and runs github-action-benchmark for it. - -on: - workflow_dispatch: - inputs: - benchmark-data-dir-path: - description: "Path to the benchmark data directory" - required: true - type: string - output-file-path: - description: "Path to the benchmark data file" - required: true - type: string - -jobs: - subsystem-benchmarks: - runs-on: ubuntu-latest - environment: subsystem-benchmarks - steps: - - name: Validate inputs - run: | - echo "${{ github.event.inputs.benchmark-data-dir-path }}" | grep -P '^[a-z\-]' - echo "${{ github.event.inputs.output-file-path }}" | grep -P '^[a-z\-]+\.json' - - - name: Checkout Sources - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: "gh-pages" - - - name: Copy bench results - id: step_one - run: | - cp bench/gitlab/${{ github.event.inputs.output-file-path }} ${{ github.event.inputs.output-file-path }} - - - name: Switch branch - id: step_two - run: | - git checkout master -- - - - uses: actions/create-github-app-token@v1 - id: app-token - with: - app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} - private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} - - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - tool: "customSmallerIsBetter" - name: ${{ github.event.inputs.benchmark-data-dir-path }} - output-file-path: ${{ github.event.inputs.output-file-path }} - benchmark-data-dir-path: "bench/${{ github.event.inputs.benchmark-data-dir-path }}" - github-token: ${{ steps.app-token.outputs.token }} - auto-push: true diff --git a/.github/workflows/release-10_rc-automation.yml b/.github/workflows/release-10_rc-automation.yml index 2d91850b82c180e25bbf87dfef529f8ab4667edd..195c14dbd5ab9a102787221f19cecc6319bf46af 100644 --- a/.github/workflows/release-10_rc-automation.yml +++ b/.github/workflows/release-10_rc-automation.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: fetch-depth: 0 diff --git a/.github/workflows/release-30_publish_release_draft.yml b/.github/workflows/release-30_publish_release_draft.yml index 6d31ca7a7365d77f6dd34c721b66acdc75ddbfa7..dd6a111d67e815077c0764368a63d29caf93a46b 100644 --- a/.github/workflows/release-30_publish_release_draft.yml +++ b/.github/workflows/release-30_publish_release_draft.yml @@ -26,6 +26,7 @@ jobs: uses: "./.github/workflows/release-srtool.yml" with: excluded_runtimes: "substrate-test bp cumulus-test kitchensink minimal-template parachain-template penpal polkadot-test seedling shell frame-try sp solochain-template" + build_opts: "--features on-chain-release-build" build-binaries: runs-on: ubuntu-latest @@ -35,7 +36,7 @@ jobs: binary: [ [frame-omni-bencher, frame-omni-bencher], [staging-chain-spec-builder, chain-spec-builder] ] steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 - name: Install protobuf-compiler run: | @@ -62,7 +63,7 @@ jobs: asset_upload_url: ${{ steps.create-release.outputs.upload_url }} steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 - name: Download artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -133,7 +134,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 - name: Download artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index cda10f2ebf15df72579508f3e4bb3252cb4bff1b..72e01a4833e254f0c329465a190206268397382e 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -45,7 +45,7 @@ on: type: string default: docker.io - # The owner is often the same than the Docker Hub username but does ont have to be. + # The owner is often the same as the Docker Hub username but does ont have to be. # In our case, it is not. owner: description: Owner of the container image repo @@ -58,6 +58,10 @@ on: default: v0.9.18 required: true + stable_tag: + description: Tag matching the actual stable release version in the format stableYYMM or stableYYMM-X for patch releases + required: true + permissions: contents: write @@ -71,16 +75,43 @@ env: # EVENT_ACTION: ${{ github.event.action }} EVENT_NAME: ${{ github.event_name }} IMAGE_TYPE: ${{ inputs.image_type }} - VERSION: ${{ inputs.version }} jobs: + validate-inputs: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.validate_inputs.outputs.VERSION }} + release_id: ${{ steps.validate_inputs.outputs.RELEASE_ID }} + stable_tag: ${{ steps.validate_inputs.outputs.stable_tag }} + + steps: + - name: Checkout sources + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + + - name: Validate inputs + id: validate_inputs + run: | + . ./.github/scripts/common/lib.sh + + VERSION=$(filter_version_from_input "${{ inputs.version }}") + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + + RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_OUTPUT + + echo "Release ID: $RELEASE_ID" + + STABLE_TAG=$(validate_stable_tag ${{ inputs.stable_tag }}) + echo "stable_tag=${STABLE_TAG}" >> $GITHUB_OUTPUT + fetch-artifacts: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest + needs: [validate-inputs] steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 #TODO: this step will be needed when automated triggering will work #this step runs only if the workflow is triggered automatically when new release is published @@ -102,9 +133,7 @@ jobs: run: | . ./.github/scripts/common/lib.sh - VERSION=$(filter_version_from_input "${{ inputs.version }}") - echo "VERSION=${VERSION}" >> $GITHUB_ENV - + VERSION="${{ needs.validate-inputs.outputs.VERSION }}" fetch_release_artifacts_from_s3 - name: Fetch chain-spec-builder rc artifacts or release artifacts based on release id @@ -112,7 +141,8 @@ jobs: if: ${{ env.EVENT_NAME == 'workflow_dispatch' && inputs.binary == 'chain-spec-builder' }} run: | . ./.github/scripts/common/lib.sh - RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + + RELEASE_ID="${{ needs.validate-inputs.outputs.RELEASE_ID }}" fetch_release_artifacts - name: Upload artifacts @@ -124,12 +154,12 @@ jobs: build-container: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest - needs: fetch-artifacts + needs: [fetch-artifacts, validate-inputs] environment: release steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Download artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -157,8 +187,7 @@ jobs: run: | . ./.github/scripts/common/lib.sh - RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") - release=release-$RELEASE_ID && \ + release="release-${{ needs.validate-inputs.outputs.RELEASE_ID }}" && \ echo "release=${release}" >> $GITHUB_OUTPUT commit=$(git rev-parse --short HEAD) && \ @@ -174,12 +203,17 @@ jobs: id: fetch_release_refs run: | chmod a+rx $BINARY - [[ $BINARY != 'chain-spec-builder' ]] && VERSION=$(./$BINARY --version | awk '{ print $2 }' ) - release=$( echo $VERSION | cut -f1 -d- ) + if [[ $BINARY != 'chain-spec-builder' ]]; then + VERSION=$(./$BINARY --version | awk '{ print $2 }' ) + release=$( echo $VERSION | cut -f1 -d- ) + else + release=$(echo ${{ needs.validate-inputs.outputs.VERSION }} | sed 's/^v//') + fi + echo "tag=latest" >> $GITHUB_OUTPUT echo "release=${release}" >> $GITHUB_OUTPUT - echo "stable=stable" >> $GITHUB_OUTPUT + echo "stable=${{ needs.validate-inputs.outputs.stable_tag }}" >> $GITHUB_OUTPUT - name: Build Injected Container image for polkadot rc or chain-spec-builder if: ${{ env.BINARY == 'polkadot' || env.BINARY == 'chain-spec-builder' }} @@ -257,14 +291,14 @@ jobs: build-polkadot-release-container: # this job will be triggered for polkadot release build if: ${{ inputs.binary == 'polkadot' && inputs.image_type == 'release' }} runs-on: ubuntu-latest - needs: fetch-latest-debian-package-version + needs: [fetch-latest-debian-package-version, validate-inputs] environment: release steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Cache Docker layers uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -288,14 +322,14 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: push: true file: docker/dockerfiles/polkadot/polkadot_injected_debian.Dockerfile # TODO: The owner should be used below but buildx does not resolve the VARs # TODO: It would be good to get rid of this GHA that we don't really need. tags: | - parity/polkadot:stable + parity/polkadot:${{ needs.validate-inputs.outputs.stable_tag }} parity/polkadot:latest parity/polkadot:${{ needs.fetch-latest-debian-package-version.outputs.polkadot_container_tag }} build-args: | diff --git a/.github/workflows/release-branchoff-stable.yml b/.github/workflows/release-branchoff-stable.yml new file mode 100644 index 0000000000000000000000000000000000000000..c4c50f5398e819fde478f333d00b10b176a7d507 --- /dev/null +++ b/.github/workflows/release-branchoff-stable.yml @@ -0,0 +1,105 @@ +name: Release - Branch off stable branch + +on: + workflow_dispatch: + inputs: + stable_version: + description: New stable version in the format stableYYMM + required: true + type: string + + node_version: + description: Version of the polkadot node in the format vX.XX.X (e.g. 1.15.0) + required: true + +jobs: + # TODO: Activate this job when the pipeline is moved to the fork in the `paritytech-release` org + # check-workflow-can-run: + # uses: paritytech-release/sync-workflows/.github/workflows/check-syncronization.yml@latest + + + prepare-tooling: + runs-on: ubuntu-latest + outputs: + node_version: ${{ steps.validate_inputs.outputs.node_version }} + stable_version: ${{ steps.validate_inputs.outputs.stable_version }} + + steps: + - name: Checkout sources + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + + - name: Validate inputs + id: validate_inputs + run: | + . ./.github/scripts/common/lib.sh + + node_version=$(filter_version_from_input "${{ inputs.node_version }}") + echo "node_version=${node_version}" >> $GITHUB_OUTPUT + + stable_version=$(validate_stable_tag ${{ inputs.stable_version }}) + echo "stable_version=${stable_version}" >> $GITHUB_OUTPUT + + create-stable-branch: + # needs: [check-workflow-can-run, prepare-tooling] + needs: [prepare-tooling] + # if: needs. check-workflow-can-run.outputs.checks_passed == 'true' + runs-on: ubuntu-latest + + env: + PGP_KMS_KEY: ${{ secrets.PGP_KMS_KEY }} + PGP_KMS_HASH: ${{ secrets.PGP_KMS_HASH }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + STABLE_BRANCH_NAME: ${{ needs.prepare-tooling.outputs.stable_version }} + + steps: + - name: Install pgpkkms + run: | + # Install pgpkms that is used to sign commits + pip install git+https://github.com/paritytech-release/pgpkms.git@5a8f82fbb607ea102d8c178e761659de54c7af69 + + - name: Checkout sources + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + with: + ref: master + + - name: Import gpg keys + run: | + . ./.github/scripts/common/lib.sh + + import_gpg_keys + + + - name: Config git + run: | + git config --global commit.gpgsign true + git config --global gpg.program /home/runner/.local/bin/pgpkms-git + git config --global user.name "ParityReleases" + git config --global user.email "release-team@parity.io" + git config --global user.signingKey "90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" + + - name: Create stable branch + run: | + git checkout -b "$STABLE_BRANCH_NAME" + git show-ref "$STABLE_BRANCH_NAME" + + - name: Bump versions, reorder prdocs and push stable branch + run: | + . ./.github/scripts/release/release_lib.sh + + NODE_VERSION="${{ needs.prepare-tooling.outputs.node_version }}" + set_version "\(NODE_VERSION[^=]*= \)\".*\"" $NODE_VERSION "polkadot/node/primitives/src/lib.rs" + commit_with_message "Bump node version to $NODE_VERSION in polkadot-cli" + + SPEC_VERSION=$(get_spec_version $NODE_VERSION) + runtimes_list=$(get_filtered_runtimes_list) + set_spec_versions $SPEC_VERSION "${runtimes_list[@]}" + + # TODO: clarify what to do with the polkadot-parachain binary + # Set new version for polkadot-parachain binary to match the polkadot node binary + # set_polkadot_parachain_binary_version $NODE_VERSION "cumulus/polkadot-parachain/Cargo.toml" + + reorder_prdocs $NODE_VERSION + + git push origin "$STABLE_BRANCH_NAME" diff --git a/.github/workflows/release-check-runtimes.yml b/.github/workflows/release-check-runtimes.yml index 930b8da772d0b6d19c0a723f9de09ff8fa5cd0b2..6666c115562f3f422639e6d4fdde74d9b169b71d 100644 --- a/.github/workflows/release-check-runtimes.yml +++ b/.github/workflows/release-check-runtimes.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Get list id: get-list @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout the repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Fetch release artifacts based on release id env: diff --git a/.github/workflows/release-clobber-stable.yml b/.github/workflows/release-clobber-stable.yml index 50c20563b4347334aa591f0f4f2e544f17f12afe..0d2ce78ab7816835cf4fcbe7a0213a2a1ae47980 100644 --- a/.github/workflows/release-clobber-stable.yml +++ b/.github/workflows/release-clobber-stable.yml @@ -24,7 +24,7 @@ jobs: AUDITED: audited steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Prechecks run: | diff --git a/.github/workflows/release-srtool.yml b/.github/workflows/release-srtool.yml index e1dc42afc6e98ded04c5689a28b224bb239114c0..83119dd4ed24363172c547b394f8a7d8e305e32b 100644 --- a/.github/workflows/release-srtool.yml +++ b/.github/workflows/release-srtool.yml @@ -9,6 +9,8 @@ on: inputs: excluded_runtimes: type: string + build_opts: + type: string outputs: published_runtimes: value: ${{ jobs.find-runtimes.outputs.runtime }} @@ -26,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 with: fetch-depth: 0 @@ -67,13 +69,15 @@ jobs: matrix: ${{ fromJSON(needs.find-runtimes.outputs.runtime) }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.0.0 with: fetch-depth: 0 - name: Srtool build id: srtool_build uses: chevdor/srtool-actions@v0.9.2 + env: + BUILD_OPTS: ${{ inputs.build_opts }} with: chain: ${{ matrix.chain }} runtime_dir: ${{ matrix.runtime_dir }} diff --git a/.github/workflows/reusable-check-changed-files.yml b/.github/workflows/reusable-check-changed-files.yml deleted file mode 100644 index 47f0620439c77f262c4fda1b68f0e250f2ec8ac6..0000000000000000000000000000000000000000 --- a/.github/workflows/reusable-check-changed-files.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Reusable workflow to perform checks and generate conditions for other workflows. -# Currently it checks if any Rust (build-related) file is changed -# and if the current (caller) workflow file is changed. -# Example: -# -# jobs: -# changes: -# permissions: -# pull-requests: read -# uses: ./.github/workflows/reusable-check-changed-files.yml -# some-job: -# needs: changes -# if: ${{ needs.changes.outputs.rust }} -# ....... - -name: Check changes files - -on: - workflow_call: - # Map the workflow outputs to job outputs - outputs: - rust: - value: ${{ jobs.changes.outputs.rust }} - description: "true if any of the build-related OR current (caller) workflow files have changed" - current-workflow: - value: ${{ jobs.changes.outputs.current-workflow }} - description: "true if current (caller) workflow file has changed" - -jobs: - changes: - runs-on: ubuntu-latest - permissions: - pull-requests: read - outputs: - # true if current workflow (caller) file is changed - rust: ${{ steps.filter.outputs.rust == 'true' || steps.filter.outputs.current-workflow == 'true' }} - current-workflow: ${{ steps.filter.outputs.current-workflow }} - steps: - - id: current-file - run: echo "current-workflow-file=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT - - run: echo "${{ steps.current-file.outputs.current-workflow-file }}" - # For pull requests it's not necessary to checkout the code - - name: Checkout - if: github.event_name != 'pull_request' - uses: actions/checkout@v4 - - id: filter - uses: dorny/paths-filter@v3 - with: - predicate-quantifier: "every" - # current-workflow - check if the current (caller) workflow file is changed - # rust - check if any Rust (build-related) file is changed - filters: | - current-workflow: - - '${{ steps.current-file.outputs.current-workflow-file }}' - rust: - - '**/*' - - '!.github/**/*' - - '!prdoc/**/*' - - '!docs/**/*' diff --git a/.github/workflows/reusable-preflight.yml b/.github/workflows/reusable-preflight.yml new file mode 100644 index 0000000000000000000000000000000000000000..71823a97ff2eb70206fd132541793dbc20dff799 --- /dev/null +++ b/.github/workflows/reusable-preflight.yml @@ -0,0 +1,131 @@ +# Reusable workflow to set various useful variables +# and to perform checks and generate conditions for other workflows. +# Currently it checks if any Rust (build-related) file is changed +# and if the current (caller) workflow file is changed. +# Example: +# +# jobs: +# preflight: +# uses: ./.github/workflows/reusable-preflight.yml +# some-job: +# needs: changes +# if: ${{ needs.preflight.outputs.changes_rust }} +# ....... + +name: Preflight + +on: + workflow_call: + # Map the workflow outputs to job outputs + outputs: + changes_rust: + value: ${{ jobs.preflight.outputs.changes_rust }} + changes_currentWorkflow: + value: ${{ jobs.preflight.outputs.changes_currentWorkflow }} + + IMAGE: + value: ${{ jobs.preflight.outputs.IMAGE }} + description: "CI image" + RUNNER: + value: ${{ jobs.preflight.outputs.RUNNER }} + description: | + Runner name. + By default we use spot machines that can be terminated at any time. + Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + OLDLINUXRUNNER: + value: ${{ jobs.preflight.outputs.OLDLINUXRUNNER }} + + SOURCE_REF_NAME: + value: ${{ jobs.preflight.outputs.SOURCE_REF_NAME }} + description: "Name of the current branch for `push` or source branch for `pull_request`" + COMMIT_SHA: + value: ${{ jobs.preflight.outputs.COMMIT_SHA }} + description: "Sha of the current commit for `push` or head of the source branch for `pull_request`" + +jobs: + preflight: + runs-on: ubuntu-latest + outputs: + changes_rust: ${{ steps.set_changes.outputs.rust_any_changed || steps.set_changes.outputs.currentWorkflow_any_changed }} + changes_currentWorkflow: ${{ steps.set_changes.outputs.currentWorkflow_any_changed }} + + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + RUNNER: ${{ steps.set_runner.outputs.RUNNER }} + OLDLINUXRUNNER: ${{ steps.set_runner.outputs.OLDLINUXRUNNER }} + + SOURCE_REF_NAME: ${{ steps.set_vars.outputs.SOURCE_REF_NAME }} + COMMIT_SHA: ${{ steps.set_vars.outputs.COMMIT_SHA }} + + + steps: + + - uses: actions/checkout@v4 + + # + # Set changes + # + - id: current_file + shell: bash + run: | + echo "currentWorkflowFile=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT + echo "currentActionDir=$(echo ${{ github.action_path }} | sed -nE "s/.*(\.github\/actions\/[a-zA-Z0-9_-]*)/\1/p")" >> $GITHUB_OUTPUT + + - name: Set changes + id: set_changes + uses: tj-actions/changed-files@v45 + with: + files_yaml: | + rust: + - '**/*' + - '!.github/**/*' + - '!prdoc/**/*' + - '!docs/**/*' + currentWorkflow: + - '${{ steps.current_file.outputs.currentWorkflowFile }}' + - '.github/workflows/reusable-preflight.yml' + + # + # Set image + # + - name: Set image + id: set_image + shell: bash + run: cat .github/env >> $GITHUB_OUTPUT + + # + # Set runner + # + # By default we use spot machines that can be terminated at any time. + # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. + # + - id: set_runner + shell: bash + run: | + # Run merge queues on persistent runners + if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then + echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT + echo "OLDLINUXRUNNER=oldlinux-persistent" >> $GITHUB_OUTPUT + else + echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + echo "OLDLINUXRUNNER=oldlinux" >> $GITHUB_OUTPUT + fi + + # + # Set vars + # + - id: set_vars + shell: bash + run: | + export BRANCH_NAME=${{ github.head_ref || github.ref_name }} + echo "SOURCE_REF_NAME=${BRANCH_NAME//\//-}" >> $GITHUB_OUTPUT + echo "COMMIT_SHA=${{ github.event.pull_request.head.sha || github.sha }}" >> $GITHUB_OUTPUT + + + - name: log + shell: bash + run: | + echo "workflow file: ${{ steps.current_file.outputs.currentWorkflowFile }}" + echo "Modified: ${{ steps.set_changes.outputs.modified_keys }}" + echo "github.ref: ${{ github.ref }}" + echo "github.ref_name: ${{ github.ref_name }}" + echo "github.sha: ${{ github.sha }}" diff --git a/.github/workflows/review-bot.yml b/.github/workflows/review-bot.yml index 80c96b0ef537fe3eaf368c7a39b2c5eddbb994e0..3dd5b1114813dbb2e151319293ade7ce44f4aae9 100644 --- a/.github/workflows/review-bot.yml +++ b/.github/workflows/review-bot.yml @@ -15,7 +15,6 @@ on: jobs: review-approvals: runs-on: ubuntu-latest - environment: master steps: - name: Generate token id: app_token diff --git a/.github/workflows/runtimes-matrix.json b/.github/workflows/runtimes-matrix.json new file mode 100644 index 0000000000000000000000000000000000000000..102437876dafb207346ba9cce1de6935ed15c409 --- /dev/null +++ b/.github/workflows/runtimes-matrix.json @@ -0,0 +1,127 @@ +[ + { + "name": "dev", + "package": "kitchensink-runtime", + "path": "substrate/frame", + "header": "substrate/HEADER-APACHE2", + "template": "substrate/.maintain/frame-weight-template.hbs", + "uri": null, + "is_relay": false + }, + { + "name": "westend", + "package": "westend-runtime", + "path": "polkadot/runtime/westend", + "header": "polkadot/file_header.txt", + "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", + "uri": "wss://try-runtime-westend.polkadot.io:443", + "is_relay": true + }, + { + "name": "rococo", + "package": "rococo-runtime", + "path": "polkadot/runtime/rococo", + "header": "polkadot/file_header.txt", + "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", + "uri": "wss://try-runtime-rococo.polkadot.io:443", + "is_relay": true + }, + { + "name": "asset-hub-westend", + "package": "asset-hub-westend-runtime", + "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://westend-asset-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "asset-hub-rococo", + "package": "asset-hub-rococo-runtime", + "path": "cumulus/parachains/runtimes/assets/asset-hub-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://rococo-asset-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "bridge-hub-rococo", + "package": "bridge-hub-rococo-runtime", + "path": "cumulus/parachains/runtimes/bridges/bridge-hub-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://rococo-bridge-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "bridge-hub-westend", + "package": "bridge-hub-rococo-runtime", + "path": "cumulus/parachains/runtimes/bridges/bridge-hub-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://westend-bridge-hub-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "collectives-westend", + "package": "collectives-westend-runtime", + "path": "cumulus/parachains/runtimes/collectives/collectives-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://westend-collectives-rpc.polkadot.io:443" + }, + { + "name": "contracts-rococo", + "package": "contracts-rococo-runtime", + "path": "cumulus/parachains/runtimes/contracts/contracts-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://rococo-contracts-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "coretime-rococo", + "package": "coretime-rococo-runtime", + "path": "cumulus/parachains/runtimes/coretime/coretime-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://rococo-coretime-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "coretime-westend", + "package": "coretime-westend-runtime", + "path": "cumulus/parachains/runtimes/coretime/coretime-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://westend-coretime-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "glutton-westend", + "package": "glutton-westend-runtime", + "path": "cumulus/parachains/runtimes/gluttons/glutton-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": null, + "is_relay": false + }, + { + "name": "people-rococo", + "package": "people-rococo-runtime", + "path": "cumulus/parachains/runtimes/people/people-rococo", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://rococo-people-rpc.polkadot.io:443", + "is_relay": false + }, + { + "name": "people-westend", + "package": "people-westend-runtime", + "path": "cumulus/parachains/runtimes/people/people-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "uri": "wss://westend-people-rpc.polkadot.io:443", + "is_relay": false + } +] diff --git a/.github/workflows/subsystem-benchmarks.yml b/.github/workflows/subsystem-benchmarks.yml new file mode 100644 index 0000000000000000000000000000000000000000..210714d847ff032dee89a4dfbd36e9a34b7f5940 --- /dev/null +++ b/.github/workflows/subsystem-benchmarks.yml @@ -0,0 +1,149 @@ +name: Subsystem Benchmarks + +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: + timeout-minutes: 80 + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + strategy: + fail-fast: false + matrix: + features: + [ + { + name: "polkadot-availability-recovery", + bench: "availability-recovery-regression-bench", + }, + { + name: "polkadot-availability-distribution", + bench: "availability-distribution-regression-bench", + }, + { + name: "polkadot-node-core-approval-voting", + bench: "approval-voting-regression-bench", + }, + { + name: "polkadot-statement-distribution", + bench: "statement-distribution-regression-bench", + }, + ] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: Run Benchmarks + id: run-benchmarks + run: | + forklift cargo bench -p ${{ matrix.features.name }} --bench ${{ matrix.features.bench }} --features subsystem-benchmarks || echo "Benchmarks failed" + ls -lsa ./charts + + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.6 + with: + name: ${{matrix.features.bench}} + path: ./charts + + publish-benchmarks: + timeout-minutes: 60 + needs: [build] + if: github.ref == 'refs/heads/master' + environment: subsystem-benchmarks + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: gh-pages + fetch-depth: 0 + + - run: git checkout master -- + + - name: Download artifacts + uses: actions/download-artifact@v4.1.8 + with: + path: ./charts + + - name: Setup git + run: | + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + ls -lsR ./charts + + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ secrets.POLKADOTSDK_GHPAGES_APP_ID }} + private-key: ${{ secrets.POLKADOTSDK_GHPAGES_APP_KEY }} + + - name: Generate ${{ env.BENCH }} + env: + BENCH: availability-recovery-regression-bench + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} + github-token: ${{ steps.app-token.outputs.token }} + auto-push: true + + - name: Generate ${{ env.BENCH }} + env: + BENCH: availability-distribution-regression-bench + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} + github-token: ${{ steps.app-token.outputs.token }} + auto-push: true + + - name: Generate ${{ env.BENCH }} + env: + BENCH: approval-voting-regression-bench + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} + github-token: ${{ steps.app-token.outputs.token }} + auto-push: true + + - name: Generate ${{ env.BENCH }} + env: + BENCH: statement-distribution-regression-bench + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: "customSmallerIsBetter" + name: ${{ env.BENCH }} + output-file-path: ./charts/${{ env.BENCH }}/${{ env.BENCH }}.json + benchmark-data-dir-path: ./bench/${{ env.BENCH }} + github-token: ${{ steps.app-token.outputs.token }} + auto-push: true diff --git a/.github/workflows/tests-linux-stable-coverage.yml b/.github/workflows/tests-linux-stable-coverage.yml new file mode 100644 index 0000000000000000000000000000000000000000..90d7bc34a92602b30f4e210cc55dc450a49219d3 --- /dev/null +++ b/.github/workflows/tests-linux-stable-coverage.yml @@ -0,0 +1,123 @@ +# GHA for test-linux-stable-int, test-linux-stable, test-linux-stable-oldkernel +name: tests linux stable coverage + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + preflight: + uses: ./.github/workflows/reusable-preflight.yml + if: contains(github.event.label.name, 'GHA-coverage') || contains(github.event.pull_request.labels.*.name, 'GHA-coverage') + + # + # + # + test-linux-stable-coverage: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 120 + 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. + # + # -Cinstrument-coverage slows everything down but it is necessary for code coverage + # https://doc.rust-lang.org/rustc/instrument-coverage.html + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings -Cinstrument-coverage" + LLVM_PROFILE_FILE: "/__w/polkadot-sdk/polkadot-sdk/target/coverage/cargo-test-${{ matrix.ci_node_index }}-%p-%m.profraw" + strategy: + fail-fast: false + matrix: + ci_node_index: [1, 2, 3, 4, 5] + ci_node_total: [5] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - run: rustup component add llvm-tools-preview + - run: cargo install cargo-llvm-cov + + - run: mkdir -p target/coverage + + # Some tests are excluded because they run very slowly or fail with -Cinstrument-coverage + - name: run tests + run: > + time cargo llvm-cov nextest + --no-report --release + --workspace + --locked --no-fail-fast + --features try-runtime,ci-only-tests,experimental,riscv + --filter-expr " + !test(/.*benchmark.*/) + - test(/recovers_from_only_chunks_if_pov_large::case_1/) + - test(/participation_requests_reprioritized_for_newly_included/) + - test(/availability_is_recovered_from_chunks_if_no_group_provided::case_1/) + - test(/rejects_missing_inherent_digest/) + - test(/availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunks_only::case_1/) + - test(/availability_is_recovered_from_chunks_if_no_group_provided::case_2/) + - test(/all_security_features_work/) + - test(/nonexistent_cache_dir/) + - test(/recovers_from_only_chunks_if_pov_large::case_3/) + - test(/recovers_from_only_chunks_if_pov_large::case_2/) + - test(/authoring_blocks/) + - test(/rejects_missing_seals/) + - test(/generate_chain_spec/) + - test(/get_preset/) + - test(/list_presets/) + - test(/tests::receive_rate_limit_is_enforced/) + - test(/polkadot-availability-recovery/) + " + --partition count:${{ matrix.ci_node_index }}/${{ matrix.ci_node_total }} + + - name: generate report + run: cargo llvm-cov report --release --codecov --output-path coverage-${{ matrix.ci_node_index }}.lcov + - name: upload report + uses: actions/upload-artifact@v4 + with: + name: coverage-report-${{ matrix.ci_node_index }}.lcov + path: coverage-${{ matrix.ci_node_index }}.lcov + + # + # + # Upload to codecov + upload-reports: + needs: [test-linux-stable-coverage] + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: reports + pattern: coverage-report-* + merge-multiple: true + - run: ls -al reports/ + - name: Upload to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + directory: reports + root_dir: /__w/polkadot-sdk/polkadot-sdk/ + + # + # + # + remove-label: + runs-on: ubuntu-latest + needs: [upload-reports] + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v4 + - uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: GHA-coverage \ No newline at end of file diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index da4bb40a2e78fed9b09ce8d79438ef203a63efe8..6cf71422511cc3d5b698725c576707a732bc9645 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -6,40 +6,24 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: - changes: - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - needs: changes - if: ${{ needs.changes.outputs.rust }} - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT + preflight: + uses: ./.github/workflows/reusable-preflight.yml test-linux-stable-int: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: 1 @@ -51,16 +35,16 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: WASM_BUILD_NO_COLOR=1 time forklift cargo test -p staging-node-cli --release --locked -- --ignored + run: WASM_BUILD_NO_COLOR=1 forklift cargo test -p staging-node-cli --release --locked -- --ignored # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUST_TOOLCHAIN: stable # Enable debug assertions since we are running optimized builds for testing @@ -70,4 +54,68 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + run: forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + + test-linux-stable: + needs: [preflight] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ matrix.runners }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + partition: [1/3, 2/3, 3/3] + runners: + [ + "${{ needs.preflight.outputs.RUNNER }}", + "${{ needs.preflight.outputs.OLDLINUXRUNNER }}", + ] + container: + image: ${{ needs.preflight.outputs.IMAGE }} + # needed for tests that use unshare syscall + options: --security-opt seccomp=unconfined + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + forklift cargo nextest run \ + --workspace \ + --locked \ + --release \ + --no-fail-fast \ + --features try-runtime,experimental,riscv,ci-only-tests \ + --partition count:${{ matrix.partition }} + # run runtime-api tests with `enable-staging-api` feature on the 1st node + - name: runtime-api tests + if: ${{ matrix.partition == '1/3' }} + run: forklift cargo nextest run -p sp-api-test --features enable-staging-api + + confirm-required-jobs-passed: + runs-on: ubuntu-latest + name: All tests passed + # If any new job gets added, be sure to add it to this array + needs: + [ + test-linux-stable-int, + test-linux-stable-runtime-benchmarks, + test-linux-stable, + ] + 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/tests-misc.yml b/.github/workflows/tests-misc.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f2b617b847de584d3755b9c1e9cd9d3cb970e10 --- /dev/null +++ b/.github/workflows/tests-misc.yml @@ -0,0 +1,380 @@ +name: tests misc + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +# Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers + +jobs: + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + + # more information about this job can be found here: + # https://github.com/paritytech/substrate/pull/3778 + test-full-crypto-feature: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-C debug-assertions" + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: script + run: | + cd substrate/primitives/core/ + forklift cargo build --locked --no-default-features --features full_crypto + cd ../application-crypto + forklift cargo build --locked --no-default-features --features full_crypto + + test-frame-examples-compile-to-wasm: + timeout-minutes: 20 + # into one job + needs: [preflight, test-full-crypto-feature] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-C debug-assertions" + RUST_BACKTRACE: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + cd substrate/frame/examples/offchain-worker/ + forklift cargo build --locked --target=wasm32-unknown-unknown --no-default-features + cd ../basic + forklift cargo build --locked --target=wasm32-unknown-unknown --no-default-features + + test-frame-ui: + timeout-minutes: 60 + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-C debug-assertions -D warnings" + RUST_BACKTRACE: 1 + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" + # Ensure we run the UI tests. + RUN_UI_TESTS: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + forklift cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,no-metadata-docs,try-runtime,experimental + forklift cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs,try-runtime,experimental + forklift cargo test --locked -q --profile testnet -p xcm-procedural + forklift cargo test --locked -q --profile testnet -p frame-election-provider-solution-type + forklift cargo test --locked -q --profile testnet -p sp-api-test + # There is multiple version of sp-runtime-interface in the repo. So we point to the manifest. + forklift cargo test --locked -q --profile testnet --manifest-path substrate/primitives/runtime-interface/Cargo.toml + + test-deterministic-wasm: + timeout-minutes: 20 + needs: [preflight, test-frame-examples-compile-to-wasm] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + WASM_BUILD_NO_COLOR: 1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + # build runtime + forklift cargo build -q --locked --release -p westend-runtime -p rococo-runtime + # make checksum + sha256sum target/release/wbuild/*-runtime/target/wasm32-unknown-unknown/release/*.wasm > checksum.sha256 + cargo clean + # build again + forklift cargo build -q --locked --release -p westend-runtime -p rococo-runtime + # confirm checksum + sha256sum -c checksum.sha256 + + cargo-check-benches: + needs: [preflight] + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} + timeout-minutes: 60 + strategy: + matrix: + branch: [master, current] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # if branch is master, use the branch, otherwise set empty string, so it uses the current context + # either PR (including forks) or merge group (main repo) + ref: ${{ matrix.branch == 'master' && matrix.branch || '' }} + + - name: script + run: | + ARTIFACTS_DIR=./artifacts + BENCH_TRIE_READ=::trie::read::small + BENCH_NODE_IMPORT=::node::import::sr25519::transfer_keep_alive::paritydb::small + mkdir -p $ARTIFACTS_DIR + + SKIP_WASM_BUILD=1 forklift cargo check --locked --benches --all; + forklift cargo run --locked --release -p node-bench -- $BENCH_TRIE_READ --json | tee $ARTIFACTS_DIR/bench_trie_read_small.json; + forklift cargo run --locked --release -p node-bench -- $BENCH_NODE_IMPORT --json | tee $ARTIFACTS_DIR/bench_transfer_keep_alive.json + + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.6 + with: + path: ./artifacts + name: cargo-check-benches-${{ matrix.branch }}-${{ github.sha }} + retention-days: 1 + + node-bench-regression-guard: + timeout-minutes: 20 + if: always() && !cancelled() + # runs-on: arc-runners-polkadot-sdk + runs-on: ubuntu-latest + needs: [preflight, cargo-check-benches] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download artifact (master run) + uses: actions/download-artifact@v4.1.8 + with: + name: cargo-check-benches-master-${{ github.sha }} + path: ./artifacts/master + + - name: Download artifact (current run) + uses: actions/download-artifact@v4.1.8 + with: + name: cargo-check-benches-current-${{ github.sha }} + path: ./artifacts/current + + - name: script + id: compare + run: | + docker run --rm \ + -v $PWD/artifacts/master:/artifacts/master \ + -v $PWD/artifacts/current:/artifacts/current \ + paritytech/node-bench-regression-guard:latest \ + node-bench-regression-guard --reference /artifacts/master --compare-with /artifacts/current + + if [ $? -ne 0 ]; then + FAILED_MSG='### node-bench-regression-guard failed ❌, check the regression in *cargo-check-benches* job' + echo $FAILED_MSG + echo $FAILED_MSG >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "### node-bench-regression-guard passed ✅" >> $GITHUB_STEP_SUMMARY + fi + + test-node-metrics: + needs: [preflight] + timeout-minutes: 30 + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run tests + id: tests + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + run: | + forklift cargo build --bin polkadot-execute-worker --bin polkadot-prepare-worker --profile testnet --verbose --locked + mkdir -p ./artifacts + forklift cargo test --profile testnet --locked --features=runtime-metrics -p polkadot-node-metrics > ./artifacts/log.txt + echo "Metrics test passed" + + - name: Upload artifacts if failed + if: ${{ steps.tests.outcome != 'success' }} + uses: actions/upload-artifact@v4.3.6 + with: + name: node-metrics-failed + path: ./artifacts + + # more information about this job can be found here: + # https://github.com/paritytech/substrate/pull/6916 + check-tracing: + timeout-minutes: 20 + needs: [preflight, test-node-metrics] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: script + run: | + forklift cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features + forklift cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features --features=with-tracing + + check-metadata-hash: + timeout-minutes: 20 + needs: [preflight, check-tracing] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: script + run: | + forklift cargo build --locked -p westend-runtime --features metadata-hash + + cargo-hfuzz: + timeout-minutes: 20 + needs: [preflight, check-metadata-hash] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + # max 10s per iteration, 60s per file + HFUZZ_RUN_ARGS: | + --exit_upon_crash + --exit_code_upon_crash 1 + --timeout 10 + --run_time 60 + + # use git version of honggfuzz-rs until v0.5.56 is out, we need a few recent changes: + # https://github.com/rust-fuzz/honggfuzz-rs/pull/75 to avoid breakage on debian + # https://github.com/rust-fuzz/honggfuzz-rs/pull/81 fix to the above pr + # https://github.com/rust-fuzz/honggfuzz-rs/pull/82 fix for handling absolute CARGO_TARGET_DIR + HFUZZ_BUILD_ARGS: | + --config=patch.crates-io.honggfuzz.git="https://github.com/altaua/honggfuzz-rs" + --config=patch.crates-io.honggfuzz.rev="205f7c8c059a0d98fe1cb912cdac84f324cb6981" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run honggfuzz + run: | + cd substrate/primitives/arithmetic/fuzzer + forklift cargo hfuzz build + for target in $(cargo read-manifest | jq -r '.targets | .[] | .name'); + do + forklift cargo hfuzz run "$target" || { printf "fuzzing failure for %s\n" "$target"; exit 1; }; + done + + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.6 + with: + path: substrate/primitives/arithmetic/fuzzer/hfuzz_workspace/ + name: hfuzz-${{ github.sha }} + + cargo-check-each-crate: + timeout-minutes: 140 + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + RUSTFLAGS: "-D warnings" + CI_JOB_NAME: cargo-check-each-crate + strategy: + matrix: + index: [1, 2, 3, 4, 5, 6, 7] # 7 parallel jobs + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: script + run: | + mkdir -p /github/home/.forklift + cp .forklift/config.toml /github/home/.forklift/config.toml + PYTHONUNBUFFERED=x .github/scripts/check-each-crate.py ${{ matrix.index }} ${{ strategy.job-total }} + + cargo-check-all-crate-macos: + timeout-minutes: 30 + needs: [ preflight ] + runs-on: parity-macos + env: + SKIP_WASM_BUILD: 1 + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Set rust version from env file + run: | + RUST_VERSION=$(cat .github/env | sed -E 's/.*ci-unified:([^-]+)-([^-]+).*/\2/') + echo $RUST_VERSION + echo "RUST_VERSION=${RUST_VERSION}" >> $GITHUB_ENV + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@1ccc07ccd54b6048295516a3eb89b192c35057dc # master from 12.09.2024 + - name: Install rust ${{ env.RUST_VERSION }} + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + cache: false + toolchain: ${{ env.RUST_VERSION }} + target: wasm32-unknown-unknown + components: cargo, clippy, rust-docs, rust-src, rustfmt, rustc, rust-std + - name: Install protobuf + run: brew install protobuf + - name: cargo info + run: | + echo "######## rustup show ########" + rustup show + echo "######## cargo --version ########" + cargo --version + - name: Run cargo check + run: cargo check --workspace --locked + + confirm-required-test-misc-jobs-passed: + runs-on: ubuntu-latest + name: All test misc tests passed + # If any new job gets added, be sure to add it to this array + needs: + - test-full-crypto-feature + - test-frame-examples-compile-to-wasm + - test-frame-ui + - cargo-check-benches + - node-bench-regression-guard + - test-node-metrics + - check-tracing + - cargo-check-each-crate + - test-deterministic-wasm + # - cargo-hfuzz remove from required for now, as it's flaky + 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/tests.yml b/.github/workflows/tests.yml index 1be2dd7921e0c521606556b3e567eab1661257d0..1132c2ca4dd5fc99ff14b8333633788a3ff36da5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,31 +12,18 @@ concurrency: cancel-in-progress: true jobs: - 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 }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT + preflight: + uses: ./.github/workflows/reusable-preflight.yml + # This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. quick-benchmarks: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [ preflight ] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-C debug-assertions -D warnings" RUST_BACKTRACE: "full" @@ -46,16 +33,16 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet + run: forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet # cf https://github.com/paritytech/polkadot-sdk/issues/1652 test-syscalls: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [ preflight ] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} continue-on-error: true # this rarely triggers in practice env: SKIP_WASM_BUILD: 1 @@ -63,28 +50,29 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script + id: test run: | forklift cargo build --locked --profile production --target x86_64-unknown-linux-musl --bin polkadot-execute-worker --bin polkadot-prepare-worker cd polkadot/scripts/list-syscalls ./list-syscalls.rb ../../../target/x86_64-unknown-linux-musl/production/polkadot-execute-worker --only-used-syscalls | diff -u execute-worker-syscalls - ./list-syscalls.rb ../../../target/x86_64-unknown-linux-musl/production/polkadot-prepare-worker --only-used-syscalls | diff -u prepare-worker-syscalls - - # todo: - # after_script: - # - if [[ "$CI_JOB_STATUS" == "failed" ]]; then - # printf "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed.\n"; - # fi + - name: on_failure + if: failure() && steps.test.outcome == 'failure' + run: | + echo "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed." >> $GITHUB_STEP_SUMMARY + cargo-check-all-benches: - needs: [ set-image, changes ] - if: ${{ needs.changes.outputs.rust }} - runs-on: arc-runners-polkadot-sdk-beefy + needs: [ preflight ] + # if: ${{ needs.preflight.outputs.changes_rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} timeout-minutes: 60 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: SKIP_WASM_BUILD: 1 steps: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo check --all --benches + run: forklift cargo check --all --benches diff --git a/.gitignore b/.gitignore index e3e382af6195ed4692672285a14e1cf5f1600a85..0263626d832df2633a3c5fc9db18268c2cc9953b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ **/node_modules **/target/ **/wip/*.stderr +**/__pycache__/ /.cargo/config /.envrc artifacts diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f2babc6bd47237032b941a6bf9de4a91b031932..43123cdbfc413fddc376a68afc099b5ec590ef51 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ workflow: - if: $CI_COMMIT_BRANCH variables: - CI_IMAGE: !reference [.ci-unified, variables, CI_IMAGE] + CI_IMAGE: !reference [ .ci-unified, variables, CI_IMAGE ] # BUILDAH_IMAGE is defined in group variables BUILDAH_COMMAND: "buildah --storage-driver overlay2" RELENG_SCRIPTS_BRANCH: "master" @@ -39,7 +39,7 @@ default: - runner_system_failure - unknown_failure - api_failure - cache: {} + cache: { } interruptible: true .collect-artifacts: @@ -68,8 +68,8 @@ default: .common-before-script: before_script: - - !reference [.job-switcher, before_script] - - !reference [.pipeline-stopper-vars, script] + - !reference [ .job-switcher, before_script ] + - !reference [ .pipeline-stopper-vars, script ] .job-switcher: before_script: @@ -78,8 +78,8 @@ default: .kubernetes-env: image: "${CI_IMAGE}" before_script: - - !reference [.common-before-script, before_script] - - !reference [.prepare-env, before_script] + - !reference [ .common-before-script, before_script ] + - !reference [ .prepare-env, before_script ] tags: - kubernetes-parity-build @@ -107,12 +107,12 @@ default: .docker-env: image: "${CI_IMAGE}" variables: - FL_FORKLIFT_VERSION: !reference [.forklift, variables, FL_FORKLIFT_VERSION] + FL_FORKLIFT_VERSION: !reference [ .forklift, variables, FL_FORKLIFT_VERSION ] before_script: - - !reference [.common-before-script, before_script] - - !reference [.prepare-env, before_script] - - !reference [.rust-info-script, script] - - !reference [.forklift-cache, before_script] + - !reference [ .common-before-script, before_script ] + - !reference [ .prepare-env, before_script ] + - !reference [ .rust-info-script, script ] + - !reference [ .forklift-cache, before_script ] tags: - linux-docker @@ -268,82 +268,6 @@ remove-cancel-pipeline-message: PR_NUM: "${CI_COMMIT_REF_NAME}" trigger: project: "parity/infrastructure/ci_cd/pipeline-stopper" -# need to copy jobs this way because otherwise gitlab will wait -# for all 3 jobs to finish instead of cancelling if one fails -cancel-pipeline-test-linux-stable1: - extends: .cancel-pipeline-template - needs: - - job: "test-linux-stable 1/3" - -cancel-pipeline-test-linux-stable2: - extends: .cancel-pipeline-template - needs: - - job: "test-linux-stable 2/3" - -cancel-pipeline-test-linux-stable3: - extends: .cancel-pipeline-template - needs: - - job: "test-linux-stable 3/3" - -cancel-pipeline-cargo-check-benches1: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-benches 1/2" - -cancel-pipeline-cargo-check-benches2: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-benches 2/2" - -cancel-pipeline-test-linux-stable-int: - extends: .cancel-pipeline-template - needs: - - job: test-linux-stable-int - -cancel-pipeline-cargo-check-each-crate-1: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 1/6" - -cancel-pipeline-cargo-check-each-crate-2: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 2/6" - -cancel-pipeline-cargo-check-each-crate-3: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 3/6" - -cancel-pipeline-cargo-check-each-crate-4: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 4/6" - -cancel-pipeline-cargo-check-each-crate-5: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 5/6" - -cancel-pipeline-cargo-check-each-crate-6: - extends: .cancel-pipeline-template - needs: - - job: "cargo-check-each-crate 6/6" - -cancel-pipeline-cargo-check-each-crate-macos: - extends: .cancel-pipeline-template - needs: - - job: cargo-check-each-crate-macos - -cancel-pipeline-check-tracing: - extends: .cancel-pipeline-template - needs: - - job: check-tracing - -cancel-pipeline-cargo-clippy: - extends: .cancel-pipeline-template - needs: - - job: cargo-clippy cancel-pipeline-build-linux-stable: extends: .cancel-pipeline-template @@ -360,42 +284,12 @@ cancel-pipeline-build-linux-substrate: needs: - job: build-linux-substrate -cancel-pipeline-test-node-metrics: - extends: .cancel-pipeline-template - needs: - - job: test-node-metrics - -cancel-pipeline-test-frame-ui: - extends: .cancel-pipeline-template - needs: - - job: test-frame-ui - -cancel-pipeline-quick-benchmarks: - extends: .cancel-pipeline-template - needs: - - job: quick-benchmarks - -cancel-pipeline-check-try-runtime: - extends: .cancel-pipeline-template - needs: - - job: check-try-runtime - -cancel-pipeline-test-frame-examples-compile-to-wasm: - extends: .cancel-pipeline-template - needs: - - job: test-frame-examples-compile-to-wasm - cancel-pipeline-build-short-benchmark: extends: .cancel-pipeline-template needs: - job: build-short-benchmark -cancel-pipeline-check-runtime-migration-rococo: - extends: .cancel-pipeline-template - needs: - - job: check-runtime-migration-rococo - -cancel-pipeline-check-runtime-migration-westend: +cancel-pipeline-cargo-check-each-crate-macos: extends: .cancel-pipeline-template needs: - - job: check-runtime-migration-westend + - job: cargo-check-each-crate-macos \ No newline at end of file diff --git a/.gitlab/pipeline/build.yml b/.gitlab/pipeline/build.yml index 8658e92efc8f9f7ae463a67a52eaf3d3d37df2f7..a5de2173a71ffcdb16504515492e086ea59fd6ca 100644 --- a/.gitlab/pipeline/build.yml +++ b/.gitlab/pipeline/build.yml @@ -313,7 +313,7 @@ build-linux-substrate: # tldr: we need to checkout the branch HEAD explicitly because of our dynamic versioning approach while building the substrate binary # see https://github.com/paritytech/ci_cd/issues/682#issuecomment-1340953589 - git checkout -B "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" - - !reference [.forklift-cache, before_script] + - !reference [ .forklift-cache, before_script ] script: - time WASM_BUILD_NO_COLOR=1 cargo build --locked --release -p staging-node-cli - mv $CARGO_TARGET_DIR/release/substrate-node ./artifacts/substrate/substrate @@ -329,73 +329,6 @@ build-linux-substrate: # - printf '\n# building node-template\n\n' # - ./scripts/ci/node-template-release.sh ./artifacts/substrate/substrate-node-template.tar.gz -build-runtimes-polkavm: - stage: build - extends: - - .docker-env - - .common-refs - - .run-immediately - script: - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p minimal-template-runtime - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p westend-runtime - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p rococo-runtime - - SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p polkadot-test-runtime - -.build-subkey: - stage: build - extends: - - .docker-env - - .common-refs - - .run-immediately - # - .collect-artifact - variables: - # this variable gets overridden by "rusty-cachier environment inject", use the value as default - CARGO_TARGET_DIR: "$CI_PROJECT_DIR/target" - before_script: - - mkdir -p ./artifacts/subkey - - !reference [.forklift-cache, before_script] - script: - - cd ./substrate/bin/utils/subkey - - time SKIP_WASM_BUILD=1 cargo build --locked --release - # - cd - - # - mv $CARGO_TARGET_DIR/release/subkey ./artifacts/subkey/. - # - echo -n "Subkey version = " - # - ./artifacts/subkey/subkey --version | - # sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | - # tee ./artifacts/subkey/VERSION; - # - sha256sum ./artifacts/subkey/subkey | tee ./artifacts/subkey/subkey.sha256 - # - cp -r ./scripts/ci/docker/subkey.Dockerfile ./artifacts/subkey/ - -build-subkey-linux: - extends: .build-subkey - # DAG - needs: - - job: build-malus - artifacts: false -# tbd -# build-subkey-macos: -# extends: .build-subkey -# # duplicating before_script & script sections from .build-subkey hidden job -# # to overwrite rusty-cachier integration as it doesn't work on macos -# before_script: -# # skip timestamp script, the osx bash doesn't support printf %()T -# - !reference [.job-switcher, before_script] -# - mkdir -p ./artifacts/subkey -# script: -# - cd ./bin/utils/subkey -# - SKIP_WASM_BUILD=1 time cargo build --locked --release -# - cd - -# - mv ./target/release/subkey ./artifacts/subkey/. -# - echo -n "Subkey version = " -# - ./artifacts/subkey/subkey --version | -# sed -n -E 's/^subkey ([0-9.]+.*)/\1/p' | -# tee ./artifacts/subkey/VERSION; -# - sha256sum ./artifacts/subkey/subkey | tee ./artifacts/subkey/subkey.sha256 -# - cp -r ./scripts/ci/docker/subkey.Dockerfile ./artifacts/subkey/ -# after_script: [""] -# tags: -# - osx - # bridges # we need some non-binary artifacts in our bridges+zombienet image diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index 2b8b90ef19a47874b3a9de065038faee376a01ae..7d1f37dddd5138a0c039af32f6b1b5e9c81654af 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -1,51 +1,3 @@ -cargo-clippy: - stage: check - extends: - - .docker-env - - .common-refs - - .pipeline-stopper-artifacts - variables: - RUSTFLAGS: "-D warnings" - script: - - SKIP_WASM_BUILD=1 cargo clippy --all-targets --locked --workspace --quiet - - SKIP_WASM_BUILD=1 cargo clippy --all-targets --all-features --locked --workspace --quiet - -check-try-runtime: - stage: check - extends: - - .docker-env - - .common-refs - script: - - time cargo check --locked --all --features try-runtime - # this is taken from cumulus - # Check that parachain-template will compile with `try-runtime` feature flag. - - time cargo check --locked -p parachain-template-node --features try-runtime - # add after https://github.com/paritytech/substrate/pull/14502 is merged - # experimental code may rely on try-runtime and vice-versa - - time cargo check --locked --all --features try-runtime,experimental - -# from substrate -# not sure if it's needed in monorepo -check-dependency-rules: - stage: check - extends: - - .kubernetes-env - - .test-refs-no-trigger-prs-only - variables: - CI_IMAGE: "paritytech/tools:latest" - allow_failure: true - script: - - cd substrate/ - - ../.gitlab/ensure-deps.sh - -test-rust-features: - stage: check - extends: - - .kubernetes-env - - .test-refs-no-trigger-prs-only - script: - - bash .gitlab/rust-features.sh . - job-starter: stage: check image: paritytech/tools:latest @@ -55,105 +7,3 @@ job-starter: allow_failure: true script: - echo ok - -check-rust-feature-propagation: - stage: check - extends: - - .kubernetes-env - - .common-refs - script: - - zepter run check - -check-toml-format: - stage: check - extends: - - .kubernetes-env - - .common-refs - script: - - taplo format --check --config .config/taplo.toml - - echo "Please run `taplo format --config .config/taplo.toml` to fix any toml formatting issues" - -# More info can be found here: https://github.com/paritytech/polkadot/pull/5865 -.check-runtime-migration: - stage: check - extends: - - .docker-env - - .test-pr-refs - script: - - export RUST_LOG=remote-ext=debug,runtime=debug - - echo "---------- Downloading try-runtime CLI ----------" - - curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.5.4/try-runtime-x86_64-unknown-linux-musl -o try-runtime - - chmod +x ./try-runtime - - echo "Using try-runtime-cli version:" - - ./try-runtime --version - - echo "---------- Building ${PACKAGE} runtime ----------" - - time cargo build --release --locked -p "$PACKAGE" --features try-runtime - - echo "---------- Executing on-runtime-upgrade for ${NETWORK} ----------" - - > - time ./try-runtime ${COMMAND_EXTRA_ARGS} \ - --runtime ./target/release/wbuild/"$PACKAGE"/"$WASM" \ - on-runtime-upgrade --disable-spec-version-check --checks=all ${SUBCOMMAND_EXTRA_ARGS} live --uri ${URI} - - sleep 5 - -# Check runtime migrations for Parity managed relay chains -check-runtime-migration-westend: - stage: check - extends: - - .docker-env - - .test-pr-refs - - .check-runtime-migration - variables: - NETWORK: "westend" - PACKAGE: "westend-runtime" - WASM: "westend_runtime.compact.compressed.wasm" - URI: "wss://westend-try-runtime-node.parity-chains.parity.io:443" - SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" - -check-runtime-migration-rococo: - stage: check - extends: - - .docker-env - - .test-pr-refs - - .check-runtime-migration - variables: - NETWORK: "rococo" - PACKAGE: "rococo-runtime" - WASM: "rococo_runtime.compact.compressed.wasm" - URI: "wss://rococo-try-runtime-node.parity-chains.parity.io:443" - SUBCOMMAND_EXTRA_ARGS: "--no-weight-warnings" - -find-fail-ci-phrase: - stage: check - variables: - CI_IMAGE: "paritytech/tools:latest" - ASSERT_REGEX: "FAIL-CI" - GIT_DEPTH: 1 - extends: - - .kubernetes-env - - .test-pr-refs - script: - - set +e - - rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? - - if [ $exit_status -eq 0 ]; then - echo "$ASSERT_REGEX was found, exiting with 1"; - exit 1; - else - echo "No $ASSERT_REGEX was found, exiting with 0"; - exit 0; - fi - -check-core-crypto-features: - stage: check - extends: - - .docker-env - - .common-refs - script: - - pushd substrate/primitives/core - - ./check-features-variants.sh - - popd - - pushd substrate/primitives/application-crypto - - ./check-features-variants.sh - - popd - - pushd substrate/primitives/keyring - - ./check-features-variants.sh - - popd diff --git a/.gitlab/pipeline/publish.yml b/.gitlab/pipeline/publish.yml index 44cd1933a9cfa0b3cbff384ff4184a36bf864021..5ad9ae9bfb36d4e4efb6c9cc41453f06afe7f93d 100644 --- a/.gitlab/pipeline/publish.yml +++ b/.gitlab/pipeline/publish.yml @@ -62,83 +62,6 @@ publish-rustdoc: after_script: - rm -rf .git/ ./* -publish-subsystem-benchmarks: - stage: publish - variables: - CI_IMAGE: "paritytech/tools:latest" - extends: - - .kubernetes-env - - .publish-gh-pages-refs - needs: - - job: subsystem-benchmark-availability-recovery - artifacts: true - - job: subsystem-benchmark-availability-distribution - artifacts: true - - job: subsystem-benchmark-approval-voting - artifacts: true - - job: subsystem-benchmark-statement-distribution - artifacts: true - - job: publish-rustdoc - artifacts: false - script: - # setup ssh - - eval $(ssh-agent) - - ssh-add - <<< ${GITHUB_SSH_PRIV_KEY} - - mkdir ~/.ssh && touch ~/.ssh/known_hosts - - ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts - # Set git config - - rm -rf .git/config - - git config user.email "devops-team@parity.io" - - git config user.name "${GITHUB_USER}" - - git config remote.origin.url "git@github.com:/paritytech/${CI_PROJECT_NAME}.git" - - git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" - - git fetch origin gh-pages - # Push result to github - - git checkout gh-pages --force - - mkdir -p bench/gitlab/ || echo "Directory exists" - - rm -rf bench/gitlab/*.json || echo "No json files" - - cp -r charts/*.json bench/gitlab/ - - git add bench/gitlab/ - - git commit -m "Add json files with benchmark results for ${CI_COMMIT_REF_NAME}" - - git push origin gh-pages - # artificial sleep to publish gh-pages - - sleep 300 - allow_failure: true - after_script: - - rm -rf .git/ ./* - -trigger_workflow: - stage: deploy - extends: - - .kubernetes-env - - .publish-gh-pages-refs - needs: - - job: publish-subsystem-benchmarks - artifacts: false - - job: subsystem-benchmark-availability-recovery - artifacts: true - - job: subsystem-benchmark-availability-distribution - artifacts: true - - job: subsystem-benchmark-approval-voting - artifacts: true - - job: subsystem-benchmark-statement-distribution - artifacts: true - script: - - echo "Triggering workflow" - - > - for benchmark in $(ls charts/*.json); do - export benchmark_name=$(basename $benchmark); - echo "Benchmark: $benchmark_name"; - export benchmark_dir=$(echo $benchmark_name | sed 's/\.json//'); - curl -q -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token $GITHUB_TOKEN" \ - https://api.github.com/repos/paritytech/${CI_PROJECT_NAME}/actions/workflows/publish-subsystem-benchmarks.yml/dispatches \ - -d "{\"ref\":\"refs/heads/master\",\"inputs\":{\"benchmark-data-dir-path\":\"$benchmark_dir\",\"output-file-path\":\"$benchmark_name\"}}"; - sleep 300; - done - allow_failure: true - # note: images are used not only in zombienet but also in rococo, wococo and versi .build-push-image: image: $BUILDAH_IMAGE diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 0103c6b76a2dfac2bfd080aa516ae9c34886bf46..0879870ae13c97dd7044f9224584dadac00c2046 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -110,121 +110,6 @@ test-linux-stable-codecov: codecovcli -v do-upload -f target/coverage/result/report-${CI_NODE_INDEX}.lcov --disable-search -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --fail-on-error --git-service github; fi - # - -test-linux-stable: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - variables: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - parallel: 3 - script: - # Build all but only execute 'runtime' tests. - - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" - - > - time cargo nextest run \ - --workspace \ - --locked \ - --release \ - --no-fail-fast \ - --features try-runtime,experimental,riscv,ci-only-tests \ - --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} - # Upload tests results to Elasticsearch - - echo "Upload test results to Elasticsearch" - - cat target/nextest/default/junit.xml | xq . > target/nextest/default/junit.json - - > - curl -v -XPOST --http1.1 \ - -u ${ELASTIC_USERNAME}:${ELASTIC_PASSWORD} \ - https://elasticsearch.parity-build.parity.io/unit-tests/_doc/${CI_JOB_ID} \ - -H 'Content-Type: application/json' \ - -d @target/nextest/default/junit.json || echo "failed to upload junit report" - # run runtime-api tests with `enable-staging-api` feature on the 1st node - - if [ ${CI_NODE_INDEX} == 1 ]; then time cargo nextest run -p sp-api-test --features enable-staging-api; fi - artifacts: - when: always - paths: - - target/nextest/default/junit.xml - reports: - junit: target/nextest/default/junit.xml - timeout: 90m - -test-linux-oldkernel-stable: - extends: test-linux-stable - tags: - - oldkernel-vm - -# https://github.com/paritytech/ci_cd/issues/864 -test-linux-stable-runtime-benchmarks: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - variables: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - script: - - time cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet - -# can be used to run all tests -# test-linux-stable-all: -# stage: test -# extends: -# - .docker-env -# - .common-refs -# - .run-immediately -# variables: -# RUST_TOOLCHAIN: stable -# # Enable debug assertions since we are running optimized builds for testing -# # but still want to have debug assertions. -# RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" -# parallel: 3 -# script: -# # Build all but only execute 'runtime' tests. -# - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" -# - > -# time cargo nextest run \ -# --workspace \ -# --locked \ -# --release \ -# --no-fail-fast \ -# --features runtime-benchmarks,try-runtime \ -# --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} -# # todo: add flacky-test collector - -# takes about 1,5h without cache -# can be used to check that nextest works correctly -# test-linux-stable-polkadot: -# stage: test -# timeout: 2h -# extends: -# - .docker-env -# - .common-refs -# - .run-immediately -# - .collect-artifacts-short -# variables: -# RUST_TOOLCHAIN: stable -# # Enable debug assertions since we are running optimized builds for testing -# # but still want to have debug assertions. -# RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" -# script: -# - mkdir -p artifacts -# - time cargo test --workspace -# --locked -# --profile testnet -# --features=runtime-benchmarks,runtime-metrics,try-runtime -- -# --skip upgrade_version_checks_should_work - test-doc: stage: test extends: @@ -252,176 +137,6 @@ test-rustdoc: script: - time cargo doc --workspace --all-features --no-deps -test-node-metrics: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .collect-artifacts-short - variables: - RUST_TOOLCHAIN: stable - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - script: - # Build the required workers. - - cargo build --bin polkadot-execute-worker --bin polkadot-prepare-worker --profile testnet --verbose --locked - - mkdir -p artifacts - - time cargo test --profile testnet - --locked - --features=runtime-metrics -p polkadot-node-metrics > artifacts/log.txt - -test-deterministic-wasm: - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: test-frame-ui - artifacts: false - script: - # build runtime - - WASM_BUILD_NO_COLOR=1 cargo build -q --locked --release -p westend-runtime -p rococo-runtime - # make checksum - - sha256sum target/release/wbuild/*-runtime/target/wasm32-unknown-unknown/release/*.wasm > checksum.sha256 - - cargo clean - # build again - - WASM_BUILD_NO_COLOR=1 cargo build -q --locked --release -p westend-runtime -p rococo-runtime - # confirm checksum - - sha256sum -c checksum.sha256 - -cargo-check-benches: - stage: test - artifacts: - expire_in: 10 days - variables: - CI_JOB_NAME: "cargo-check-benches" - extends: - - .docker-env - - .common-refs - - .run-immediately - - .collect-artifacts - - .pipeline-stopper-artifacts - before_script: - # TODO: DON'T FORGET TO CHANGE FOR PROD VALUES!!! - # merges in the master branch on PRs. skip if base is not master - - 'if [ $CI_COMMIT_REF_NAME != "master" ]; then - BASE=$(curl -s -H "Authorization: Bearer ${GITHUB_PR_TOKEN}" https://api.github.com/repos/paritytech-stg/polkadot-sdk/pulls/${CI_COMMIT_REF_NAME} | jq -r .base.ref); - printf "Merging base branch %s\n" "${BASE:=master}"; - if [ $BASE != "master" ]; then - echo "$BASE is not master, skipping merge"; - else - git config user.email "ci@gitlab.parity.io"; - git fetch origin "refs/heads/${BASE}"; - git merge --verbose --no-edit FETCH_HEAD; - fi - fi' - - !reference [.forklift-cache, before_script] - parallel: 2 - script: - - mkdir -p ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA - # this job is executed in parallel on two runners - - echo "___Running benchmarks___"; - - case ${CI_NODE_INDEX} in - 1) - SKIP_WASM_BUILD=1 time cargo check --locked --benches --all; - cargo run --locked --release -p node-bench -- ::trie::read::small --json - | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::trie::read::small.json; - echo "___Cache could be uploaded___"; - ;; - 2) - cargo run --locked --release -p node-bench -- ::node::import::sr25519::transfer_keep_alive::paritydb::small --json - | tee ./artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA/::node::import::sr25519::transfer_keep_alive::paritydb::small.json - ;; - esac - -node-bench-regression-guard: - # it's not belong to `build` semantically, but dag jobs can't depend on each other - # within the single stage - https://gitlab.com/gitlab-org/gitlab/-/issues/30632 - # more: https://github.com/paritytech/substrate/pull/8519#discussion_r608012402 - stage: build - extends: - - .docker-env - - .common-refs - needs: - # this is a DAG - - job: cargo-check-benches - artifacts: true - # polls artifact from master to compare with current result - # need to specify both parallel jobs from master because of the bug - # https://gitlab.com/gitlab-org/gitlab/-/issues/39063 - - project: $CI_PROJECT_PATH - job: "cargo-check-benches 1/2" - ref: master - artifacts: true - - project: $CI_PROJECT_PATH - job: "cargo-check-benches 2/2" - ref: master - artifacts: true - variables: - CI_IMAGE: "paritytech/node-bench-regression-guard:latest" - before_script: [""] - script: - - if [ $(ls -la artifacts/benches/ | grep master | wc -l) == 0 ]; then - echo "Couldn't find master artifacts"; - exit 1; - fi - - echo "------- IMPORTANT -------" - - echo "node-bench-regression-guard depends on the results of a cargo-check-benches job" - - echo "In case of this job failure, check your pipeline's cargo-check-benches" - - "node-bench-regression-guard --reference artifacts/benches/master-* - --compare-with artifacts/benches/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - after_script: [""] - -# if this fails run `bot update-ui` in the Pull Request or "./scripts/update-ui-tests.sh" locally -# see ./docs/contributor/CONTRIBUTING.md#ui-tests -test-frame-ui: - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: test-frame-examples-compile-to-wasm - artifacts: false - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions -D warnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" - # Ensure we run the UI tests. - RUN_UI_TESTS: 1 - script: - - time cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,no-metadata-docs,try-runtime,experimental - - time cargo test --locked -q --profile testnet -p frame-support-test --features=frame-feature-testing,frame-feature-testing-2,no-metadata-docs,try-runtime,experimental - - time cargo test --locked -q --profile testnet -p xcm-procedural - - time cargo test --locked -q --profile testnet -p frame-election-provider-solution-type - - time cargo test --locked -q --profile testnet -p sp-api-test - # There is multiple version of sp-runtime-interface in the repo. So we point to the manifest. - - time cargo test --locked -q --profile testnet --manifest-path substrate/primitives/runtime-interface/Cargo.toml - - cat /cargo_target_dir/debug/.fingerprint/memory_units-759eddf317490d2b/lib-memory_units.json || true - -# This job runs all benchmarks defined in the `/bin/node/runtime` once to check that there are no errors. -quick-benchmarks: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions -D warnings" - RUST_BACKTRACE: "full" - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" - script: - - time cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks --quiet -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet - quick-benchmarks-omni: stage: test extends: @@ -439,110 +154,6 @@ quick-benchmarks-omni: - time cargo build --locked --quiet --release -p asset-hub-westend-runtime --features runtime-benchmarks - time cargo run --locked --release -p frame-omni-bencher --quiet -- v1 benchmark pallet --runtime target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm --all --steps 2 --repeat 1 --quiet -test-frame-examples-compile-to-wasm: - # into one job - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: test-full-crypto-feature - artifacts: false - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions" - RUST_BACKTRACE: 1 - script: - - cd ./substrate/frame/examples/offchain-worker/ - - cargo build --locked --target=wasm32-unknown-unknown --no-default-features - - cd ../basic - - cargo build --locked --target=wasm32-unknown-unknown --no-default-features - # FIXME - allow_failure: true - -test-linux-stable-int: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions -D warnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings" - # Ensure we run the UI tests. - RUN_UI_TESTS: 1 - script: - - WASM_BUILD_NO_COLOR=1 - time cargo test -p staging-node-cli --release --locked -- --ignored - -# more information about this job can be found here: -# https://github.com/paritytech/substrate/pull/6916 -check-tracing: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - script: - # with-tracing must be explicitly activated, we run a test to ensure this works as expected in both cases - - time cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features - - time cargo test --locked --manifest-path ./substrate/primitives/tracing/Cargo.toml --no-default-features --features=with-tracing - -# Check that `westend-runtime` compiles with the `metadata-hash` feature enabled. -check-metadata-hash: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - - .pipeline-stopper-artifacts - script: - - time cargo build --locked -p westend-runtime --features metadata-hash - -# more information about this job can be found here: -# https://github.com/paritytech/substrate/pull/3778 -test-full-crypto-feature: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - variables: - # Enable debug assertions since we are running optimized builds for testing - # but still want to have debug assertions. - RUSTFLAGS: "-C debug-assertions" - RUST_BACKTRACE: 1 - script: - - cd substrate/primitives/core/ - - time cargo build --locked --no-default-features --features full_crypto - - cd ../application-crypto - - time cargo build --locked --no-default-features --features full_crypto - -cargo-check-each-crate: - stage: test - extends: - - .docker-env - - .common-refs - - .run-immediately - # - .collect-artifacts - variables: - RUSTFLAGS: "-D warnings" - # $CI_JOB_NAME is set manually so that cache could be shared for all jobs - # "cargo-check-each-crate I/N" jobs - CI_JOB_NAME: cargo-check-each-crate - timeout: 2h - script: - - PYTHONUNBUFFERED=x time .gitlab/check-each-crate.py "$CI_NODE_INDEX" "$CI_NODE_TOTAL" - parallel: 6 - cargo-check-each-crate-macos: stage: test extends: @@ -564,81 +175,3 @@ cargo-check-each-crate-macos: timeout: 2h tags: - osx - -cargo-hfuzz: - stage: test - extends: - - .docker-env - - .common-refs - # DAG - needs: - - job: check-tracing - artifacts: false - variables: - # max 10s per iteration, 60s per file - HFUZZ_RUN_ARGS: > - --exit_upon_crash - --exit_code_upon_crash 1 - --timeout 10 - --run_time 60 - # use git version of honggfuzz-rs until v0.5.56 is out, we need a few recent changes: - # https://github.com/rust-fuzz/honggfuzz-rs/pull/75 to avoid breakage on debian - # https://github.com/rust-fuzz/honggfuzz-rs/pull/81 fix to the above pr - # https://github.com/rust-fuzz/honggfuzz-rs/pull/82 fix for handling absolute CARGO_TARGET_DIR - HFUZZ_BUILD_ARGS: > - --config=patch.crates-io.honggfuzz.git="https://github.com/altaua/honggfuzz-rs" - --config=patch.crates-io.honggfuzz.rev="205f7c8c059a0d98fe1cb912cdac84f324cb6981" - artifacts: - name: "hfuzz-$CI_COMMIT_SHORT_SHA" - expire_in: 7 days - when: on_failure - paths: - - substrate/primitives/arithmetic/fuzzer/hfuzz_workspace/ - script: - - cd ./substrate/primitives/arithmetic/fuzzer - - cargo hfuzz build - - for target in $(cargo read-manifest | jq -r '.targets | .[] | .name'); do - cargo hfuzz run "$target" || { printf "fuzzing failure for %s\n" "$target"; exit 1; }; done - -.subsystem-benchmark-template: - stage: test - artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: always - expire_in: 1 hour - paths: - - charts/ - extends: - - .docker-env - - .common-refs - - .run-immediately - tags: - - benchmark - -subsystem-benchmark-availability-recovery: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-availability-recovery --bench availability-recovery-regression-bench --features subsystem-benchmarks - allow_failure: true - -subsystem-benchmark-availability-distribution: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-availability-distribution --bench availability-distribution-regression-bench --features subsystem-benchmarks - allow_failure: true - -subsystem-benchmark-approval-voting: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-node-core-approval-voting --bench approval-voting-regression-bench --features subsystem-benchmarks - allow_failure: true - -subsystem-benchmark-statement-distribution: - extends: - - .subsystem-benchmark-template - script: - - cargo bench -p polkadot-statement-distribution --bench statement-distribution-regression-bench --features subsystem-benchmarks - allow_failure: true diff --git a/.gitlab/pipeline/zombienet/bridges.yml b/.gitlab/pipeline/zombienet/bridges.yml index 9d7a8b9311934a148e855caf7c4315d8a281aed1..070bfc8472d5ab797141862bd0d0066d420bc922 100644 --- a/.gitlab/pipeline/zombienet/bridges.yml +++ b/.gitlab/pipeline/zombienet/bridges.yml @@ -52,12 +52,12 @@ zombienet-bridges-0001-asset-transfer-works: extends: - .zombienet-bridges-common script: - - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0001-asset-transfer --docker + - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-test.sh 0001-asset-transfer --docker - echo "Done" zombienet-bridges-0002-free-headers-synced-while-idle: extends: - .zombienet-bridges-common script: - - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-new-test.sh 0002-free-headers-synced-while-idle --docker + - /home/nonroot/bridges-polkadot-sdk/bridges/testing/run-test.sh 0002-free-headers-synced-while-idle --docker - echo "Done" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 120000 index 0000000000000000000000000000000000000000..63b2a0dc1abc8daf348a8112688d254407837dd9 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +docs/contributor/CODE_OF_CONDUCT.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 120000 index 0000000000000000000000000000000000000000..0f645512e8e47f744720a79767f0f78f919ea914 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +docs/contributor/CONTRIBUTING.md \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 54a01f12f35c1a39f26acd8e53884250f6cd3d02..1478a543d14c70fb7d06cd85cac0f0aa7b64b98c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,9 +157,9 @@ dependencies = [ "dunce", "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", "syn-solidity", "tiny-keccak", ] @@ -284,9 +284,9 @@ dependencies = [ "include_dir", "itertools 0.10.5", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -484,7 +484,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -494,7 +494,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -506,7 +506,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -518,8 +518,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -620,8 +620,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -724,8 +724,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", ] @@ -736,9 +736,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", "synstructure 0.13.1", ] @@ -748,8 +748,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -759,9 +759,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -790,6 +790,7 @@ name = "asset-hub-rococo-emulated-chain" version = "0.0.0" dependencies = [ "asset-hub-rococo-runtime", + "bp-bridge-hub-rococo", "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", @@ -887,6 +888,7 @@ dependencies = [ "primitive-types", "rococo-runtime-constants", "scale-info", + "serde_json", "snowbridge-router-primitives", "sp-api", "sp-block-builder", @@ -915,6 +917,7 @@ name = "asset-hub-westend-emulated-chain" version = "0.0.0" dependencies = [ "asset-hub-westend-runtime", + "bp-bridge-hub-westend", "cumulus-primitives-core", "emulated-integration-tests-common", "frame-support", @@ -1079,6 +1082,7 @@ dependencies = [ "impl-trait-for-tuples", "log", "pallet-asset-conversion", + "pallet-assets", "pallet-xcm", "parachains-common", "parity-scale-codec", @@ -1097,7 +1101,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "syn 1.0.109", ] @@ -1286,9 +1290,9 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -1299,13 +1303,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -1362,8 +1366,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -1465,15 +1469,6 @@ dependencies = [ "serde", ] -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - [[package]] name = "binary-merkle-tree" version = "13.0.0" @@ -1481,6 +1476,7 @@ dependencies = [ "array-bytes", "hash-db", "log", + "parity-scale-codec", "sp-core", "sp-runtime", "sp-tracing 16.0.0", @@ -1507,13 +1503,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.12", - "proc-macro2 1.0.82", - "quote 1.0.36", + "prettyplease", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -1622,13 +1618,13 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq 0.2.6", + "constant_time_eq 0.3.0", ] [[package]] @@ -1794,7 +1790,9 @@ dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", "bp-runtime", + "bp-xcm-bridge-hub", "frame-support", + "parity-scale-codec", "sp-api", "sp-runtime", "sp-std 14.0.0", @@ -1807,7 +1805,9 @@ dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", "bp-runtime", + "bp-xcm-bridge-hub", "frame-support", + "parity-scale-codec", "sp-api", "sp-runtime", "sp-std 14.0.0", @@ -1857,6 +1857,7 @@ dependencies = [ "scale-info", "serde", "sp-core", + "sp-io", "sp-std 14.0.0", ] @@ -1927,11 +1928,15 @@ dependencies = [ name = "bp-relayers" version = "0.7.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-parachains", "bp-runtime", "frame-support", + "frame-system", "hex", "hex-literal", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-runtime", @@ -2008,7 +2013,16 @@ dependencies = [ name = "bp-xcm-bridge-hub" version = "0.2.0" dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", "sp-std 14.0.0", + "staging-xcm", ] [[package]] @@ -2019,6 +2033,7 @@ dependencies = [ "scale-info", "sp-core", "sp-runtime", + "staging-xcm", ] [[package]] @@ -2033,6 +2048,7 @@ dependencies = [ "snowbridge-core", "sp-core", "sp-runtime", + "sp-std 14.0.0", "staging-xcm", ] @@ -2063,6 +2079,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-message-queue", "pallet-xcm", + "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", "rococo-system-emulated-network", @@ -2187,10 +2204,12 @@ dependencies = [ "asset-test-utils", "bp-header-chain", "bp-messages", + "bp-parachains", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", + "bp-xcm-bridge-hub", "bridge-runtime-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", @@ -2205,6 +2224,7 @@ dependencies = [ "pallet-bridge-relayers", "pallet-timestamp", "pallet-utility", + "pallet-xcm-bridge-hub", "parachains-common", "parachains-runtimes-test-utils", "parity-scale-codec", @@ -2248,6 +2268,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-message-queue", "pallet-xcm", + "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", "rococo-westend-system-emulated-network", @@ -2375,7 +2396,6 @@ dependencies = [ "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", - "bp-xcm-bridge-hub-router", "frame-support", "frame-system", "log", @@ -2388,22 +2408,16 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "scale-info", + "sp-core", "sp-io", "sp-runtime", "sp-std 14.0.0", "sp-trie", "staging-xcm", - "staging-xcm-builder", "static_assertions", "tuplex", ] -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - [[package]] name = "bs58" version = "0.5.1" @@ -2465,9 +2479,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2-sys" @@ -2812,9 +2826,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.0" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" +checksum = "aa3c596da3cf0983427b0df0dba359df9182c13bd5b519b585a482b0c351f4e8" dependencies = [ "clap 4.5.11", ] @@ -2827,8 +2841,8 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -2839,9 +2853,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -2909,6 +2923,7 @@ dependencies = [ "pallet-message-queue", "pallet-treasury", "pallet-utility", + "pallet-whitelist", "pallet-xcm", "parachains-common", "parity-scale-codec", @@ -2999,9 +3014,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "eyre", @@ -3026,8 +3041,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" dependencies = [ "nom", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -3285,7 +3300,7 @@ dependencies = [ [[package]] name = "coretime-rococo-emulated-chain" -version = "0.0.0" +version = "0.1.0" dependencies = [ "coretime-rococo-runtime", "cumulus-primitives-core", @@ -3298,14 +3313,17 @@ dependencies = [ [[package]] name = "coretime-rococo-integration-tests" -version = "0.1.0" +version = "0.0.0" dependencies = [ + "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", "pallet-balances", + "pallet-broker", "pallet-identity", "pallet-message-queue", "polkadot-runtime-common", + "polkadot-runtime-parachains", "rococo-runtime-constants", "rococo-system-emulated-network", "sp-runtime", @@ -3343,6 +3361,7 @@ dependencies = [ "pallet-collator-selection", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-sudo", "pallet-timestamp", @@ -3381,7 +3400,7 @@ dependencies = [ [[package]] name = "coretime-westend-emulated-chain" -version = "0.0.0" +version = "0.1.0" dependencies = [ "coretime-westend-runtime", "cumulus-primitives-core", @@ -3394,14 +3413,17 @@ dependencies = [ [[package]] name = "coretime-westend-integration-tests" -version = "0.1.0" +version = "0.0.0" dependencies = [ + "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", "pallet-balances", + "pallet-broker", "pallet-identity", "pallet-message-queue", "polkadot-runtime-common", + "polkadot-runtime-parachains", "sp-runtime", "staging-xcm", "staging-xcm-executor", @@ -3439,6 +3461,7 @@ dependencies = [ "pallet-collator-selection", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-timestamp", "pallet-transaction-payment", @@ -3609,21 +3632,6 @@ dependencies = [ "wasmtime-types", ] -[[package]] -name = "crc" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2b432c56615136f8dba245fed7ec3d5518c500a31108661067e61e72fe7e6bc" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" version = "1.3.2" @@ -4149,9 +4157,9 @@ name = "cumulus-pallet-parachain-system-proc-macro" version = "0.6.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4256,12 +4264,8 @@ dependencies = [ name = "cumulus-primitives-aura" version = "0.7.0" dependencies = [ - "parity-scale-codec", - "polkadot-core-primitives", - "polkadot-primitives", "sp-api", "sp-consensus-aura", - "sp-runtime", ] [[package]] @@ -4289,8 +4293,6 @@ dependencies = [ "scale-info", "sp-core", "sp-inherents", - "sp-runtime", - "sp-state-machine", "sp-trie", ] @@ -4343,8 +4345,6 @@ dependencies = [ "pallet-asset-conversion", "parity-scale-codec", "polkadot-runtime-common", - "polkadot-runtime-parachains", - "sp-io", "sp-runtime", "staging-xcm", "staging-xcm-builder", @@ -4445,6 +4445,7 @@ dependencies = [ "parity-scale-codec", "pin-project", "polkadot-overseer", + "portpicker", "rand", "sc-client-api", "sc-rpc-api", @@ -4692,9 +4693,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4731,10 +4732,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "scratch", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -4749,9 +4750,9 @@ version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4855,8 +4856,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -4866,9 +4867,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4877,9 +4878,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -4889,8 +4890,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustc_version 0.4.0", "syn 1.0.109", ] @@ -4985,9 +4986,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5045,12 +5046,12 @@ dependencies = [ "common-path", "derive-syn-parse", "once_cell", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", - "syn 2.0.61", + "syn 2.0.65", "termcolor", - "toml 0.8.8", + "toml 0.8.12", "walkdir", ] @@ -5094,8 +5095,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -5192,6 +5193,7 @@ version = "3.0.0" dependencies = [ "asset-test-utils", "bp-messages", + "bp-xcm-bridge-hub", "bridge-runtime-common", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", @@ -5202,6 +5204,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-message-queue", "pallet-xcm", + "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", "paste", @@ -5240,8 +5243,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -5252,9 +5255,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5272,9 +5275,9 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5283,9 +5286,9 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5485,10 +5488,10 @@ dependencies = [ "blake2 0.10.6", "file-guard", "fs-err", - "prettyplease 0.2.12", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "prettyplease", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5558,9 +5561,9 @@ dependencies = [ "expander", "indexmap 2.2.3", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -5737,21 +5740,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "fork-tree" version = "12.0.0" @@ -5890,11 +5878,11 @@ dependencies = [ "frame-support", "parity-scale-codec", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "scale-info", "sp-arithmetic", - "syn 2.0.61", + "syn 2.0.65", "trybuild", ] @@ -5998,7 +5986,7 @@ dependencies = [ "sc-cli", "sp-runtime", "sp-statement-store", - "sp-tracing 16.0.0", + "tracing-subscriber 0.3.18", ] [[package]] @@ -6030,6 +6018,7 @@ dependencies = [ "aquamarine", "array-bytes", "assert_matches", + "binary-merkle-tree", "bitflags 1.3.2", "docify", "environmental", @@ -6063,6 +6052,7 @@ dependencies = [ "sp-std 14.0.0", "sp-timestamp", "sp-tracing 16.0.0", + "sp-trie", "sp-weights", "static_assertions", "tt-call", @@ -6085,8 +6075,8 @@ dependencies = [ "parity-scale-codec", "pretty_assertions", "proc-macro-warning 1.0.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "scale-info", "sp-core", @@ -6095,7 +6085,7 @@ dependencies = [ "sp-metadata-ir", "sp-runtime", "static_assertions", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -6104,18 +6094,18 @@ version = "10.0.0" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] name = "frame-support-procedural-tools-derive" version = "11.0.0" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -6364,9 +6354,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -6776,11 +6766,56 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner 0.6.0", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot 0.12.3", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac 0.12.1", ] @@ -7157,8 +7192,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -7177,8 +7212,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", ] [[package]] @@ -7366,6 +7401,23 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jemalloc_pprof" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96368c0fc161a0a1a20b3952b6fd31ee342fffc87ed9e48ac1ed49fb25686655" +dependencies = [ + "anyhow", + "libc", + "mappings", + "once_cell", + "pprof_util", + "tempfile", + "tikv-jemalloc-ctl", + "tokio", + "tracing", +] + [[package]] name = "jni" version = "0.19.0" @@ -7423,9 +7475,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b089779ad7f80768693755a031cc14a7766aba707cbe886674e3f79e9b7e47" +checksum = "5ec465b607a36dc5dd45d48b7689bc83f679f66a3ac6b6b21cc787a11e0f8685" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -7439,9 +7491,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08163edd8bcc466c33d79e10f695cdc98c00d1e6ddfb95cec41b6b0279dd5432" +checksum = "90f0977f9c15694371b8024c35ab58ca043dbbf4b51ccb03db8858a021241df1" dependencies = [ "base64 0.22.1", "futures-util", @@ -7462,13 +7514,11 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79712302e737d23ca0daa178e752c9334846b08321d439fd89af9a384f8c830b" +checksum = "e942c55635fbf5dc421938b8558a8141c7e773720640f4f1dbe1f4164ca4e221" dependencies = [ - "anyhow", "async-trait", - "beef", "bytes", "futures-timer", "futures-util", @@ -7479,7 +7529,7 @@ dependencies = [ "parking_lot 0.12.3", "pin-project", "rand", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_json", "thiserror", @@ -7490,9 +7540,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d90064e04fb9d7282b1c71044ea94d0bbc6eff5621c66f1a0bce9e9de7cf3ac" +checksum = "e33774602df12b68a2310b38a535733c477ca4a498751739f89fe8dbbb62ec4c" dependencies = [ "async-trait", "base64 0.22.1", @@ -7515,24 +7565,23 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" +checksum = "6b07a2daf52077ab1b197aea69a5c990c060143835bf04c77070e98903791715" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] name = "jsonrpsee-server" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654afab2e92e5d88ebd8a39d6074483f3f2bfdf91c5ac57fe285e7127cdd4f51" +checksum = "038fb697a709bec7134e9ccbdbecfea0e2d15183f7140254afef7c5610a3f488" dependencies = [ - "anyhow", "futures-util", "http 1.1.0", "http-body 1.0.0", @@ -7556,11 +7605,10 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c465fbe385238e861fdc4d1c85e04ada6c1fd246161d26385c1b311724d2af" +checksum = "23b67d6e008164f027afbc2e7bb79662650158d26df200040282d2aa1cbb093b" dependencies = [ - "beef", "http 1.1.0", "serde", "serde_json", @@ -7569,9 +7617,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.23.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c28759775f5cb2f1ea9667672d3fe2b0e701d1f4b7b67954e60afe7fd058b5e" +checksum = "992bf67d1132f88edf4a4f8cff474cf01abb2be203004a2b8e11c2b20795b99e" dependencies = [ "http 1.1.0", "jsonrpsee-client-transport", @@ -7701,9 +7749,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -7719,9 +7767,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libflate" @@ -7910,11 +7958,11 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" dependencies = [ - "bs58 0.5.1", + "bs58", "ed25519-dalek", "hkdf", "multihash 0.19.1", @@ -8051,7 +8099,7 @@ dependencies = [ "libp2p-tls", "log", "parking_lot 0.12.3", - "quinn 0.10.2", + "quinn", "rand", "ring 0.16.20", "rustls 0.21.7", @@ -8109,9 +8157,9 @@ checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74" dependencies = [ "heck 0.4.1", "proc-macro-warning 0.4.2", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8372,21 +8420,22 @@ dependencies = [ [[package]] name = "litep2p" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f46c51c205264b834ceed95c8b195026e700494bc3991aaba3b4ea9e20626d9" +checksum = "d4ab2528b02b6dbbc3e6ec4b55ccde885647c622a315b7da45081ed2dfe4b813" dependencies = [ "async-trait", - "bs58 0.4.0", + "bs58", "bytes", "cid 0.10.1", "ed25519-dalek", "futures", "futures-timer", "hex-literal", + "hickory-resolver", "indexmap 2.2.3", "libc", - "mockall 0.12.1", + "mockall 0.13.0", "multiaddr 0.17.1", "multihash 0.17.0", "network-interface", @@ -8394,8 +8443,7 @@ dependencies = [ "parking_lot 0.12.3", "pin-project", "prost 0.12.6", - "prost-build 0.11.9", - "quinn 0.9.4", + "prost-build 0.13.2", "rand", "rcgen", "ring 0.16.20", @@ -8407,18 +8455,15 @@ dependencies = [ "snow", "socket2 0.5.7", "static_assertions", - "str0m", "thiserror", "tokio", "tokio-stream", "tokio-tungstenite", "tokio-util", "tracing", - "trust-dns-resolver", "uint", "unsigned-varint 0.8.0", "url", - "webpki", "x25519-dalek", "x509-parser 0.16.0", "yasna", @@ -8524,8 +8569,8 @@ checksum = "cc33f9f0351468d26fbc53d9ce00a096c8522ecb42f19b50f34f2c422f76d21d" dependencies = [ "macro_magic_core", "macro_magic_macros", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8537,9 +8582,9 @@ dependencies = [ "const-random", "derive-syn-parse", "macro_magic_core_macros", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8548,9 +8593,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8560,8 +8605,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", - "quote 1.0.36", - "syn 2.0.61", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -8570,6 +8615,19 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "mappings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fa2605f461115ef6336342b12f0d8cabdfd7b258fed86f5f98c725535843601" +dependencies = [ + "anyhow", + "libc", + "once_cell", + "pprof_util", + "tracing", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -8736,19 +8794,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "minimal-template" -version = "0.0.0" -dependencies = [ - "docify", - "minimal-template-node", - "minimal-template-runtime", - "pallet-minimal-template", - "polkadot-sdk-docs", - "polkadot-sdk-frame", - "simple-mermaid 0.1.1", -] - [[package]] name = "minimal-template-node" version = "0.0.0" @@ -8759,48 +8804,18 @@ dependencies = [ "futures-timer", "jsonrpsee", "minimal-template-runtime", - "polkadot-sdk-frame", - "sc-basic-authorship", - "sc-cli", - "sc-client-api", - "sc-consensus", - "sc-consensus-manual-seal", - "sc-executor", - "sc-network", - "sc-offchain", - "sc-rpc-api", - "sc-service", - "sc-telemetry", - "sc-transaction-pool", - "sc-transaction-pool-api", + "polkadot-sdk", "serde_json", - "sp-api", - "sp-block-builder", - "sp-blockchain", - "sp-io", - "sp-keyring", - "sp-runtime", - "sp-timestamp", - "substrate-build-script-utils", - "substrate-frame-rpc-system", ] [[package]] name = "minimal-template-runtime" version = "0.0.0" dependencies = [ - "pallet-balances", "pallet-minimal-template", - "pallet-sudo", - "pallet-timestamp", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", - "polkadot-sdk-frame", + "polkadot-sdk", "scale-info", - "sp-genesis-builder", - "sp-runtime", - "substrate-wasm-builder", ] [[package]] @@ -8903,15 +8918,14 @@ dependencies = [ [[package]] name = "mockall" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" dependencies = [ "cfg-if", "downcast", "fragile", - "lazy_static", - "mockall_derive 0.12.1", + "mockall_derive 0.13.0", "predicates 3.0.3", "predicates-tree", ] @@ -8923,21 +8937,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "mockall_derive" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" dependencies = [ "cfg-if", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -9047,8 +9061,8 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", ] @@ -9095,8 +9109,8 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -9303,7 +9317,6 @@ dependencies = [ "sc-consensus-grandpa-rpc", "sc-mixnet", "sc-rpc", - "sc-rpc-api", "sc-sync-state-rpc", "sc-transaction-pool-api", "sp-api", @@ -9487,9 +9500,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -9591,6 +9604,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.6.1" @@ -9634,46 +9656,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" +name = "openssl-probe" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "openssl-src" -version = "300.2.3+3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.102" @@ -9682,7 +9669,6 @@ checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] @@ -9721,8 +9707,8 @@ dependencies = [ "itertools 0.11.0", "petgraph", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -9882,7 +9868,6 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", - "sp-std 14.0.0", ] [[package]] @@ -10089,6 +10074,7 @@ version = "28.0.0" dependencies = [ "array-bytes", "binary-merkle-tree", + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -10217,18 +10203,27 @@ dependencies = [ name = "pallet-bridge-relayers" version = "0.7.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-parachains", + "bp-polkadot-core", "bp-relayers", "bp-runtime", + "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", "log", "pallet-balances", + "pallet-bridge-grandpa", "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-arithmetic", + "sp-core", "sp-io", "sp-runtime", "sp-std 14.0.0", @@ -10300,10 +10295,12 @@ dependencies = [ name = "pallet-collective" version = "28.0.0" dependencies = [ + "docify", "frame-benchmarking", "frame-support", "frame-system", "log", + "pallet-balances", "parity-scale-codec", "scale-info", "sp-core", @@ -10377,10 +10374,9 @@ dependencies = [ "anyhow", "frame-system", "parity-wasm", - "polkavm-linker", "sp-runtime", "tempfile", - "toml 0.8.8", + "toml 0.8.12", "twox-hash", ] @@ -10425,9 +10421,9 @@ dependencies = [ name = "pallet-contracts-proc-macro" version = "18.0.0" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -10437,7 +10433,6 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive", "scale-info", ] @@ -10496,6 +10491,7 @@ dependencies = [ "frame-election-provider-support", "frame-support", "frame-system", + "log", "pallet-balances", "pallet-nomination-pools", "pallet-staking", @@ -10982,7 +10978,7 @@ name = "pallet-minimal-template" version = "0.0.0" dependencies = [ "parity-scale-codec", - "polkadot-sdk-frame", + "polkadot-sdk", "scale-info", ] @@ -11431,6 +11427,119 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-revive" +version = "0.1.0" +dependencies = [ + "array-bytes", + "assert_matches", + "bitflags 1.3.2", + "environmental", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-assets", + "pallet-balances", + "pallet-message-queue", + "pallet-proxy", + "pallet-revive-fixtures", + "pallet-revive-proc-macro", + "pallet-revive-uapi", + "pallet-timestamp", + "pallet-utility", + "parity-scale-codec", + "paste", + "polkavm 0.10.0", + "pretty_assertions", + "rlp", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-std 14.0.0", + "sp-tracing 16.0.0", + "staging-xcm", + "staging-xcm-builder", + "wat", +] + +[[package]] +name = "pallet-revive-fixtures" +version = "0.1.0" +dependencies = [ + "anyhow", + "frame-system", + "log", + "parity-wasm", + "polkavm-linker 0.10.0", + "sp-core", + "sp-io", + "sp-runtime", + "tempfile", + "toml 0.8.12", +] + +[[package]] +name = "pallet-revive-mock-network" +version = "0.1.0" +dependencies = [ + "assert_matches", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "pallet-message-queue", + "pallet-proxy", + "pallet-revive", + "pallet-revive-fixtures", + "pallet-revive-proc-macro", + "pallet-revive-uapi", + "pallet-timestamp", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "pretty_assertions", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-tracing 16.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "xcm-simulator", +] + +[[package]] +name = "pallet-revive-proc-macro" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", +] + +[[package]] +name = "pallet-revive-uapi" +version = "0.1.0" +dependencies = [ + "bitflags 1.3.2", + "parity-scale-codec", + "paste", + "polkavm-derive 0.10.0", + "scale-info", +] + [[package]] name = "pallet-root-offences" version = "25.0.0" @@ -11659,10 +11768,10 @@ name = "pallet-staking-reward-curve" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "sp-runtime", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -12013,7 +12122,6 @@ dependencies = [ "bp-messages", "bp-runtime", "bp-xcm-bridge-hub", - "bridge-runtime-common", "frame-support", "frame-system", "log", @@ -12021,6 +12129,7 @@ dependencies = [ "pallet-bridge-messages", "pallet-xcm-bridge-hub-router", "parity-scale-codec", + "polkadot-parachain-primitives", "scale-info", "sp-core", "sp-io", @@ -12150,6 +12259,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", + "serde_json", "smallvec", "sp-api", "sp-block-builder", @@ -12304,8 +12414,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -12333,7 +12443,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", "syn 1.0.109", "synstructure 0.12.6", ] @@ -12480,6 +12590,7 @@ dependencies = [ "frame-try-runtime", "hex-literal", "log", + "pallet-asset-conversion", "pallet-asset-tx-payment", "pallet-assets", "pallet-aura", @@ -12498,6 +12609,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", + "primitive-types", "scale-info", "smallvec", "sp-api", @@ -12583,6 +12695,7 @@ dependencies = [ "pallet-identity", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-timestamp", "pallet-transaction-payment", @@ -12641,6 +12754,7 @@ dependencies = [ "pallet-balances", "pallet-identity", "pallet-message-queue", + "pallet-xcm", "parachains-common", "parity-scale-codec", "polkadot-runtime-common", @@ -12681,6 +12795,7 @@ dependencies = [ "pallet-identity", "pallet-message-queue", "pallet-multisig", + "pallet-proxy", "pallet-session", "pallet-timestamp", "pallet-transaction-payment", @@ -12750,9 +12865,9 @@ checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -12791,9 +12906,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -12901,7 +13016,9 @@ dependencies = [ "rand", "rand_chacha", "rand_core", + "sc-keystore", "schnorrkel 0.11.4", + "sp-application-crypto", "sp-authority-discovery", "sp-core", "sp-tracing 16.0.0", @@ -13057,6 +13174,7 @@ dependencies = [ "rstest", "sc-keystore", "sc-network", + "schnellru", "sp-core", "sp-keyring", "sp-keystore", @@ -13443,23 +13561,17 @@ name = "polkadot-node-core-prospective-parachains" version = "6.0.0" dependencies = [ "assert_matches", - "bitvec", "fatality", "futures", - "parity-scale-codec", - "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", - "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rand", "rstest", - "sc-keystore", - "sp-application-crypto", "sp-core", - "sp-keyring", - "sp-keystore", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] @@ -13589,8 +13701,10 @@ dependencies = [ "nix 0.28.0", "parity-scale-codec", "polkadot-node-core-pvf-common", + "polkadot-node-primitives", "polkadot-parachain-primitives", "polkadot-primitives", + "sp-maybe-compressed-blob", "tracing-gum", ] @@ -13605,6 +13719,7 @@ dependencies = [ "nix 0.28.0", "parity-scale-codec", "polkadot-node-core-pvf-common", + "polkadot-node-primitives", "polkadot-primitives", "rayon", "rococo-runtime", @@ -13661,7 +13776,7 @@ name = "polkadot-node-metrics" version = "7.0.0" dependencies = [ "assert_cmd", - "bs58 0.5.1", + "bs58", "futures", "futures-timer", "http-body-util", @@ -13717,14 +13832,17 @@ dependencies = [ "bitvec", "bounded-vec", "futures", + "futures-timer", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-parachain-primitives", "polkadot-primitives", + "sc-keystore", "schnorrkel 0.11.4", "serde", "sp-application-crypto", "sp-consensus-babe", + "sp-consensus-slots", "sp-core", "sp-keystore", "sp-maybe-compressed-blob", @@ -13865,19 +13983,48 @@ dependencies = [ name = "polkadot-parachain-bin" version = "4.0.0" dependencies = [ - "assert_cmd", "asset-hub-rococo-runtime", "asset-hub-westend-runtime", - "async-trait", "bridge-hub-rococo-runtime", "bridge-hub-westend-runtime", - "clap 4.5.11", "collectives-westend-runtime", "color-eyre", - "color-print", "contracts-rococo-runtime", "coretime-rococo-runtime", "coretime-westend-runtime", + "cumulus-primitives-core", + "glutton-westend-runtime", + "hex-literal", + "log", + "parachains-common", + "penpal-runtime", + "people-rococo-runtime", + "people-westend-runtime", + "polkadot-parachain-lib", + "polkadot-service", + "rococo-parachain-runtime", + "sc-chain-spec", + "sc-cli", + "sc-service", + "seedling-runtime", + "serde", + "serde_json", + "shell-runtime", + "sp-core", + "sp-runtime", + "staging-xcm", + "substrate-build-script-utils", + "testnet-parachains-constants", +] + +[[package]] +name = "polkadot-parachain-lib" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "async-trait", + "clap 4.5.11", + "color-print", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", @@ -13896,8 +14043,6 @@ dependencies = [ "frame-system-rpc-runtime-api", "frame-try-runtime", "futures", - "glutton-westend-runtime", - "hex-literal", "jsonrpsee", "log", "nix 0.28.0", @@ -13906,56 +14051,40 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parachains-common", "parity-scale-codec", - "penpal-runtime", - "people-rococo-runtime", - "people-westend-runtime", "polkadot-cli", "polkadot-primitives", - "polkadot-service", - "rococo-parachain-runtime", "sc-basic-authorship", "sc-chain-spec", "sc-cli", "sc-client-api", + "sc-client-db", "sc-consensus", "sc-executor", "sc-network", - "sc-network-sync", "sc-rpc", "sc-service", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", - "sc-transaction-pool-api", - "seedling-runtime", "serde", "serde_json", - "shell-runtime", "sp-api", "sp-block-builder", - "sp-blockchain", "sp-consensus-aura", "sp-core", "sp-genesis-builder", "sp-inherents", - "sp-io", "sp-keystore", - "sp-offchain", "sp-runtime", "sp-session", - "sp-std 14.0.0", "sp-timestamp", - "sp-tracing 16.0.0", "sp-transaction-pool", "sp-version", - "staging-xcm", - "substrate-build-script-utils", + "sp-weights", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-state-trie-migration-rpc", - "tempfile", - "testnet-parachains-constants", "tokio", "wait-timeout", ] @@ -13985,6 +14114,7 @@ dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain-primitives", + "polkadot-primitives-test-helpers", "scale-info", "serde", "sp-api", @@ -13998,6 +14128,7 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-staking", + "sp-std 14.0.0", ] [[package]] @@ -14105,7 +14236,7 @@ dependencies = [ name = "polkadot-runtime-metrics" version = "7.0.0" dependencies = [ - "bs58 0.5.1", + "bs58", "frame-benchmarking", "parity-scale-codec", "polkadot-primitives", @@ -14315,6 +14446,11 @@ dependencies = [ "pallet-recovery", "pallet-referenda", "pallet-remark", + "pallet-revive", + "pallet-revive-fixtures", + "pallet-revive-mock-network", + "pallet-revive-proc-macro", + "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", @@ -14387,6 +14523,7 @@ dependencies = [ "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", + "polkadot-parachain-lib", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-rpc", @@ -14573,6 +14710,7 @@ dependencies = [ "pallet-balances", "pallet-broker", "pallet-collective", + "pallet-contracts", "pallet-default-config-example", "pallet-democracy", "pallet-example-offchain-worker", @@ -14856,6 +14994,7 @@ dependencies = [ "futures-timer", "hex", "itertools 0.11.0", + "jemalloc_pprof", "kvdb-memorydb", "log", "orchestra", @@ -14879,6 +15018,7 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "polkadot-primitives-test-helpers", + "polkadot-service", "polkadot-statement-distribution", "prometheus", "pyroscope", @@ -14907,6 +15047,7 @@ dependencies = [ "sp-tracing 16.0.0", "strum 0.26.2", "substrate-prometheus-endpoint", + "tikv-jemallocator", "tokio", "tracing-gum", ] @@ -15102,9 +15243,22 @@ checksum = "8a3693e5efdb2bf74e449cd25fd777a28bd7ed87e41f5d5da75eb31b4de48b94" dependencies = [ "libc", "log", - "polkavm-assembler", - "polkavm-common", - "polkavm-linux-raw", + "polkavm-assembler 0.9.0", + "polkavm-common 0.9.0", + "polkavm-linux-raw 0.9.0", +] + +[[package]] +name = "polkavm" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec0c5935f2eff23cfc4653002f4f8d12b37f87a720e0631282d188c32089d6" +dependencies = [ + "libc", + "log", + "polkavm-assembler 0.10.0", + "polkavm-common 0.10.0", + "polkavm-linux-raw 0.10.0", ] [[package]] @@ -15116,6 +15270,15 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-assembler" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e4fd5a43100bf1afe9727b8130d01f966f5cfc9144d5604b21e795c2bcd80e" +dependencies = [ + "log", +] + [[package]] name = "polkavm-common" version = "0.9.0" @@ -15125,13 +15288,32 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-common" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0097b48bc0bedf9f3f537ce8f37e8f1202d8d83f9b621bdb21ff2c59b9097c50" +dependencies = [ + "log", + "polkavm-assembler 0.10.0", +] + [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro", + "polkavm-derive-impl-macro 0.9.0", +] + +[[package]] +name = "polkavm-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dcc701385c08c31bdb0569f0c51a290c580d892fa77f1dd88a7352a62679ecf" +dependencies = [ + "polkavm-derive-impl-macro 0.10.0", ] [[package]] @@ -15140,10 +15322,22 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "polkavm-common 0.9.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7855353a5a783dd5d09e3b915474bddf66575f5a3cf45dec8d1c5e051ba320dc" +dependencies = [ + "polkavm-common 0.10.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15152,8 +15346,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl", - "syn 2.0.61", + "polkavm-derive-impl 0.9.0", + "syn 2.0.65", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9324fe036de37c17829af233b46ef6b5562d4a0c09bb7fdb9f8378856dee30cf" +dependencies = [ + "polkavm-derive-impl 0.10.0", + "syn 2.0.65", ] [[package]] @@ -15166,7 +15370,22 @@ dependencies = [ "hashbrown 0.14.3", "log", "object 0.32.2", - "polkavm-common", + "polkavm-common 0.9.0", + "regalloc2 0.9.3", + "rustc-demangle", +] + +[[package]] +name = "polkavm-linker" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d704edfe7bdcc876784f19436d53d515b65eb07bc9a0fae77085d552c2dbbb5" +dependencies = [ + "gimli 0.28.0", + "hashbrown 0.14.3", + "log", + "object 0.36.1", + "polkavm-common 0.10.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -15177,6 +15396,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120" +[[package]] +name = "polkavm-linux-raw" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26e45fa59c7e1bb12ef5289080601e9ec9b31435f6e32800a5c90c132453d126" + [[package]] name = "polling" version = "2.8.0" @@ -15271,6 +15496,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "pprof_util" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c620a1858d6ebf10d7c60256629078b2d106968d0e6ff63b850d9ecd84008fbe" +dependencies = [ + "anyhow", + "flate2", + "num", + "paste", + "prost 0.11.9", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -15329,24 +15567,14 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2 1.0.82", - "syn 1.0.109", -] - [[package]] name = "prettyplease" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ - "proc-macro2 1.0.82", - "syn 2.0.61", + "proc-macro2 1.0.86", + "syn 2.0.65", ] [[package]] @@ -15406,8 +15634,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", "version_check", ] @@ -15418,8 +15646,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "version_check", ] @@ -15435,9 +15663,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15446,9 +15674,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15462,9 +15690,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -15527,9 +15755,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15584,33 +15812,42 @@ dependencies = [ "prost-derive 0.12.6", ] +[[package]] +name = "prost" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +dependencies = [ + "bytes", + "prost-derive 0.13.2", +] + [[package]] name = "prost-build" -version = "0.11.9" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "lazy_static", + "heck 0.5.0", + "itertools 0.11.0", "log", "multimap", + "once_cell", "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", + "prettyplease", + "prost 0.12.6", + "prost-types 0.12.4", "regex", - "syn 1.0.109", + "syn 2.0.65", "tempfile", - "which", ] [[package]] name = "prost-build" -version = "0.12.4" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", "heck 0.5.0", @@ -15619,11 +15856,11 @@ dependencies = [ "multimap", "once_cell", "petgraph", - "prettyplease 0.2.12", - "prost 0.12.6", - "prost-types 0.12.4", + "prettyplease", + "prost 0.13.2", + "prost-types 0.13.2", "regex", - "syn 2.0.61", + "syn 2.0.65", "tempfile", ] @@ -15635,8 +15872,8 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -15648,18 +15885,22 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] -name = "prost-types" -version = "0.11.9" +name = "prost-derive" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ - "prost 0.11.9", + "anyhow", + "itertools 0.11.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -15671,6 +15912,15 @@ dependencies = [ "prost 0.12.6", ] +[[package]] +name = "prost-types" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +dependencies = [ + "prost 0.13.2", +] + [[package]] name = "psm" version = "0.1.21" @@ -15776,24 +16026,6 @@ dependencies = [ "rand", ] -[[package]] -name = "quinn" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto 0.9.6", - "quinn-udp 0.3.2", - "rustc-hash", - "rustls 0.20.9", - "thiserror", - "tokio", - "tracing", - "webpki", -] - [[package]] name = "quinn" version = "0.10.2" @@ -15803,33 +16035,15 @@ dependencies = [ "bytes", "futures-io", "pin-project-lite", - "quinn-proto 0.10.6", - "quinn-udp 0.4.1", - "rustc-hash", + "quinn-proto", + "quinn-udp", + "rustc-hash 1.1.0", "rustls 0.21.7", "thiserror", "tokio", "tracing", ] -[[package]] -name = "quinn-proto" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" -dependencies = [ - "bytes", - "rand", - "ring 0.16.20", - "rustc-hash", - "rustls 0.20.9", - "slab", - "thiserror", - "tinyvec", - "tracing", - "webpki", -] - [[package]] name = "quinn-proto" version = "0.10.6" @@ -15839,7 +16053,7 @@ dependencies = [ "bytes", "rand", "ring 0.16.20", - "rustc-hash", + "rustc-hash 1.1.0", "rustls 0.21.7", "slab", "thiserror", @@ -15847,19 +16061,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "quinn-udp" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" -dependencies = [ - "libc", - "quinn-proto 0.9.6", - "socket2 0.4.9", - "tracing", - "windows-sys 0.42.0", -] - [[package]] name = "quinn-udp" version = "0.4.1" @@ -15884,11 +16085,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2 1.0.86", ] [[package]] @@ -16087,9 +16288,9 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -16112,7 +16313,7 @@ checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" dependencies = [ "hashbrown 0.13.2", "log", - "rustc-hash", + "rustc-hash 1.1.0", "slice-group-by", "smallvec", ] @@ -16637,12 +16838,12 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.61", + "syn 2.0.65", "unicode-ident", ] @@ -16673,9 +16874,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -16697,9 +16898,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" @@ -16713,6 +16914,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -16934,9 +17141,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -17125,9 +17332,9 @@ name = "sc-chain-spec-derive" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -17647,7 +17854,7 @@ dependencies = [ name = "sc-executor-common" version = "0.29.0" dependencies = [ - "polkavm", + "polkavm 0.9.3", "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface 20.0.0", @@ -17660,7 +17867,7 @@ name = "sc-executor-polkavm" version = "0.29.0" dependencies = [ "log", - "polkavm", + "polkavm 0.9.3", "sc-executor-common", "sp-wasm-interface 20.0.0", ] @@ -17985,7 +18192,7 @@ dependencies = [ name = "sc-network-types" version = "0.10.0" dependencies = [ - "bs58 0.5.1", + "bs58", "ed25519-dalek", "libp2p-identity", "litep2p", @@ -18083,11 +18290,9 @@ dependencies = [ "sp-runtime", "sp-session", "sp-statement-store", - "sp-tracing 16.0.0", "sp-version", "substrate-test-runtime-client", "tokio", - "tracing-subscriber 0.3.18", ] [[package]] @@ -18113,6 +18318,7 @@ dependencies = [ name = "sc-rpc-server" version = "11.0.0" dependencies = [ + "dyn-clone", "forwarded-header-value", "futures", "governor", @@ -18122,6 +18328,7 @@ dependencies = [ "ip_network", "jsonrpsee", "log", + "sc-rpc-api", "serde", "serde_json", "substrate-prometheus-endpoint", @@ -18399,7 +18606,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.3", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "sc-client-api", "sc-tracing-proc-macro", "serde", @@ -18420,9 +18627,9 @@ name = "sc-tracing-proc-macro" version = "11.0.0" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -18534,8 +18741,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -18572,17 +18779,17 @@ version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde_derive_internals", "syn 1.0.109", ] [[package]] name = "schnellru" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" dependencies = [ "ahash 0.8.11", "cfg-if", @@ -18652,21 +18859,6 @@ dependencies = [ "untrusted 0.7.1", ] -[[package]] -name = "sctp-proto" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6220f78bb44c15f326b0596113305f6101097a18755d53727a575c97e09fb24" -dependencies = [ - "bytes", - "crc", - "fxhash", - "log", - "rand", - "slab", - "thiserror", -] - [[package]] name = "sec1" version = "0.7.3" @@ -18841,9 +19033,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -18868,13 +19060,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -18883,8 +19075,8 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -18899,9 +19091,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap 2.2.3", "itoa", @@ -18912,9 +19104,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -18974,9 +19166,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -18992,18 +19184,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", - "sha1-asm", -] - [[package]] name = "sha1" version = "0.10.6" @@ -19015,15 +19195,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha1-asm" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba6947745e7f86be3b8af00b7355857085dbdf8901393c89514510eb61f4e21" -dependencies = [ - "cc", -] - [[package]] name = "sha2" version = "0.9.9" @@ -19153,9 +19324,9 @@ dependencies = [ [[package]] name = "simple-dns" -version = "0.5.7" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae9a3fcdadafb6d97f4c0e007e4247b114ee0f119f650c3cbf3a8b3a1479694" +checksum = "4c80e565e7dcc4f1ef247e2f395550d4cf7d777746d5988e7e4e3156b71077fc" dependencies = [ "bitflags 2.6.0", ] @@ -19266,7 +19437,7 @@ dependencies = [ "base64 0.21.2", "bip39", "blake2-rfc", - "bs58 0.5.1", + "bs58", "chacha20", "crossbeam-queue", "derive_more", @@ -19420,6 +19591,7 @@ dependencies = [ "sp-std 14.0.0", "staging-xcm", "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] @@ -19719,7 +19891,7 @@ dependencies = [ "httparse", "log", "rand", - "sha-1 0.9.8", + "sha-1", ] [[package]] @@ -19759,7 +19931,6 @@ dependencies = [ "sc-executor", "sc-network", "sc-offchain", - "sc-rpc-api", "sc-service", "sc-telemetry", "sc-transaction-pool", @@ -19850,9 +20021,9 @@ dependencies = [ "blake2 0.10.6", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20048,6 +20219,7 @@ dependencies = [ "sp-keystore", "sp-mmr-primitives", "sp-runtime", + "sp-weights", "strum 0.26.2", "w3f-bls", ] @@ -20111,7 +20283,7 @@ dependencies = [ "bitflags 1.3.2", "blake2 0.10.6", "bounded-collections", - "bs58 0.5.1", + "bs58", "criterion", "dyn-clonable", "ed25519-zebra", @@ -20234,9 +20406,9 @@ dependencies = [ name = "sp-crypto-hashing-proc-macro" version = "0.1.0" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "sp-crypto-hashing", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -20252,18 +20424,18 @@ name = "sp-debug-derive" version = "8.0.0" source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] name = "sp-debug-derive" version = "14.0.0" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20320,7 +20492,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "rustversion", "secp256k1", "sp-core", @@ -20447,7 +20619,7 @@ dependencies = [ name = "sp-rpc" version = "26.0.0" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "sp-core", @@ -20478,6 +20650,7 @@ dependencies = [ "sp-state-machine", "sp-std 14.0.0", "sp-tracing 16.0.0", + "sp-trie", "sp-weights", "substrate-test-runtime-client", "tracing", @@ -20509,7 +20682,7 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "primitive-types", "rustversion", "sp-core", @@ -20533,9 +20706,9 @@ source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf5 dependencies = [ "Inflector", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20545,9 +20718,9 @@ dependencies = [ "Inflector", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -20806,10 +20979,11 @@ name = "sp-version-proc-macro" version = "13.0.0" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro-warning 1.0.0", + "proc-macro2 1.0.86", + "quote 1.0.37", "sp-version", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -20891,8 +21065,8 @@ checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde", "serde_json", "unicode-xid 0.2.4", @@ -20916,8 +21090,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -20934,8 +21108,10 @@ dependencies = [ "clap 4.5.11", "log", "sc-chain-spec", + "serde", "serde_json", "sp-tracing 16.0.0", + "substrate-test-runtime", ] [[package]] @@ -21109,31 +21285,11 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] -[[package]] -name = "str0m" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6706347e49b13373f7ddfafad47df7583ed52083d6fc8a594eb2c80497ef959d" -dependencies = [ - "combine", - "crc", - "fastrand 2.1.0", - "hmac 0.12.1", - "once_cell", - "openssl", - "openssl-sys", - "sctp-proto", - "serde", - "sha-1 0.10.1", - "thiserror", - "tracing", -] - [[package]] name = "string-interner" version = "0.17.0" @@ -21182,8 +21338,8 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -21218,8 +21374,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", "syn 1.0.109", ] @@ -21231,10 +21387,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -21244,10 +21400,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 2.0.61", + "syn 2.0.65", ] [[package]] @@ -21379,6 +21535,7 @@ dependencies = [ "rbtag", "relay-substrate-client", "relay-utils", + "rustc-hex", "scale-info", "sp-consensus-grandpa", "sp-core", @@ -21554,7 +21711,7 @@ dependencies = [ "merkleized-metadata", "parity-scale-codec", "parity-wasm", - "polkavm-linker", + "polkavm-linker 0.9.2", "sc-executor", "sp-core", "sp-io", @@ -21563,7 +21720,7 @@ dependencies = [ "sp-version", "strum 0.26.2", "tempfile", - "toml 0.8.8", + "toml 0.8.12", "walkdir", "wasm-opt", ] @@ -21694,19 +21851,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.61" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] @@ -21717,9 +21874,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" dependencies = [ "paste", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -21728,8 +21885,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -21740,9 +21897,9 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -21859,9 +22016,9 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22023,8 +22180,8 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -22034,9 +22191,9 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22200,9 +22357,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22302,14 +22459,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.12", ] [[package]] @@ -22329,7 +22486,7 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.2.3", "toml_datetime", - "winnow", + "winnow 0.5.15", ] [[package]] @@ -22337,12 +22494,23 @@ name = "toml_edit" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.2.3", + "toml_datetime", + "winnow 0.5.15", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -22407,9 +22575,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22449,9 +22617,9 @@ dependencies = [ "assert_matches", "expander", "proc-macro-crate 3.1.0", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -22547,9 +22715,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ed83be775d85ebb0e272914fff6462c39b3ddd6dc67b5c1c41271aad280c69" +checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f" dependencies = [ "hash-db", "log", @@ -23000,11 +23168,12 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "serde", "serde_json", "wasm-bindgen-macro", @@ -23012,16 +23181,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", "wasm-bindgen-shared", ] @@ -23039,32 +23208,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.36", + "quote 1.0.37", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-bindgen-test" @@ -23086,8 +23255,8 @@ version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", ] [[package]] @@ -23664,17 +23833,6 @@ dependencies = [ "westend-emulated-chain", ] -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "wide" version = "0.7.11" @@ -23769,21 +23927,6 @@ dependencies = [ "windows-targets 0.52.0", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -23991,6 +24134,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -24152,10 +24304,10 @@ name = "xcm-procedural" version = "7.0.0" dependencies = [ "Inflector", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2 1.0.86", + "quote 1.0.37", "staging-xcm", - "syn 2.0.61", + "syn 2.0.65", "trybuild", ] @@ -24318,9 +24470,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] @@ -24338,9 +24490,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.61", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.65", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7ae7c3bd1811b651339569f9fe794b611fd12cc2..7e48fa14ccc227e92f9545c489d9932d91c62098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,6 +136,7 @@ members = [ "cumulus/parachains/runtimes/testing/penpal", "cumulus/parachains/runtimes/testing/rococo-parachain", "cumulus/polkadot-parachain", + "cumulus/polkadot-parachain/polkadot-parachain-lib", "cumulus/primitives/aura", "cumulus/primitives/core", "cumulus/primitives/parachain-inherent", @@ -394,6 +395,11 @@ members = [ "substrate/frame/recovery", "substrate/frame/referenda", "substrate/frame/remark", + "substrate/frame/revive", + "substrate/frame/revive/fixtures", + "substrate/frame/revive/mock-network", + "substrate/frame/revive/proc-macro", + "substrate/frame/revive/uapi", "substrate/frame/root-offences", "substrate/frame/root-testing", "substrate/frame/safe-mode", @@ -522,7 +528,6 @@ members = [ "substrate/utils/prometheus", "substrate/utils/substrate-bip39", "substrate/utils/wasm-builder", - "templates/minimal", "templates/minimal/node", "templates/minimal/pallets/template", "templates/minimal/runtime", @@ -536,6 +541,7 @@ members = [ ] default-members = [ + "cumulus/polkadot-parachain", "polkadot", "substrate/bin/node/cli", ] @@ -578,7 +584,7 @@ ahash = { version = "0.8.2" } alloy-primitives = { version = "0.4.2", default-features = false } alloy-sol-types = { version = "0.4.2", default-features = false } always-assert = { version = "0.1" } -anyhow = { version = "1.0.81" } +anyhow = { version = "1.0.81", default-features = false } aquamarine = { version = "0.5.0" } arbitrary = { version = "1.3.2" } ark-bls12-377 = { version = "0.4.0", default-features = false } @@ -615,7 +621,7 @@ bip39 = { version = "2.0.0" } bitflags = { version = "1.3.2" } bitvec = { version = "1.0.1", default-features = false } blake2 = { version = "0.10.4", default-features = false } -blake2b_simd = { version = "1.0.1", default-features = false } +blake2b_simd = { version = "1.0.2", default-features = false } blake3 = { version = "1.5" } bounded-collections = { version = "0.2.0", default-features = false } bounded-vec = { version = "0.7" } @@ -662,12 +668,12 @@ chrono = { version = "0.4.31" } cid = { version = "0.9.0" } clap = { version = "4.5.10" } clap-num = { version = "1.0.2" } -clap_complete = { version = "4.0.2" } +clap_complete = { version = "4.5.13" } coarsetime = { version = "0.1.22" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } collectives-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend" } collectives-westend-runtime = { path = "cumulus/parachains/runtimes/collectives/collectives-westend" } -color-eyre = { version = "0.6.1", default-features = false } +color-eyre = { version = "0.6.3", default-features = false } color-print = { version = "0.3.4" } colored = { version = "2.0.4" } comfy-table = { version = "7.1.0", default-features = false } @@ -804,10 +810,11 @@ is-terminal = { version = "0.4.9" } is_executable = { version = "1.0.1" } isahc = { version = "1.2" } itertools = { version = "0.11" } +jemalloc_pprof = { version = "0.4" } jobserver = { version = "0.1.26" } jsonpath_lib = { version = "0.3" } -jsonrpsee = { version = "0.23.2" } -jsonrpsee-core = { version = "0.23.2" } +jsonrpsee = { version = "0.24.3" } +jsonrpsee-core = { version = "0.24.3" } k256 = { version = "0.13.3", default-features = false } kitchensink-runtime = { path = "substrate/bin/node/runtime" } kvdb = { version = "0.13.0" } @@ -815,17 +822,17 @@ kvdb-memorydb = { version = "0.13.0" } kvdb-rocksdb = { version = "0.19.0" } kvdb-shared-tests = { version = "0.11.0" } landlock = { version = "0.3.0" } -lazy_static = { version = "1.4.0" } +lazy_static = { version = "1.5.0" } libc = { version = "0.2.155" } libfuzzer-sys = { version = "0.4" } libp2p = { version = "0.52.4" } -libp2p-identity = { version = "0.2.3" } +libp2p-identity = { version = "0.2.9" } libsecp256k1 = { version = "0.7.0", default-features = false } linked-hash-map = { version = "0.5.4" } linked_hash_set = { version = "0.1.4" } linregress = { version = "0.5.1" } lite-json = { version = "0.2.0", default-features = false } -litep2p = { version = "0.6.2" } +litep2p = { version = "0.7.0", features = ["websocket"] } log = { version = "0.4.22", default-features = false } macro_magic = { version = "0.5.1" } maplit = { version = "1.0.2" } @@ -893,7 +900,7 @@ pallet-collator-selection = { path = "cumulus/pallets/collator-selection", defau pallet-collective = { path = "substrate/frame/collective", default-features = false } pallet-collective-content = { path = "cumulus/parachains/pallets/collective-content", default-features = false } pallet-contracts = { path = "substrate/frame/contracts", default-features = false } -pallet-contracts-fixtures = { path = "substrate/frame/contracts/fixtures" } +pallet-contracts-fixtures = { path = "substrate/frame/contracts/fixtures", default-features = false } pallet-contracts-mock-network = { default-features = false, path = "substrate/frame/contracts/mock-network" } pallet-contracts-proc-macro = { path = "substrate/frame/contracts/proc-macro", default-features = false } pallet-contracts-uapi = { path = "substrate/frame/contracts/uapi", default-features = false } @@ -949,6 +956,11 @@ pallet-ranked-collective = { path = "substrate/frame/ranked-collective", default pallet-recovery = { path = "substrate/frame/recovery", default-features = false } pallet-referenda = { path = "substrate/frame/referenda", default-features = false } pallet-remark = { default-features = false, path = "substrate/frame/remark" } +pallet-revive = { path = "substrate/frame/revive", default-features = false } +pallet-revive-fixtures = { path = "substrate/frame/revive/fixtures", default-features = false } +pallet-revive-mock-network = { default-features = false, path = "substrate/frame/revive/mock-network" } +pallet-revive-proc-macro = { path = "substrate/frame/revive/proc-macro", default-features = false } +pallet-revive-uapi = { path = "substrate/frame/revive/uapi", default-features = false } pallet-root-offences = { default-features = false, path = "substrate/frame/root-offences" } pallet-root-testing = { path = "substrate/frame/root-testing", default-features = false } pallet-safe-mode = { default-features = false, path = "substrate/frame/safe-mode" } @@ -1042,6 +1054,7 @@ polkadot-node-subsystem-test-helpers = { path = "polkadot/node/subsystem-test-he polkadot-node-subsystem-types = { path = "polkadot/node/subsystem-types", default-features = false } polkadot-node-subsystem-util = { path = "polkadot/node/subsystem-util", default-features = false } polkadot-overseer = { path = "polkadot/node/overseer", default-features = false } +polkadot-parachain-lib = { path = "cumulus/polkadot-parachain/polkadot-parachain-lib", default-features = false } polkadot-parachain-primitives = { path = "polkadot/parachain", default-features = false } polkadot-primitives = { path = "polkadot/primitives", default-features = false } polkadot-primitives-test-helpers = { path = "polkadot/primitives/test-helpers" } @@ -1058,7 +1071,7 @@ polkadot-subsystem-bench = { path = "polkadot/node/subsystem-bench" } polkadot-test-client = { path = "polkadot/node/test/client" } polkadot-test-runtime = { path = "polkadot/runtime/test-runtime" } polkadot-test-service = { path = "polkadot/node/test/service" } -polkavm = "0.9.3" +polkavm = { version = "0.9.3", default-features = false } polkavm-derive = "0.9.1" polkavm-linker = "0.9.2" portpicker = { version = "0.1.1" } @@ -1066,7 +1079,7 @@ pretty_assertions = { version = "1.3.0" } primitive-types = { version = "0.12.1", default-features = false } proc-macro-crate = { version = "3.0.0" } proc-macro-warning = { version = "1.0.0", default-features = false } -proc-macro2 = { version = "1.0.64" } +proc-macro2 = { version = "1.0.86" } procfs = { version = "0.16.0" } prometheus = { version = "0.13.0", default-features = false } prometheus-endpoint = { path = "substrate/utils/prometheus", default-features = false, package = "substrate-prometheus-endpoint" } @@ -1077,7 +1090,7 @@ pyroscope = { version = "0.5.7" } pyroscope_pprofrs = { version = "0.2.7" } quick_cache = { version = "0.3" } quickcheck = { version = "1.0.3", default-features = false } -quote = { version = "1.0.36" } +quote = { version = "1.0.37" } rand = { version = "0.8.5", default-features = false } rand_chacha = { version = "0.3.1", default-features = false } rand_core = { version = "0.6.2" } @@ -1103,7 +1116,7 @@ rstest = { version = "0.18.2" } rustc-hash = { version = "1.1.0" } rustc-hex = { version = "2.1.0", default-features = false } rustix = { version = "0.36.7", default-features = false } -rustversion = { version = "1.0.6" } +rustversion = { version = "1.0.17" } rusty-fork = { version = "0.3.0", default-features = false } safe-mix = { version = "1.0", default-features = false } sc-allocator = { path = "substrate/client/allocator", default-features = false } @@ -1165,17 +1178,17 @@ sc-transaction-pool-api = { path = "substrate/client/transaction-pool/api", defa sc-utils = { path = "substrate/client/utils", default-features = false } scale-info = { version = "2.11.1", default-features = false } schemars = { version = "0.8.13", default-features = false } -schnellru = { version = "0.2.1" } +schnellru = { version = "0.2.3" } schnorrkel = { version = "0.11.4", default-features = false } seccompiler = { version = "0.4.0" } secp256k1 = { version = "0.28.0", default-features = false } secrecy = { version = "0.8.0", default-features = false } seedling-runtime = { path = "cumulus/parachains/runtimes/starters/seedling" } separator = { version = "0.4.1" } -serde = { version = "1.0.204", default-features = false } +serde = { version = "1.0.210", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } -serde_json = { version = "1.0.121", default-features = false } +serde_json = { version = "1.0.128", default-features = false } serde_yaml = { version = "0.9" } serial_test = { version = "2.0.0" } sha1 = { version = "0.10.6" } @@ -1284,7 +1297,7 @@ substrate-test-runtime-client = { path = "substrate/test-utils/runtime/client" } substrate-test-runtime-transaction-pool = { path = "substrate/test-utils/runtime/transaction-pool" } substrate-test-utils = { path = "substrate/test-utils" } substrate-wasm-builder = { path = "substrate/utils/wasm-builder", default-features = false } -syn = { version = "2.0.53" } +syn = { version = "2.0.65" } sysinfo = { version = "0.30" } tar = { version = "0.4" } tempfile = { version = "3.8.1" } @@ -1308,7 +1321,7 @@ tokio-stream = { version = "0.1.14" } tokio-test = { version = "0.4.2" } tokio-tungstenite = { version = "0.20.1" } tokio-util = { version = "0.7.8" } -toml = { version = "0.8.8" } +toml = { version = "0.8.12" } toml_edit = { version = "0.19" } tower = { version = "0.4.13" } tower-http = { version = "0.5.2" } @@ -1319,7 +1332,7 @@ tracing-log = { version = "0.2.0" } tracing-subscriber = { version = "0.3.18" } tracking-allocator = { path = "polkadot/node/tracking-allocator", default-features = false, package = "staging-tracking-allocator" } trie-bench = { version = "0.39.0" } -trie-db = { version = "0.29.0", default-features = false } +trie-db = { version = "0.29.1", default-features = false } trie-root = { version = "0.18.0", default-features = false } trie-standardmap = { version = "0.16.0" } trybuild = { version = "1.0.89" } diff --git a/README.md b/README.md index 34d657194daa67de18088fb04d3ae878421c6d9f..702c853684cda347fe6c9f7ec7d2e6fcedc25ae6 100644 --- a/README.md +++ b/README.md @@ -38,27 +38,15 @@ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytec ## 🚀 Releases -> [!NOTE] -> Our release process is still Work-In-Progress and may not yet reflect the aspired outline -> here. - -The Polkadot-SDK has two release channels: `stable` and `nightly`. Production software is advised to -only use `stable`. `nightly` is meant for tinkerers to try out the latest features. The detailed -release process is described in [RELEASE.md](docs/RELEASE.md). - -You can use [`psvm`](https://github.com/paritytech/psvm) to manage your Polkadot-SDK dependency -versions in downstream projects. - -### 😌 Stable - -`stable` releases have a support duration of **three months**. In this period, the release will not -have any breaking changes. It will receive bug fixes, security fixes, performance fixes and new -non-breaking features on a **two week** cadence. + +![Current Stable Release](https://raw.githubusercontent.com/paritytech/release-registry/main/badges/polkadot-sdk-latest.svg)  ![Next Stable Release](https://raw.githubusercontent.com/paritytech/release-registry/main/badges/polkadot-sdk-next.svg) -### 🤠 Nightly +The Polkadot-SDK is released every three months as a `stableYYMMDD` release. They are supported for +one year with patches. See the next upcoming versions in the [Release +Registry](https://github.com/paritytech/release-registry/). -`nightly` releases are released every night from the `master` branch, potentially with breaking -changes. They have pre-release version numbers in the format `major.0.0-nightlyYYMMDD`. +You can use [`psvm`](https://github.com/paritytech/psvm) to update all dependencies to a specific +version without needing to manually select the correct version for each crate. ## 🛠️ Tooling 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..dc7e14de28f311817b7bced78e62ff1b5b037b1d 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions.rs @@ -18,23 +18,20 @@ //! obsolete (duplicated) data or do not pass some additional pallet-specific //! checks. -use crate::{ - extensions::refund_relayer_extension::RefundableParachainId, - messages_call_ext::MessagesCallSubType, -}; +use bp_parachains::SubmitParachainHeadsInfo; use bp_relayers::ExplicitOrAccountParams; use bp_runtime::Parachain; use pallet_bridge_grandpa::{ BridgedBlockNumber, CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, }; -use pallet_bridge_parachains::{ - CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, -}; +use pallet_bridge_messages::CallSubType as MessagesCallSubType; +use pallet_bridge_parachains::{CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper}; use pallet_bridge_relayers::Pallet as RelayersPallet; use sp_runtime::{ - traits::{Get, PhantomData, UniqueSaturatedInto}, + traits::{Get, UniqueSaturatedInto}, transaction_validity::{TransactionPriority, TransactionValidity, ValidTransactionBuilder}, }; +use sp_std::marker::PhantomData; // Re-export to avoid include tuplex dependency everywhere. #[doc(hidden)] @@ -126,17 +123,27 @@ where /// `(BundledHeaderNumber - 1 - BestKnownHeaderNumber) * Priority::get()`. /// The boost is only applied if submitter has active registration in the relayers /// pallet. -pub struct CheckAndBoostBridgeParachainsTransactions( - PhantomData<(T, RefPara, Priority, SlashAccount)>, -); - -impl, SlashAccount: Get> - BridgeRuntimeFilterCall - for CheckAndBoostBridgeParachainsTransactions +pub struct CheckAndBoostBridgeParachainsTransactions< + T, + ParachainsInstance, + Para, + Priority, + SlashAccount, +>(PhantomData<(T, ParachainsInstance, Para, Priority, SlashAccount)>); + +impl< + T, + ParachainsInstance, + Para, + Priority: Get, + SlashAccount: Get, + > BridgeRuntimeFilterCall + for CheckAndBoostBridgeParachainsTransactions where - T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, - RefPara: RefundableParachainId, - T::RuntimeCall: ParachainsCallSubtype, + T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, + T::RuntimeCall: ParachainsCallSubtype, { // bridged header number, bundled in transaction type ToPostDispatch = Option; @@ -145,10 +152,10 @@ where who: &T::AccountId, call: &T::RuntimeCall, ) -> (Self::ToPostDispatch, TransactionValidity) { - match ParachainsCallSubtype::::check_obsolete_submit_parachain_heads( + match ParachainsCallSubtype::::check_obsolete_submit_parachain_heads( call, ) { - Ok(Some(our_tx)) if our_tx.base.para_id.0 == RefPara::BridgedChain::PARACHAIN_ID => { + Ok(Some(our_tx)) if our_tx.base.para_id.0 == Para::PARACHAIN_ID => { let to_post_dispatch = Some(our_tx.base); let total_priority_boost = compute_priority_boost::(&who, our_tx.improved_by); @@ -167,7 +174,7 @@ where let Some(update) = maybe_update else { return }; // we are only interested in failed or unneeded transactions let has_failed = has_failed || - !SubmitParachainHeadsHelper::::was_successful(&update); + !SubmitParachainHeadsHelper::::was_successful(&update); if !has_failed { return @@ -275,7 +282,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { type Pre = ( $account_id, ( $( - <$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + <$filter_call as $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::ToPostDispatch, @@ -302,7 +309,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { $( let (from_validate, call_filter_validity) = < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< Self::AccountId, $call, >>::validate(&who, call); @@ -319,12 +326,13 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { info: &sp_runtime::traits::DispatchInfoOf, len: usize, ) -> Result { - use $crate::extensions::check_obsolete_extension::__private::tuplex::PushBack; + use $crate::extensions::__private::tuplex::PushBack; + let to_post_dispatch = (); $( let (from_validate, call_filter_validity) = < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::validate(&relayer, call); @@ -342,14 +350,15 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { len: usize, result: &sp_runtime::DispatchResult, ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - use $crate::extensions::check_obsolete_extension::__private::tuplex::PopFront; + use $crate::extensions::__private::tuplex::PopFront; + let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) }; let has_failed = result.is_err(); $( let (item, to_post_dispatch) = to_post_dispatch.pop_front(); < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::post_dispatch(&relayer, has_failed, item); @@ -363,25 +372,37 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { #[cfg(test)] mod tests { use super::*; - use crate::{ - extensions::refund_relayer_extension::{ - tests::{ - initialize_environment, relayer_account_at_this_chain, - submit_parachain_head_call_ex, submit_relay_header_call_ex, - }, - RefundableParachain, - }, - mock::*, - }; - use bp_polkadot_core::parachains::ParaId; + use crate::mock::*; + use bp_header_chain::StoredHeaderDataBuilder; + use bp_messages::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData}; + use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; + use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::HeaderId; - use frame_support::{assert_err, assert_ok}; + use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; + use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; + use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; + use pallet_bridge_parachains::Call as ParachainsCall; use sp_runtime::{ - traits::{ConstU64, SignedExtension}, + traits::{parameter_types, ConstU64, Header as _, SignedExtension}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; + parameter_types! { + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::ThisChain, + ); + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::BridgedChain, + ); + pub TestLaneId: LaneId = test_lane_id(); + } + pub struct MockCall { data: u32, } @@ -455,6 +476,103 @@ mod tests { } } + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } + + fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { + let test_stake: ThisChainBalance = TestStake::get(); + ExistentialDeposit::get().saturating_add(test_stake * 100) + } + + // in tests, the following accounts are equal (because of how `into_sub_account_truncating` + // works) + + fn delivery_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get()) + } + + fn confirmation_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) + } + + fn relayer_account_at_this_chain() -> ThisChainAccountId { + 0 + } + + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, + best_message: MessageNonce, + ) { + let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); + pallet_bridge_grandpa::CurrentAuthoritySet::::put( + StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), + ); + pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); + pallet_bridge_grandpa::ImportedHeaders::::insert( + best_relay_header.hash(), + bp_test_utils::test_header::(0).build(), + ); + + let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_info = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: parachain_head_at_relay_header_number, + head_hash: [parachain_head_at_relay_header_number as u8; 32].into(), + }, + next_imported_hash_position: 0, + }; + pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); + + let lane_id = test_lane_id(); + let in_lane_data = + InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); + + let out_lane_data = + OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); + + Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into( + &relayer_account_at_this_chain(), + initial_balance_of_relayer_account_at_this_chain(), + ) + .unwrap(); + } + + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { + let relay_header = BridgedChainHeader::new( + relay_header_number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let relay_justification = make_default_justification(&relay_header); + + RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { + finality_target: Box::new(relay_header), + justification: relay_justification, + }) + } + + fn submit_parachain_head_call( + parachain_head_at_relay_header_number: BridgedChainBlockNumber, + ) -> RuntimeCall { + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), + parachains: vec![( + ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + [parachain_head_at_relay_header_number as u8; 32].into(), + )], + parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, + }) + } + #[test] fn test_generated_obsolete_extension() { generate_bridge_reject_obsolete_headers_and_messages!( @@ -546,7 +664,7 @@ mod tests { let priority_boost = BridgeGrandpaWrapper::validate( &relayer_account_at_this_chain(), - &submit_relay_header_call_ex(200), + &submit_relay_header_call(200), ) .1 .unwrap() @@ -564,7 +682,7 @@ mod tests { let priority_boost = BridgeGrandpaWrapper::validate( &relayer_account_at_this_chain(), - &submit_relay_header_call_ex(200), + &submit_relay_header_call(200), ) .1 .unwrap() @@ -601,7 +719,8 @@ mod tests { type BridgeParachainsWrapper = CheckAndBoostBridgeParachainsTransactions< TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, + (), + BridgedUnderlyingParachain, ConstU64<1_000>, SlashDestination, >; @@ -613,7 +732,7 @@ mod tests { let priority_boost = BridgeParachainsWrapper::validate( &relayer_account_at_this_chain(), - &submit_parachain_head_call_ex(200), + &submit_parachain_head_call(200), ) .1 .unwrap() @@ -631,7 +750,7 @@ mod tests { let priority_boost = BridgeParachainsWrapper::validate( &relayer_account_at_this_chain(), - &submit_parachain_head_call_ex(200), + &submit_parachain_head_call(200), ) .1 .unwrap() diff --git a/bridges/bin/runtime-common/src/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_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..fed0d15cc080b78381d3e2ad6bfe8a768139101d 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -18,8 +18,6 @@ #![cfg(test)] -use crate::messages_xcm_extension::XcmAsPlainPayload; - use bp_header_chain::ChainWithGrandpa; use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, @@ -28,6 +26,7 @@ use bp_messages::{ use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::PayRewardFromAccount; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ derive_impl, parameter_types, weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, @@ -85,7 +84,11 @@ pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< >; /// Message lane used in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +#[allow(unused)] +pub fn test_lane_id() -> LaneId { + LaneId::new(1, 2) +} + /// Bridged chain id used in tests. pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; /// Maximal extrinsic size at the `BridgedChain`. @@ -111,7 +114,6 @@ crate::generate_bridge_reject_obsolete_headers_and_messages! { } parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; pub const BridgedParasPalletName: &'static str = "Paras"; pub const ExistentialDeposit: ThisChainBalance = 500; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; @@ -185,9 +187,8 @@ impl pallet_bridge_parachains::Config for TestRuntime { impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = XcmAsPlainPayload; + type OutboundPayload = Vec; type InboundPayload = Vec; type DeliveryPayments = (); @@ -218,8 +219,8 @@ impl pallet_bridge_relayers::Config for TestRuntime { pub struct DummyMessageDispatch; impl DummyMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(&b"inactive"[..], &false); + pub fn deactivate(lane: LaneId) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); } } @@ -227,8 +228,9 @@ impl MessageDispatch for DummyMessageDispatch { type DispatchPayload = Vec; type DispatchLevelResult = (); - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&b"inactive"[..]) != Some(false) + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) } fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs index 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-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..7920eb93403376159c7520dcca31295116193d08 100644 --- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs @@ -25,6 +25,7 @@ use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, }; +use codec::{Decode, Encode}; use frame_support::{ dispatch::DispatchClass, sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug, StateVersion}, @@ -104,13 +105,21 @@ frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo /// BridgeHub. /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266; + pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 57_145_832; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 314_037_860; + pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 297_644_174; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 57_414_813; + pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 56_740_432; +} + +/// Wrapper over `BridgeHubRococo`'s `RuntimeCall` that can be used without a runtime. +#[derive(Decode, Encode)] +pub enum RuntimeCall { + /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubWestend`. + #[codec(index = 52)] + XcmOverBridgeHubWestend(bp_xcm_bridge_hub::XcmBridgeHubCall), } diff --git a/bridges/chains/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml index 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..644fa64c687b3162f1380cf3e666373226c0a16d 100644 --- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs @@ -24,6 +24,7 @@ use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, }; +use codec::{Decode, Encode}; use frame_support::dispatch::DispatchClass; use sp_runtime::{RuntimeDebug, StateVersion}; @@ -93,13 +94,21 @@ frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend /// BridgeHub. /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 17_756_830_000; + pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 18_191_740_000; /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 94_211_536_452; + pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 89_293_427_116; /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_224_486_452; + pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_022_177_116; +} + +/// Wrapper over `BridgeHubWestend`'s `RuntimeCall` that can be used without a runtime. +#[derive(Decode, Encode)] +pub enum RuntimeCall { + /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubRococo`. + #[codec(index = 45)] + XcmOverBridgeHubRococo(bp_xcm_bridge_hub::XcmBridgeHubCall), } diff --git a/bridges/docs/polkadot-kusama-bridge-overview.md b/bridges/docs/polkadot-kusama-bridge-overview.md index 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..dff4b98fd91904260545e581562668ab5be40b4a 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -117,7 +117,7 @@ pub mod pallet { /// /// However, if the bridged chain gets compromised, its validators may generate as many /// "free" headers as they want. And they may fill the whole block (at this chain) for - /// free. This constants limits number of calls that we may refund in a single block. + /// free. This constant limits number of calls that we may refund in a single block. /// All calls above this limit are accepted, but are not refunded. #[pallet::constant] type MaxFreeHeadersPerBlock: Get; diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml index 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..b3a4447fb0211737cc5410a11c87dca9e12d8d80 100644 --- a/bridges/modules/messages/src/benchmarking.rs +++ b/bridges/modules/messages/src/benchmarking.rs @@ -19,14 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, - BridgedChainOf, Call, OutboundLanes, RuntimeInboundLaneStorage, + active_outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, BridgedChainOf, Call, + InboundLanes, OutboundLanes, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, ChainWithMessages, DeliveredMessages, - InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + InboundLaneData, LaneId, LaneState, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, HashOf, UnverifiedStorageProofParams}; @@ -74,10 +74,8 @@ pub struct MessageDeliveryProofParams { /// Trait that must be implemented by runtime. pub trait Config: crate::Config { /// Lane id to use in benchmarks. - /// - /// By default, lane 00000000 is used. fn bench_lane_id() -> LaneId { - LaneId([0, 0, 0, 0]) + LaneId::new(1, 2) } /// Return id of relayer account at the bridged chain. @@ -113,22 +111,32 @@ pub trait Config: crate::Config { } fn send_regular_message, I: 'static>() { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); + OutboundLanes::::insert( + T::bench_lane_id(), + OutboundLaneData { + state: LaneState::Opened, + latest_generated_nonce: 1, + ..Default::default() + }, + ); + + let mut outbound_lane = active_outbound_lane::(T::bench_lane_id()).unwrap(); outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages")); } fn receive_messages, I: 'static>(nonce: MessageNonce) { - let mut inbound_lane_storage = - RuntimeInboundLaneStorage::::from_lane_id(T::bench_lane_id()); - inbound_lane_storage.set_data(InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: T::bridged_relayer_id(), - messages: DeliveredMessages::new(nonce), - }] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }); + InboundLanes::::insert( + T::bench_lane_id(), + InboundLaneData { + state: LaneState::Opened, + relayers: vec![UnrewardedRelayer { + relayer: T::bridged_relayer_id(), + messages: DeliveredMessages::new(nonce), + }] + .into(), + last_confirmed_nonce: 0, + }, + ); } struct ReceiveMessagesProofSetup, I: 'static> { @@ -173,8 +181,8 @@ impl, I: 'static> ReceiveMessagesProofSetup { fn check_last_nonce(&self) { assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - self.last_nonce(), + crate::InboundLanes::::get(&T::bench_lane_id()).map(|d| d.last_delivered_nonce()), + Some(self.last_nonce()), ); } } @@ -277,6 +285,7 @@ mod benchmarks { lane: T::bench_lane_id(), message_nonces: setup.nonces(), outbound_lane_data: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: setup.last_nonce(), latest_received_nonce: ReceiveMessagesProofSetup::::LATEST_RECEIVED_NONCE, latest_generated_nonce: setup.last_nonce(), @@ -356,6 +365,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: DeliveredMessages::new(1), @@ -374,7 +384,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(1) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -404,6 +417,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: delivered_messages, @@ -422,7 +436,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -451,6 +468,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer1_id.clone(), @@ -475,7 +493,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer1_id)); assert!(T::is_relayer_rewarded(&relayer2_id)); } diff --git a/bridges/bin/runtime-common/src/messages_call_ext.rs b/bridges/modules/messages/src/call_ext.rs similarity index 57% rename from bridges/bin/runtime-common/src/messages_call_ext.rs rename to bridges/modules/messages/src/call_ext.rs index a9ee1969ae0ca462f36098f03b4454e1399af129..8e021c8e5e242a6b89873b6178bdee6279d3b626 100644 --- a/bridges/bin/runtime-common/src/messages_call_ext.rs +++ b/bridges/modules/messages/src/call_ext.rs @@ -16,121 +16,18 @@ //! Helpers for easier manipulation of call processing with signed extensions. +use crate::{BridgedChainOf, Config, InboundLanes, OutboundLanes, Pallet, LOG_TARGET}; + use bp_messages::{ - target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, MessageNonce, + target_chain::MessageDispatch, BaseMessagesProofInfo, ChainWithMessages, InboundLaneData, + LaneId, MessageNonce, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo, + ReceiveMessagesProofInfo, UnrewardedRelayerOccupation, }; use bp_runtime::{AccountIdOf, OwnedBridgeModule}; use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use pallet_bridge_messages::{BridgedChainOf, Config, Pallet}; -use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; -use sp_std::ops::RangeInclusive; - -/// Generic info about a messages delivery/confirmation proof. -#[derive(PartialEq, RuntimeDebug)] -pub struct BaseMessagesProofInfo { - /// Message lane, used by the call. - pub lane_id: LaneId, - /// Nonces of messages, included in the call. - /// - /// For delivery transaction, it is nonces of bundled messages. For confirmation - /// transaction, it is nonces that are to be confirmed during the call. - pub bundled_range: RangeInclusive, - /// Nonce of the best message, stored by this chain before the call is dispatched. - /// - /// For delivery transaction, it is the nonce of best delivered message before the call. - /// For confirmation transaction, it is the nonce of best confirmed message before the call. - pub best_stored_nonce: MessageNonce, -} - -impl BaseMessagesProofInfo { - /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. - fn appends_to_stored_nonce(&self) -> bool { - Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) - } -} - -/// Occupation state of the unrewarded relayers vector. -#[derive(PartialEq, RuntimeDebug)] -#[cfg_attr(test, derive(Default))] -pub struct UnrewardedRelayerOccupation { - /// The number of remaining unoccupied entries for new relayers. - pub free_relayer_slots: MessageNonce, - /// The number of messages that we are ready to accept. - pub free_message_slots: MessageNonce, -} - -/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo { - /// Base messages proof info - pub base: BaseMessagesProofInfo, - /// State of unrewarded relayers vector. - pub unrewarded_relayers: UnrewardedRelayerOccupation, -} - -impl ReceiveMessagesProofInfo { - /// Returns true if: - /// - /// - either inbound lane is ready to accept bundled messages; - /// - /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed - /// messages and/or unrewarded relayers. - fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { - // if dispatcher is inactive, we don't accept any delivery transactions - if !is_dispatcher_active { - return true - } - - // transactions with zero bundled nonces are not allowed, unless they're message - // delivery transactions, which brings reward confirmations required to unblock - // the lane - if self.base.bundled_range.is_empty() { - let empty_transactions_allowed = - // we allow empty transactions when we can't accept delivery from new relayers - self.unrewarded_relayers.free_relayer_slots == 0 || - // or if we can't accept new messages at all - self.unrewarded_relayers.free_message_slots == 0; - - return !empty_transactions_allowed - } - - // otherwise we require bundled messages to continue stored range - !self.base.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); - -impl ReceiveMessagesDeliveryProofInfo { - /// Returns true if outbound lane is ready to accept confirmations of bundled messages. - fn is_obsolete(&self) -> bool { - self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call -/// which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub enum CallInfo { - /// Messages delivery call info. - ReceiveMessagesProof(ReceiveMessagesProofInfo), - /// Messages delivery confirmation call info. - ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), -} - -impl CallInfo { - /// Returns range of messages, bundled with the call. - pub fn bundled_messages(&self) -> RangeInclusive { - match *self { - Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), - Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), - } - } -} +use sp_runtime::transaction_validity::TransactionValidity; -/// Helper struct that provides methods for working with a call supported by `CallInfo`. +/// Helper struct that provides methods for working with a call supported by `MessagesCallInfo`. pub struct CallHelper, I: 'static> { _phantom_data: sp_std::marker::PhantomData<(T, I)>, } @@ -142,11 +39,13 @@ impl, I: 'static> CallHelper { /// /// - call is `receive_messages_delivery_proof` and all messages confirmations have been /// received. - pub fn was_successful(info: &CallInfo) -> bool { + pub fn was_successful(info: &MessagesCallInfo) -> bool { match info { - CallInfo::ReceiveMessagesProof(info) => { - let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); + MessagesCallInfo::ReceiveMessagesProof(info) => { + let inbound_lane_data = match InboundLanes::::get(info.base.lane_id) { + Some(inbound_lane_data) => inbound_lane_data, + None => return false, + }; if info.base.bundled_range.is_empty() { let post_occupation = unrewarded_relayers_occupation::(&inbound_lane_data); @@ -160,9 +59,11 @@ impl, I: 'static> CallHelper { inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() }, - CallInfo::ReceiveMessagesDeliveryProof(info) => { - let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); + MessagesCallInfo::ReceiveMessagesDeliveryProof(info) => { + let outbound_lane_data = match OutboundLanes::::get(info.0.lane_id) { + Some(outbound_lane_data) => outbound_lane_data, + None => return false, + }; outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() }, } @@ -170,7 +71,7 @@ impl, I: 'static> CallHelper { } /// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. -pub trait MessagesCallSubType, I: 'static>: +pub trait CallSubType, I: 'static>: IsSubType, T>> { /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. @@ -180,13 +81,13 @@ pub trait MessagesCallSubType, I: 'static>: /// a `ReceiveMessagesDeliveryProof` call. fn receive_messages_delivery_proof_info(&self) -> Option; - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof` /// or a `ReceiveMessagesDeliveryProof` call. - fn call_info(&self) -> Option; + fn call_info(&self) -> Option; - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof` /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. - fn call_info_for(&self, lane_id: LaneId) -> Option; + fn call_info_for(&self, lane_id: LaneId) -> Option; /// Ensures that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call: /// @@ -211,15 +112,13 @@ impl< Call: IsSubType, T>>, T: frame_system::Config + Config, I: 'static, - > MessagesCallSubType for T::RuntimeCall + > CallSubType for T::RuntimeCall { fn receive_messages_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_proof { - ref proof, - .. - }) = self.is_sub_type() + if let Some(crate::Call::::receive_messages_proof { ref proof, .. }) = + self.is_sub_type() { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); + let inbound_lane_data = InboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { @@ -237,13 +136,13 @@ impl< } fn receive_messages_delivery_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + if let Some(crate::Call::::receive_messages_delivery_proof { ref proof, ref relayers_state, .. }) = self.is_sub_type() { - let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); + let outbound_lane_data = OutboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: proof.lane, @@ -260,23 +159,23 @@ impl< None } - fn call_info(&self) -> Option { + fn call_info(&self) -> Option { if let Some(info) = self.receive_messages_proof_info() { - return Some(CallInfo::ReceiveMessagesProof(info)) + return Some(MessagesCallInfo::ReceiveMessagesProof(info)) } if let Some(info) = self.receive_messages_delivery_proof_info() { - return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) + return Some(MessagesCallInfo::ReceiveMessagesDeliveryProof(info)) } None } - fn call_info_for(&self, lane_id: LaneId) -> Option { + fn call_info_for(&self, lane_id: LaneId) -> Option { self.call_info().filter(|info| { let actual_lane_id = match info { - CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, - CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, + MessagesCallInfo::ReceiveMessagesProof(info) => info.base.lane_id, + MessagesCallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, }; actual_lane_id == lane_id }) @@ -287,29 +186,30 @@ impl< match self.call_info() { Some(proof_info) if is_pallet_halted => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting messages transaction on halted pallet: {:?}", proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Call.into() }, - Some(CallInfo::ReceiveMessagesProof(proof_info)) - if proof_info.is_obsolete(T::MessageDispatch::is_active()) => + Some(MessagesCallInfo::ReceiveMessagesProof(proof_info)) + if proof_info + .is_obsolete(T::MessageDispatch::is_active(proof_info.base.lane_id)) => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting obsolete messages delivery transaction: {:?}", proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() }, - Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) + Some(MessagesCallInfo::ReceiveMessagesDeliveryProof(proof_info)) if proof_info.is_obsolete() => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting obsolete messages confirmation transaction: {:?}", proof_info, ); @@ -343,52 +243,49 @@ fn unrewarded_relayers_occupation, I: 'static>( #[cfg(test)] mod tests { use super::*; - use crate::{ - messages_call_ext::MessagesCallSubType, - mock::{BridgedUnderlyingChain, DummyMessageDispatch, TestRuntime, ThisChainRuntimeCall}, - }; + use crate::tests::mock::*; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, LaneState, + OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use sp_std::ops::RangeInclusive; + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } + fn fill_unrewarded_relayers() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - for n in 0..BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { + let mut inbound_lane_state = InboundLanes::::get(test_lane_id()).unwrap(); + for n in 0..BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { begin: n + 1, end: n + 1 }, }); } - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); + InboundLanes::::insert(test_lane_id(), inbound_lane_state); } fn fill_unrewarded_messages() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + let mut inbound_lane_state = InboundLanes::::get(test_lane_id()).unwrap(); inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { begin: 1, - end: BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + end: BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }); - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); + InboundLanes::::insert(test_lane_id(), inbound_lane_state); } fn deliver_message_10() { - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, + InboundLanes::::insert( + test_lane_id(), + bp_messages::InboundLaneData { + state: LaneState::Opened, + relayers: Default::default(), + last_confirmed_nonce: 10, + }, ); } @@ -396,28 +293,33 @@ mod tests { nonces_start: bp_messages::MessageNonce, nonces_end: bp_messages::MessageNonce, ) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: 42, - messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) - as u32, - dispatch_weight: frame_support::weights::Weight::zero(), - proof: Box::new(FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), - nonces_start, - nonces_end, - }), - }, - ) + RuntimeCall::Messages(crate::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: 42, + messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) as u32, + dispatch_weight: frame_support::weights::Weight::zero(), + proof: Box::new(FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: Default::default(), + lane: test_lane_id(), + nonces_start, + nonces_end, + }), + }) .check_obsolete_call() .is_ok() } + fn run_test(test: impl Fn() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + InboundLanes::::insert(test_lane_id(), InboundLaneData::opened()); + OutboundLanes::::insert(test_lane_id(), OutboundLaneData::opened()); + test() + }) + } + #[test] fn extension_rejects_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages 8..=9 // => tx is rejected deliver_message_10(); @@ -427,7 +329,7 @@ mod tests { #[test] fn extension_rejects_same_message() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to import messages 10..=10 // => tx is rejected deliver_message_10(); @@ -437,7 +339,7 @@ mod tests { #[test] fn extension_rejects_call_with_some_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 10..=15 => tx is rejected deliver_message_10(); @@ -447,7 +349,7 @@ mod tests { #[test] fn extension_rejects_call_with_future_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 13..=15 => tx is rejected deliver_message_10(); @@ -457,12 +359,12 @@ mod tests { #[test] fn extension_reject_call_when_dispatcher_is_inactive() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted, but we have inactive dispatcher, so... deliver_message_10(); - DummyMessageDispatch::deactivate(); + TestMessageDispatch::deactivate(test_lane_id()); assert!(!validate_message_delivery(11, 15)); }); } @@ -470,7 +372,7 @@ mod tests { #[test] fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!validate_message_delivery(10, 9)); }); @@ -479,7 +381,7 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); fill_unrewarded_relayers(); assert!(validate_message_delivery(10, 9)); @@ -489,18 +391,18 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(validate_message_delivery( - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1 + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1 )); }); } #[test] fn extension_accepts_new_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted deliver_message_10(); @@ -509,9 +411,10 @@ mod tests { } fn confirm_message_10() { - pallet_bridge_messages::OutboundLanes::::insert( - LaneId([0, 0, 0, 0]), + OutboundLanes::::insert( + test_lane_id(), bp_messages::OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 0, latest_received_nonce: 10, latest_generated_nonce: 10, @@ -520,26 +423,21 @@ mod tests { } fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() - }, + RuntimeCall::Messages(crate::Call::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Default::default(), + lane: test_lane_id(), }, - ) + relayers_state: UnrewardedRelayersState { last_delivered_nonce, ..Default::default() }, + }) .check_obsolete_call() .is_ok() } #[test] fn extension_rejects_obsolete_confirmations() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#5 => tx // is rejected confirm_message_10(); @@ -549,7 +447,7 @@ mod tests { #[test] fn extension_rejects_same_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#10 => // tx is rejected confirm_message_10(); @@ -559,7 +457,7 @@ mod tests { #[test] fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); fill_unrewarded_relayers(); assert!(!validate_message_confirmation(10)); @@ -568,7 +466,7 @@ mod tests { #[test] fn extension_accepts_new_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#15 => // tx is accepted confirm_message_10(); @@ -580,10 +478,10 @@ mod tests { bundled_range: RangeInclusive, is_empty: bool, ) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( + CallHelper::::was_successful(&MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), + lane_id: test_lane_id(), bundled_range, best_stored_nonce: 0, // doesn't matter for `was_successful` }, @@ -592,7 +490,7 @@ mod tests { free_message_slots: if is_empty { 0 } else { - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX }, }, }, @@ -602,7 +500,7 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(!was_message_delivery_successful(10..=9, true)); }); @@ -611,14 +509,14 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { assert!(was_message_delivery_successful(10..=9, true)); }); } #[test] fn was_successful_returns_false_for_failed_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(10..=12, false)); }); @@ -626,7 +524,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(9..=12, false)); }); @@ -634,25 +532,27 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(was_message_delivery_successful(9..=10, false)); }); } fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }), - )) + CallHelper::::was_successful( + &MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: test_lane_id(), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }, + )), + ) } #[test] fn was_successful_returns_false_for_failed_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(10..=12)); }); @@ -660,7 +560,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(9..=12)); }); @@ -668,7 +568,7 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(was_message_confirmation_successful(9..=10)); }); diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs index 7ef4599a93c4823a9be7fa918b35c309cea6611a..65240feb7194aaef83ed5e2268f3fa44a4f739a6 100644 --- a/bridges/modules/messages/src/inbound_lane.rs +++ b/bridges/modules/messages/src/inbound_lane.rs @@ -20,8 +20,8 @@ use crate::{BridgedChainOf, Config}; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, - OutboundLaneData, ReceptionResult, UnrewardedRelayer, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, MessageKey, + MessageNonce, OutboundLaneData, ReceptionResult, UnrewardedRelayer, }; use bp_runtime::AccountIdOf; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; @@ -41,9 +41,11 @@ pub trait InboundLaneStorage { /// Return maximal number of unconfirmed messages in inbound lane. fn max_unconfirmed_messages(&self) -> MessageNonce; /// Get lane data from the storage. - fn get_or_init_data(&mut self) -> InboundLaneData; + fn data(&self) -> InboundLaneData; /// Update lane data in the storage. fn set_data(&mut self, data: InboundLaneData); + /// Purge lane data from the storage. + fn purge(self); } /// Inbound lane data wrapper that implements `MaxEncodedLen`. @@ -120,9 +122,21 @@ impl InboundLane { InboundLane { storage } } - /// Returns `mut` storage reference. - pub fn storage_mut(&mut self) -> &mut S { - &mut self.storage + /// Get lane state. + pub fn state(&self) -> LaneState { + self.storage.data().state + } + + /// Returns storage reference. + pub fn storage(&self) -> &S { + &self.storage + } + + /// Set lane state. + pub fn set_state(&mut self, state: LaneState) { + let mut data = self.storage.data(); + data.state = state; + self.storage.set_data(data); } /// Receive state of the corresponding outbound lane. @@ -130,7 +144,7 @@ impl InboundLane { &mut self, outbound_lane_data: OutboundLaneData, ) -> Option { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); let last_delivered_nonce = data.last_delivered_nonce(); if outbound_lane_data.latest_received_nonce > last_delivered_nonce { @@ -173,7 +187,7 @@ impl InboundLane { nonce: MessageNonce, message_data: DispatchMessageData, ) -> ReceptionResult { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); if Some(nonce) != data.last_delivered_nonce().checked_add(1) { return ReceptionResult::InvalidNonce } @@ -211,20 +225,17 @@ impl InboundLane { ReceptionResult::Dispatched(dispatch_result) } + + /// Purge lane state from the storage. + pub fn purge(self) { + self.storage.purge() + } } #[cfg(test)] mod tests { use super::*; - use crate::{ - inbound_lane, - tests::mock::{ - dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test, - unrewarded_relayer, BridgedChain, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, - TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, - }, - RuntimeInboundLaneStorage, - }; + use crate::{active_inbound_lane, lanes_manager::RuntimeInboundLaneStorage, tests::mock::*}; use bp_messages::UnrewardedRelayersState; fn receive_regular_message( @@ -244,7 +255,7 @@ mod tests { #[test] fn receive_status_update_ignores_status_from_the_future() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -254,14 +265,14 @@ mod tests { None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); }); } #[test] fn receive_status_update_ignores_obsolete_status() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); @@ -272,7 +283,7 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -281,20 +292,20 @@ mod tests { }), None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); }); } #[test] fn receive_status_update_works() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] ); @@ -305,9 +316,9 @@ mod tests { }), Some(2), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 2); + assert_eq!(lane.storage.data().last_confirmed_nonce, 2); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] ); @@ -318,16 +329,16 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!(lane.storage.get_or_init_data().relayers, vec![]); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().relayers, vec![]); }); } #[test] fn receive_status_update_works_with_batches_from_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut seed_storage_data = lane.storage.get_or_init_data(); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); + let mut seed_storage_data = lane.storage.data(); // Prepare data seed_storage_data.last_confirmed_nonce = 0; seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); @@ -343,9 +354,9 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(4, 4, TEST_RELAYER_B), unrewarded_relayer(5, 5, TEST_RELAYER_C) @@ -357,7 +368,7 @@ mod tests { #[test] fn fails_to_receive_message_with_incorrect_nonce() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -366,14 +377,14 @@ mod tests { ), ReceptionResult::InvalidNonce ); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); + assert_eq!(lane.storage.data().last_delivered_nonce(), 0); }); } #[test] fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let max_nonce = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; for current_nonce in 1..max_nonce + 1 { assert_eq!( @@ -409,7 +420,7 @@ mod tests { #[test] fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let max_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; for current_nonce in 1..=max_nonce { assert_eq!( @@ -445,7 +456,7 @@ mod tests { #[test] fn correctly_receives_following_messages_from_two_relayers_alternately() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -471,7 +482,7 @@ mod tests { ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), unrewarded_relayer(2, 2, TEST_RELAYER_B), @@ -484,7 +495,7 @@ mod tests { #[test] fn rejects_same_message_from_two_different_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -507,16 +518,16 @@ mod tests { #[test] fn correct_message_is_processed_instantly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 1); + assert_eq!(lane.storage.data().last_delivered_nonce(), 1); }); } #[test] fn unspent_weight_is_returned_by_receive_message() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let mut payload = REGULAR_PAYLOAD; *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; assert_eq!( @@ -533,7 +544,7 @@ mod tests { #[test] fn first_message_is_confirmed_correctly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); assert_eq!( @@ -544,7 +555,7 @@ mod tests { Some(1), ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, diff --git a/bridges/modules/messages/src/lanes_manager.rs b/bridges/modules/messages/src/lanes_manager.rs new file mode 100644 index 0000000000000000000000000000000000000000..4f5ac1c0a40325d9f15279bc84a3753e185b92fc --- /dev/null +++ b/bridges/modules/messages/src/lanes_manager.rs @@ -0,0 +1,283 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +use crate::{ + BridgedChainOf, Config, InboundLane, InboundLaneStorage, InboundLanes, OutboundLane, + OutboundLaneStorage, OutboundLanes, OutboundMessages, StoredInboundLaneData, + StoredMessagePayload, +}; + +use bp_messages::{ + target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, LaneState, + MessageKey, MessageNonce, OutboundLaneData, +}; +use bp_runtime::AccountIdOf; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ensure, sp_runtime::RuntimeDebug, PalletError}; +use scale_info::TypeInfo; +use sp_std::marker::PhantomData; + +/// Lanes manager errors. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum LanesManagerError { + /// Inbound lane already exists. + InboundLaneAlreadyExists, + /// Outbound lane already exists. + OutboundLaneAlreadyExists, + /// No inbound lane with given id. + UnknownInboundLane, + /// No outbound lane with given id. + UnknownOutboundLane, + /// Inbound lane with given id is closed. + ClosedInboundLane, + /// Outbound lane with given id is closed. + ClosedOutboundLane, + /// Message dispatcher is inactive at given inbound lane. This is logical equivalent + /// of the [`Self::ClosedInboundLane`] variant. + LaneDispatcherInactive, +} + +/// Message lanes manager. +pub struct LanesManager(PhantomData<(T, I)>); + +impl, I: 'static> Default for LanesManager { + fn default() -> Self { + Self::new() + } +} + +impl, I: 'static> LanesManager { + /// Create new lanes manager. + pub fn new() -> Self { + Self(PhantomData) + } + + /// Create new inbound lane in `Opened` state. + pub fn create_inbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + InboundLanes::::try_mutate(lane_id, |lane| match lane { + Some(_) => Err(LanesManagerError::InboundLaneAlreadyExists), + None => { + *lane = Some(StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, + ..Default::default() + })); + Ok(()) + }, + })?; + + self.active_inbound_lane(lane_id) + } + + /// Create new outbound lane in `Opened` state. + pub fn create_outbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + OutboundLanes::::try_mutate(lane_id, |lane| match lane { + Some(_) => Err(LanesManagerError::OutboundLaneAlreadyExists), + None => { + *lane = Some(OutboundLaneData { state: LaneState::Opened, ..Default::default() }); + Ok(()) + }, + })?; + + self.active_outbound_lane(lane_id) + } + + /// Get existing inbound lane, checking that it is in usable state. + pub fn active_inbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, true)?)) + } + + /// Get existing outbound lane, checking that it is in usable state. + pub fn active_outbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, true)?)) + } + + /// Get existing inbound lane without any additional state checks. + pub fn any_state_inbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, false)?)) + } + + /// Get existing outbound lane without any additional state checks. + pub fn any_state_outbound_lane( + &self, + lane_id: LaneId, + ) -> Result>, LanesManagerError> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, false)?)) + } +} + +/// Runtime inbound lane storage. +pub struct RuntimeInboundLaneStorage, I: 'static = ()> { + pub(crate) lane_id: LaneId, + pub(crate) cached_data: InboundLaneData>>, +} + +impl, I: 'static> RuntimeInboundLaneStorage { + /// Creates new runtime inbound lane storage for given **existing** lane. + fn from_lane_id( + lane_id: LaneId, + check_active: bool, + ) -> Result, LanesManagerError> { + let cached_data = + InboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownInboundLane)?; + + if check_active { + // check that the lane is not explicitly closed + ensure!(cached_data.state.is_active(), LanesManagerError::ClosedInboundLane); + // apart from the explicit closure, the lane may be unable to receive any messages. + // Right now we do an additional check here, but it may be done later (e.g. by + // explicitly closing the lane and reopening it from + // `pallet-xcm-bridge-hub::on-initialize`) + // + // The fact that we only check it here, means that the `MessageDispatch` may switch + // to inactive state during some message dispatch in the middle of message delivery + // transaction. But we treat result of `MessageDispatch::is_active()` as a hint, so + // we know that it won't drop messages - just it experiences problems with processing. + // This would allow us to check that in our signed extensions, and invalidate + // transaction early, thus avoiding losing honest relayers funds. This problem should + // gone with relayers coordination protocol. + // + // There's a limit on number of messages in the message delivery transaction, so even + // if we dispatch (enqueue) some additional messages, we'll know the maximal queue + // length; + ensure!( + T::MessageDispatch::is_active(lane_id), + LanesManagerError::LaneDispatcherInactive + ); + } + + Ok(RuntimeInboundLaneStorage { lane_id, cached_data: cached_data.into() }) + } + + /// Returns number of bytes that may be subtracted from the PoV component of + /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the + /// maximal configured. + /// + /// Maximal inbound lane state set size is configured by the + /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pallet configuration. The PoV + /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, + /// we may subtract extra bytes from this component. + pub fn extra_proof_size_bytes(&self) -> u64 { + let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); + let relayers_count = self.data().relayers.len(); + let actual_encoded_len = + InboundLaneData::>>::encoded_size_hint(relayers_count) + .unwrap_or(usize::MAX); + max_encoded_len.saturating_sub(actual_encoded_len) as _ + } +} + +impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { + type Relayer = AccountIdOf>; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn max_unrewarded_relayer_entries(&self) -> MessageNonce { + BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX + } + + fn max_unconfirmed_messages(&self) -> MessageNonce { + BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + } + + fn data(&self) -> InboundLaneData>> { + self.cached_data.clone() + } + + fn set_data(&mut self, data: InboundLaneData>>) { + self.cached_data = data.clone(); + InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) + } + + fn purge(self) { + InboundLanes::::remove(self.lane_id) + } +} + +/// Runtime outbound lane storage. +#[derive(Debug, PartialEq, Eq)] +pub struct RuntimeOutboundLaneStorage { + pub(crate) lane_id: LaneId, + pub(crate) cached_data: OutboundLaneData, + pub(crate) _phantom: PhantomData<(T, I)>, +} + +impl, I: 'static> RuntimeOutboundLaneStorage { + /// Creates new runtime outbound lane storage for given **existing** lane. + fn from_lane_id(lane_id: LaneId, check_active: bool) -> Result { + let cached_data = + OutboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownOutboundLane)?; + ensure!( + !check_active || cached_data.state.is_active(), + LanesManagerError::ClosedOutboundLane + ); + Ok(Self { lane_id, cached_data, _phantom: PhantomData }) + } +} + +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { + type StoredMessagePayload = StoredMessagePayload; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn data(&self) -> OutboundLaneData { + self.cached_data.clone() + } + + fn set_data(&mut self, data: OutboundLaneData) { + self.cached_data = data.clone(); + OutboundLanes::::insert(self.lane_id, data) + } + + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) + } + + fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { + OutboundMessages::::insert( + MessageKey { lane_id: self.lane_id, nonce }, + message_payload, + ); + } + + fn remove_message(&mut self, nonce: &MessageNonce) { + OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + } + + fn purge(self) { + OutboundLanes::::remove(self.lane_id) + } +} diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index bf105b14040185f61e627a35f4f53b3a66b48c19..b7fe1c7dbb19f029de81bd2cd42548a612e1980a 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -36,8 +36,13 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -pub use inbound_lane::StoredInboundLaneData; -pub use outbound_lane::StoredMessagePayload; +pub use inbound_lane::{InboundLane, InboundLaneStorage, StoredInboundLaneData}; +pub use lanes_manager::{ + LanesManager, LanesManagerError, RuntimeInboundLaneStorage, RuntimeOutboundLaneStorage, +}; +pub use outbound_lane::{ + OutboundLane, OutboundLaneStorage, ReceptionConfirmationError, StoredMessagePayload, +}; pub use weights::WeightInfo; pub use weights_ext::{ ensure_able_to_receive_confirmation, ensure_able_to_receive_message, @@ -45,11 +50,6 @@ pub use weights_ext::{ EXPECTED_DEFAULT_MESSAGE_LENGTH, EXTRA_STORAGE_PROOF_SIZE, }; -use crate::{ - inbound_lane::{InboundLane, InboundLaneStorage}, - outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError}, -}; - use bp_header_chain::HeaderChain; use bp_messages::{ source_chain::{ @@ -68,12 +68,13 @@ use bp_runtime::{ AccountIdOf, BasicOperatingMode, HashOf, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size, }; -use codec::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; -use sp_runtime::traits::UniqueSaturatedFrom; use sp_std::{marker::PhantomData, prelude::*}; +mod call_ext; mod inbound_lane; +mod lanes_manager; mod outbound_lane; mod proofs; mod tests; @@ -83,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::*; @@ -116,9 +119,6 @@ pub mod pallet { /// Bridged chain headers provider. type BridgedHeaderChain: HeaderChain; - /// Get all active outbound lanes that the message pallet is serving. - type ActiveOutboundLanes: Get<&'static [LaneId]>; - /// Payload type of outbound messages. This payload is dispatched on the bridged chain. type OutboundPayload: Parameter + Size; /// Payload type of inbound messages. This payload is dispatched on this chain. @@ -144,6 +144,7 @@ pub mod pallet { pub type BridgedHeaderChainOf = >::BridgedHeaderChain; #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { @@ -153,40 +154,6 @@ pub mod pallet { type OperatingModeStorage = PalletOperatingMode; } - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet - where - u32: TryFrom>, - { - fn on_idle(_block: BlockNumberFor, remaining_weight: Weight) -> Weight { - // we'll need at least to read outbound lane state, kill a message and update lane state - let db_weight = T::DbWeight::get(); - if !remaining_weight.all_gte(db_weight.reads_writes(1, 2)) { - return Weight::zero() - } - - // messages from lane with index `i` in `ActiveOutboundLanes` are pruned when - // `System::block_number() % lanes.len() == i`. Otherwise we need to read lane states on - // every block, wasting the whole `remaining_weight` for nothing and causing starvation - // of the last lane pruning - let active_lanes = T::ActiveOutboundLanes::get(); - let active_lanes_len = (active_lanes.len() as u32).into(); - let active_lane_index = u32::unique_saturated_from( - frame_system::Pallet::::block_number() % active_lanes_len, - ); - let active_lane_id = active_lanes[active_lane_index as usize]; - - // first db read - outbound lane state - let mut active_lane = outbound_lane::(active_lane_id); - let mut used_weight = db_weight.reads(1); - // and here we'll have writes - used_weight += active_lane.prune_messages(db_weight, remaining_weight - used_weight); - - // we already checked we have enough `remaining_weight` to cover this `used_weight` - used_weight - } - } - #[pallet::call] impl, I: 'static> Pallet { /// Change `PalletOwner`. @@ -250,9 +217,6 @@ pub mod pallet { Error::::TooManyMessagesInTheProof ); - // if message dispatcher is currently inactive, we won't accept any messages - ensure!(T::MessageDispatch::is_active(), Error::::MessageDispatchInactive); - // why do we need to know the weight of this (`receive_messages_proof`) call? Because // we may want to return some funds for not-dispatching (or partially dispatching) some // messages to the call origin (relayer). And this is done by returning actual weight @@ -271,92 +235,89 @@ pub mod pallet { let mut actual_weight = declared_weight; // verify messages proof && convert proof into messages - let messages = verify_and_decode_messages_proof::(*proof, messages_count) - .map_err(|err| { - log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); + let (lane_id, lane_data) = + verify_and_decode_messages_proof::(*proof, messages_count).map_err( + |err| { + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); - Error::::InvalidMessagesProof - })?; + Error::::InvalidMessagesProof + }, + )?; // dispatch messages and (optionally) update lane(s) state(s) let mut total_messages = 0; let mut valid_messages = 0; - let mut messages_received_status = Vec::with_capacity(messages.len()); let mut dispatch_weight_left = dispatch_weight; - for (lane_id, lane_data) in messages { - let mut lane = inbound_lane::(lane_id); - - // subtract extra storage proof bytes from the actual PoV size - there may be - // less unrewarded relayers than the maximal configured value - let lane_extra_proof_size_bytes = lane.storage_mut().extra_proof_size_bytes(); - actual_weight = actual_weight.set_proof_size( - actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), - ); + let mut lane = active_inbound_lane::(lane_id)?; - if let Some(lane_state) = lane_data.lane_state { - let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); - if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { - log::trace!( - target: LOG_TARGET, - "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", - lane_id, - updated_latest_confirmed_nonce, - UnrewardedRelayersState::from(&lane.storage_mut().get_or_init_data()), - ); - } - } + // subtract extra storage proof bytes from the actual PoV size - there may be + // less unrewarded relayers than the maximal configured value + let lane_extra_proof_size_bytes = lane.storage().extra_proof_size_bytes(); + actual_weight = actual_weight.set_proof_size( + actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), + ); - let mut lane_messages_received_status = - ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); - for mut message in lane_data.messages { - debug_assert_eq!(message.key.lane_id, lane_id); - total_messages += 1; - - // ensure that relayer has declared enough weight for dispatching next message - // on this lane. We can't dispatch lane messages out-of-order, so if declared - // weight is not enough, let's move to next lane - let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); - if message_dispatch_weight.any_gt(dispatch_weight_left) { - log::trace!( - target: LOG_TARGET, - "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", - lane_id, - message_dispatch_weight, - dispatch_weight_left, - ); - - fail!(Error::::InsufficientDispatchWeight); - } + if let Some(lane_state) = lane_data.lane_state { + let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); + if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { + log::trace!( + target: LOG_TARGET, + "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", + lane_id, + updated_latest_confirmed_nonce, + UnrewardedRelayersState::from(&lane.storage().data()), + ); + } + } - let receival_result = lane.receive_message::( - &relayer_id_at_bridged_chain, - message.key.nonce, - message.data, + let mut messages_received_status = + ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); + for mut message in lane_data.messages { + debug_assert_eq!(message.key.lane_id, lane_id); + total_messages += 1; + + // ensure that relayer has declared enough weight for dispatching next message + // on this lane. We can't dispatch lane messages out-of-order, so if declared + // weight is not enough, let's move to next lane + let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); + if message_dispatch_weight.any_gt(dispatch_weight_left) { + log::trace!( + target: LOG_TARGET, + "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", + lane_id, + message_dispatch_weight, + dispatch_weight_left, ); - // note that we're returning unspent weight to relayer even if message has been - // rejected by the lane. This allows relayers to submit spam transactions with - // e.g. the same set of already delivered messages over and over again, without - // losing funds for messages dispatch. But keep in mind that relayer pays base - // delivery transaction cost anyway. And base cost covers everything except - // dispatch, so we have a balance here. - let unspent_weight = match &receival_result { - ReceptionResult::Dispatched(dispatch_result) => { - valid_messages += 1; - dispatch_result.unspent_weight - }, - ReceptionResult::InvalidNonce | - ReceptionResult::TooManyUnrewardedRelayers | - ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, - }; - lane_messages_received_status.push(message.key.nonce, receival_result); - - let unspent_weight = unspent_weight.min(message_dispatch_weight); - dispatch_weight_left -= message_dispatch_weight - unspent_weight; - actual_weight = actual_weight.saturating_sub(unspent_weight); + fail!(Error::::InsufficientDispatchWeight); } - messages_received_status.push(lane_messages_received_status); + let receival_result = lane.receive_message::( + &relayer_id_at_bridged_chain, + message.key.nonce, + message.data, + ); + + // note that we're returning unspent weight to relayer even if message has been + // rejected by the lane. This allows relayers to submit spam transactions with + // e.g. the same set of already delivered messages over and over again, without + // losing funds for messages dispatch. But keep in mind that relayer pays base + // delivery transaction cost anyway. And base cost covers everything except + // dispatch, so we have a balance here. + let unspent_weight = match &receival_result { + ReceptionResult::Dispatched(dispatch_result) => { + valid_messages += 1; + dispatch_result.unspent_weight + }, + ReceptionResult::InvalidNonce | + ReceptionResult::TooManyUnrewardedRelayers | + ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, + }; + messages_received_status.push(message.key.nonce, receival_result); + + let unspent_weight = unspent_weight.min(message_dispatch_weight); + dispatch_weight_left -= message_dispatch_weight - unspent_weight; + actual_weight = actual_weight.saturating_sub(unspent_weight); } // let's now deal with relayer payments @@ -412,7 +373,7 @@ pub mod pallet { ); // mark messages as delivered - let mut lane = outbound_lane::(lane_id); + let mut lane = any_state_outbound_lane::(lane_id)?; let last_delivered_nonce = lane_data.last_delivered_nonce(); let confirmed_messages = lane .confirm_delivery( @@ -487,7 +448,7 @@ pub mod pallet { /// Messages have been received from the bridged chain. MessagesReceived( /// Result of received messages dispatch. - Vec::DispatchLevelResult>>, + ReceivedMessages<::DispatchLevelResult>, ), /// Messages in the inclusive range have been delivered to the bridged chain. MessagesDelivered { @@ -503,14 +464,10 @@ pub mod pallet { pub enum Error { /// Pallet is not in Normal operating mode. NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, - /// The inbound message dispatcher is inactive. - MessageDispatchInactive, + /// Error that is reported by the lanes manager. + LanesManager(LanesManagerError), /// Message has been treated as invalid by the pallet logic. MessageRejectedByPallet(VerificationError), - /// Submitter has failed to pay fee for delivering and dispatching messages. - FailedToWithdrawMessageFee, /// The transaction brings too many messages. TooManyMessagesInTheProof, /// Invalid messages has been submitted. @@ -523,8 +480,6 @@ pub mod pallet { /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch /// of all bundled messages. InsufficientDispatchWeight, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, /// Error confirming messages receival. ReceptionConfirmation(ReceptionConfirmationError), /// Error generated by the `OwnedBridgeModule` trait. @@ -549,10 +504,13 @@ pub mod pallet { pub type PalletOperatingMode, I: 'static = ()> = StorageValue<_, MessagesOperatingMode, ValueQuery>; + // TODO: https://github.com/paritytech/parity-bridges-common/pull/2213: let's limit number of + // possible opened lanes && use it to constraint maps below + /// Map of lane id => inbound lane data. #[pallet::storage] pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, OptionQuery>; /// Map of lane id => outbound lane data. #[pallet::storage] @@ -560,28 +518,7 @@ pub mod pallet { Hasher = Blake2_128Concat, Key = LaneId, Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// Map of lane id => is congested signal sent. It is managed by the - /// `bridge_runtime_common::LocalXcmQueueManager`. - /// - /// **bridges-v1**: this map is a temporary hack and will be dropped in the `v2`. We can emulate - /// a storage map using `sp_io::unhashed` storage functions, but then benchmarks are not - /// accounting its `proof_size`, so it is missing from the final weights. So we need to make it - /// a map inside some pallet. We could use a simply value instead of map here, because - /// in `v1` we'll only have a single lane. But in the case of adding another lane before `v2`, - /// it'll be easier to deal with the isolated storage map instead. - #[pallet::storage] - pub type OutboundLanesCongestedSignals, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = bool, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, + QueryKind = OptionQuery, >; /// All queued outbound messages. @@ -596,8 +533,11 @@ pub mod pallet { pub operating_mode: MessagesOperatingMode, /// Initial pallet owner. pub owner: Option, + /// Opened lanes. + pub opened_lanes: Vec, /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData, } #[pallet::genesis_build] @@ -607,6 +547,19 @@ 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()); + } + } + } + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() } } @@ -632,24 +585,67 @@ pub mod pallet { } /// Return outbound lane data. - pub fn outbound_lane_data(lane: LaneId) -> OutboundLaneData { + pub fn outbound_lane_data(lane: LaneId) -> Option { OutboundLanes::::get(lane) } /// Return inbound lane data. pub fn inbound_lane_data( lane: LaneId, - ) -> InboundLaneData>> { - InboundLanes::::get(lane).0 + ) -> Option>>> { + InboundLanes::::get(lane).map(|lane| lane.0) } } - /// Get-parameter that returns number of active outbound lanes that the pallet maintains. - pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); + #[cfg(any(feature = "try-runtime", test))] + impl, I: 'static> Pallet { + /// Ensure the correctness of the state of this pallet. + pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state_for_outbound_lanes() + } + + /// Ensure the correctness of the state of outbound lanes. + pub fn do_try_state_for_outbound_lanes() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_runtime::traits::One; + use sp_std::vec::Vec; + + // collect unpruned lanes + let mut unpruned_lanes = Vec::new(); + for (lane_id, lane_data) in OutboundLanes::::iter() { + let Some(expected_last_prunned_nonce) = + lane_data.oldest_unpruned_nonce.checked_sub(One::one()) + else { + continue; + }; + + // collect message_nonces that were supposed to be pruned + let mut unpruned_message_nonces = Vec::new(); + const MAX_MESSAGES_ITERATION: u64 = 16; + let start_nonce = + expected_last_prunned_nonce.checked_sub(MAX_MESSAGES_ITERATION).unwrap_or(0); + for current_nonce in start_nonce..=expected_last_prunned_nonce { + // check a message for current_nonce + if OutboundMessages::::contains_key(MessageKey { + lane_id, + nonce: current_nonce, + }) { + unpruned_message_nonces.push(current_nonce); + } + } + + if !unpruned_message_nonces.is_empty() { + log::warn!( + target: LOG_TARGET, + "do_try_state_for_outbound_lanes for lane_id: {lane_id:?} with lane_data: {lane_data:?} found unpruned_message_nonces: {unpruned_message_nonces:?}", + ); + unpruned_lanes.push((lane_id, lane_data, unpruned_message_nonces)); + } + } + + // ensure messages before `oldest_unpruned_nonce` are really pruned. + ensure!(unpruned_lanes.is_empty(), "Found unpruned lanes!"); - impl, I: 'static> Get> for MaybeOutboundLanesCount { - fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) + Ok(()) } } } @@ -659,6 +655,7 @@ pub mod pallet { #[derive(Debug, PartialEq, Eq)] pub struct SendMessageArgs, I: 'static> { lane_id: LaneId, + lane: OutboundLane>, payload: StoredMessagePayload, } @@ -671,16 +668,18 @@ where type SendMessageArgs = SendMessageArgs; fn validate_message( - lane: LaneId, + lane_id: LaneId, message: &T::OutboundPayload, ) -> Result, Self::Error> { + // we can't accept any messages if the pallet is halted ensure_normal_operating_mode::()?; - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::::InactiveOutboundLane); + // check lane + let lane = active_outbound_lane::(lane_id)?; Ok(SendMessageArgs { - lane_id: lane, + lane_id, + lane, payload: StoredMessagePayload::::try_from(message.encode()).map_err(|_| { Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge) })?, @@ -689,7 +688,7 @@ where fn send_message(args: SendMessageArgs) -> SendMessageArtifacts { // save message in outbound storage and emit event - let mut lane = outbound_lane::(args.lane_id); + let mut lane = args.lane; let message_len = args.payload.len(); let nonce = lane.send_message(args.payload); @@ -721,122 +720,31 @@ fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error< Err(Error::::NotOperatingNormally) } -/// Creates new inbound lane object, backed by runtime storage. -fn inbound_lane, I: 'static>( +/// Creates new inbound lane object, backed by runtime storage. Lane must be active. +fn active_inbound_lane, I: 'static>( lane_id: LaneId, -) -> InboundLane> { - InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)) +) -> Result>, Error> { + LanesManager::::new() + .active_inbound_lane(lane_id) + .map_err(Error::LanesManager) } -/// Creates new outbound lane object, backed by runtime storage. -fn outbound_lane, I: 'static>( - lane_id: LaneId, -) -> OutboundLane> { - OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) -} - -/// Runtime inbound lane storage. -struct RuntimeInboundLaneStorage, I: 'static = ()> { +/// Creates new outbound lane object, backed by runtime storage. Lane must be active. +fn active_outbound_lane, 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() } - } +) -> 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 { +/// Creates new outbound lane object, backed by runtime storage. +fn any_state_outbound_lane, I: 'static>( lane_id: LaneId, - _phantom: PhantomData<(T, I)>, -} - -impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { - type StoredMessagePayload = StoredMessagePayload; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(self.lane_id) - } - - fn set_data(&mut self, data: OutboundLaneData) { - OutboundLanes::::insert(self.lane_id, data) - } - - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option { - OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) - } - - fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { - OutboundMessages::::insert( - MessageKey { lane_id: self.lane_id, nonce }, - message_payload, - ); - } - - fn remove_message(&mut self, nonce: &MessageNonce) { - OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); - } +) -> Result>, Error> { + LanesManager::::new() + .any_state_outbound_lane(lane_id) + .map_err(Error::LanesManager) } /// Verify messages proof and return proved messages with decoded payload. @@ -847,18 +755,13 @@ fn verify_and_decode_messages_proof, I: 'static>( // `receive_messages_proof` weight formula and `MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` // check guarantees that the `message_count` is sane and Vec may be allocated. // (tx with too many messages will either be rejected from the pool, or will fail earlier) - proofs::verify_messages_proof::(proof, messages_count).map(|messages_by_lane| { - messages_by_lane - .into_iter() - .map(|(lane, lane_data)| { - ( - lane, - ProvedLaneMessages { - lane_state: lane_data.lane_state, - messages: lane_data.messages.into_iter().map(Into::into).collect(), - }, - ) - }) - .collect() + proofs::verify_messages_proof::(proof, messages_count).map(|(lane, lane_data)| { + ( + lane, + ProvedLaneMessages { + lane_state: lane_data.lane_state, + messages: lane_data.messages.into_iter().map(Into::into).collect(), + }, + ) }) } diff --git a/bridges/modules/messages/src/migration.rs b/bridges/modules/messages/src/migration.rs new file mode 100644 index 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 fcdddf199dc65b37dd014745b6a1630709ad8f8b..f71240ab7c703203d97294e1a0639059413f25cb 100644 --- a/bridges/modules/messages/src/outbound_lane.rs +++ b/bridges/modules/messages/src/outbound_lane.rs @@ -19,20 +19,18 @@ use crate::{Config, LOG_TARGET}; use bp_messages::{ - ChainWithMessages, DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + ChainWithMessages, DeliveredMessages, LaneId, LaneState, MessageNonce, OutboundLaneData, + UnrewardedRelayer, }; use codec::{Decode, Encode}; -use frame_support::{ - traits::Get, - weights::{RuntimeDbWeight, Weight}, - BoundedVec, PalletError, -}; +use frame_support::{traits::Get, BoundedVec, PalletError}; use scale_info::TypeInfo; -use sp_runtime::{traits::Zero, RuntimeDebug}; -use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData}; +use sp_runtime::RuntimeDebug; +use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; /// Outbound lane storage. pub trait OutboundLaneStorage { + /// Stored message payload type. type StoredMessagePayload; /// Lane id. @@ -48,6 +46,8 @@ pub trait OutboundLaneStorage { fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload); /// Remove outbound message from the storage. fn remove_message(&mut self, nonce: &MessageNonce); + /// Purge lane data from the storage. + fn purge(self); } /// Limit for the `StoredMessagePayload` vector. @@ -79,6 +79,7 @@ pub enum ReceptionConfirmationError { } /// Outbound messages lane. +#[derive(Debug, PartialEq, Eq)] pub struct OutboundLane { storage: S, } @@ -94,6 +95,24 @@ impl OutboundLane { self.storage.data() } + /// Get lane state. + pub fn state(&self) -> LaneState { + self.storage.data().state + } + + /// Set lane state. + pub fn set_state(&mut self, state: LaneState) { + let mut data = self.storage.data(); + data.state = state; + self.storage.set_data(data); + } + + /// Return nonces of all currently queued messages. + pub fn queued_messages(&self) -> RangeInclusive { + let data = self.storage.data(); + data.oldest_unpruned_nonce..=data.latest_generated_nonce + } + /// Send message over lane. /// /// Returns new message nonce. @@ -143,40 +162,29 @@ impl OutboundLane { ensure_unrewarded_relayers_are_correct(confirmed_messages.end, relayers)?; + // prune all confirmed messages + for nonce in confirmed_messages.begin..=confirmed_messages.end { + self.storage.remove_message(&nonce); + } + data.latest_received_nonce = confirmed_messages.end; + data.oldest_unpruned_nonce = data.latest_received_nonce.saturating_add(1); self.storage.set_data(data); Ok(Some(confirmed_messages)) } - /// Prune at most `max_messages_to_prune` already received messages. - /// - /// Returns weight, consumed by messages pruning and lane state update. - pub fn prune_messages( - &mut self, - db_weight: RuntimeDbWeight, - mut remaining_weight: Weight, - ) -> Weight { - let write_weight = db_weight.writes(1); - let two_writes_weight = write_weight + write_weight; - let mut spent_weight = Weight::zero(); + /// Remove message from the storage. Doesn't perform any checks. + pub fn remove_oldest_unpruned_message(&mut self) { let mut data = self.storage.data(); - while remaining_weight.all_gte(two_writes_weight) && - data.oldest_unpruned_nonce <= data.latest_received_nonce - { - self.storage.remove_message(&data.oldest_unpruned_nonce); - - spent_weight += write_weight; - remaining_weight -= write_weight; - data.oldest_unpruned_nonce += 1; - } - - if !spent_weight.is_zero() { - spent_weight += write_weight; - self.storage.set_data(data); - } + self.storage.remove_message(&data.oldest_unpruned_nonce); + data.oldest_unpruned_nonce += 1; + self.storage.set_data(data); + } - spent_weight + /// Purge lane state from the storage. + pub fn purge(self) { + self.storage.purge() } } @@ -215,13 +223,12 @@ 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 frame_support::weights::constants::RocksDbWeight; use sp_std::ops::RangeInclusive; fn unrewarded_relayers( @@ -241,7 +248,7 @@ mod tests { relayers: &VecDeque>, ) -> Result, ReceptionConfirmationError> { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -257,7 +264,7 @@ mod tests { #[test] fn send_message_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.storage.data().latest_generated_nonce, 0); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert!(lane.storage.message(&1).is_some()); @@ -268,7 +275,7 @@ mod tests { #[test] fn confirm_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -281,14 +288,14 @@ mod tests { ); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); }); } #[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); @@ -302,7 +309,7 @@ mod tests { ); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 2); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); assert_eq!( lane.confirm_delivery(3, 3, &unrewarded_relayers(3..=3)), @@ -310,14 +317,14 @@ mod tests { ); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); }); } #[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)); @@ -331,12 +338,12 @@ mod tests { assert_eq!(lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), Ok(None),); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); assert_eq!(lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), Ok(None),); assert_eq!(lane.storage.data().latest_generated_nonce, 3); assert_eq!(lane.storage.data().latest_received_nonce, 3); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); }); } @@ -394,61 +401,10 @@ mod tests { ); } - #[test] - fn prune_messages_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - // when lane is empty, nothing is pruned - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // when nothing is confirmed, nothing is pruned - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert!(lane.storage.message(&1).is_some()); - assert!(lane.storage.message(&2).is_some()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // after confirmation, some messages are received - assert_eq!( - lane.confirm_delivery(2, 2, &unrewarded_relayers(1..=2)), - Ok(Some(delivered_messages(1..=2))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(3), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); - // after last message is confirmed, everything is pruned - assert_eq!( - lane.confirm_delivery(1, 3, &unrewarded_relayers(3..=3)), - Ok(Some(delivered_messages(3..=3))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(2), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_none()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); - }); - } - #[test] fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { run_test(|| { - 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..f35eb24d98c5d7ad178c4d2e6791c98c044b7600 100644 --- a/bridges/modules/messages/src/proofs.rs +++ b/bridges/modules/messages/src/proofs.rs @@ -98,11 +98,7 @@ pub fn verify_messages_proof, I: 'static>( // Check that the storage proof doesn't have any untouched keys. parser.ensure_no_unused_keys().map_err(VerificationError::StorageProof)?; - // We only support single lane messages in this generated_schema - let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane, proved_lane_messages); - - Ok(proved_messages) + Ok((lane, proved_lane_messages)) } /// Verify proof of This -> Bridged chain messages delivery. @@ -220,7 +216,8 @@ mod tests { mock::*, }; - use bp_header_chain::StoredHeaderDataBuilder; + use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder}; + use bp_messages::LaneState; use bp_runtime::{HeaderId, StorageProofError}; use codec::Encode; use sp_runtime::traits::Header; @@ -235,7 +232,7 @@ mod tests { test: impl Fn(FromBridgedChainMessagesProof) -> R, ) -> R { let (state_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, + test_lane_id(), 1..=nonces_end, outbound_lane_data, bp_runtime::UnverifiedStorageProofParams::default(), @@ -267,7 +264,7 @@ mod tests { test(FromBridgedChainMessagesProof { bridged_header_hash, storage_proof, - lane: TEST_LANE_ID, + lane: test_lane_id(), nonces_start: 1, nonces_end, }) @@ -440,6 +437,7 @@ mod tests { using_messages_proof( 10, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -480,6 +478,7 @@ mod tests { using_messages_proof( 0, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -490,19 +489,18 @@ mod tests { false, |proof| verify_messages_proof::(proof, 0), ), - Ok(vec![( - TEST_LANE_ID, + Ok(( + test_lane_id(), ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, }), messages: Vec::new(), }, - )] - .into_iter() - .collect()), + )), ); } @@ -512,6 +510,7 @@ mod tests { using_messages_proof( 1, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -522,22 +521,21 @@ mod tests { false, |proof| verify_messages_proof::(proof, 1), ), - Ok(vec![( - TEST_LANE_ID, + Ok(( + test_lane_id(), ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, }), messages: vec![Message { - key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, + key: MessageKey { lane_id: test_lane_id(), nonce: 1 }, payload: vec![42], }], }, - )] - .into_iter() - .collect()), + )) ); } diff --git a/bridges/modules/messages/src/tests/mock.rs b/bridges/modules/messages/src/tests/mock.rs index 99da019dc0847d0787c30ada8e614270e83ddfdb..2caea9813e8277c6989e8815aae280cde993c33c 100644 --- a/bridges/modules/messages/src/tests/mock.rs +++ b/bridges/modules/messages/src/tests/mock.rs @@ -35,7 +35,7 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof, MessageDispatch, }, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, LaneState, Message, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{ @@ -43,7 +43,7 @@ use bp_runtime::{ }; use codec::{Decode, Encode}; use frame_support::{ - derive_impl, parameter_types, + derive_impl, weights::{constants::RocksDbWeight, Weight}, }; use scale_info::TypeInfo; @@ -183,12 +183,6 @@ impl pallet_bridge_grandpa::Config for TestRuntime { type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } -parameter_types! { - pub const MaxMessagesToPruneAtOnce: u64 = 10; - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; -} - /// weights of messages pallet calls we use in tests. pub type TestWeightInfo = (); @@ -200,8 +194,6 @@ impl Config for TestRuntime { type BridgedChain = BridgedChain; type BridgedHeaderChain = BridgedChainGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = TestPayload; type InboundPayload = TestPayload; @@ -216,7 +208,7 @@ impl Config for TestRuntime { #[cfg(feature = "runtime-benchmarks")] impl crate::benchmarking::Config<()> for TestRuntime { fn bench_lane_id() -> LaneId { - TEST_LANE_ID + test_lane_id() } fn prepare_message_proof( @@ -267,13 +259,19 @@ pub const TEST_RELAYER_B: AccountId = 101; pub const TEST_RELAYER_C: AccountId = 102; /// Lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); +pub fn test_lane_id() -> LaneId { + LaneId::new(1, 2) +} -/// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); +/// Lane that is completely unknown to our runtime. +pub fn unknown_lane_id() -> LaneId { + LaneId::new(1, 3) +} -/// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); +/// Lane that is registered, but it is closed. +pub fn closed_lane_id() -> LaneId { + LaneId::new(1, 4) +} /// Regular message payload. pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); @@ -343,8 +341,18 @@ impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayment pub struct TestMessageDispatch; impl TestMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(b"TestMessageDispatch.IsCongested", &true) + pub fn deactivate(lane: LaneId) { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1; + for _ in 1..=latest_received_nonce { + Self::emulate_enqueued_message(lane); + } + } + + pub fn emulate_enqueued_message(lane: LaneId) { + let key = (b"dispatched", lane).encode(); + let dispatched = frame_support::storage::unhashed::get_or_default::(&key[..]); + frame_support::storage::unhashed::put(&key[..], &(dispatched + 1)); } } @@ -352,10 +360,10 @@ impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; type DispatchLevelResult = TestDispatchLevelResult; - fn is_active() -> bool { - !frame_support::storage::unhashed::get_or_default::( - b"TestMessageDispatch.IsCongested", - ) + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::get_or_default::( + &(b"dispatched", lane).encode()[..], + ) <= BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX } fn dispatch_weight(message: &mut DispatchMessage) -> Weight { @@ -369,7 +377,10 @@ impl MessageDispatch for TestMessageDispatch { message: DispatchMessage, ) -> MessageDispatchResult { match message.data.payload.as_ref() { - Ok(payload) => payload.dispatch_result.clone(), + Ok(payload) => { + Self::emulate_enqueued_message(message.key.lane_id); + payload.dispatch_result.clone() + }, Err(_) => dispatch_result(0), } } @@ -395,7 +406,7 @@ impl OnMessagesDelivered for TestOnMessagesDelivered { /// Return test lane message with given nonce and payload. pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { - Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } + Message { key: MessageKey { lane_id: test_lane_id(), nonce }, payload: payload.encode() } } /// Return valid outbound message data, constructed from given payload. @@ -439,7 +450,7 @@ pub fn unrewarded_relayer( /// Returns unrewarded relayers state at given lane. pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { - let inbound_lane_data = crate::InboundLanes::::get(lane).0; + let inbound_lane_data = crate::InboundLanes::::get(lane).unwrap().0; UnrewardedRelayersState::from(&inbound_lane_data) } @@ -454,7 +465,19 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + crate::InboundLanes::::insert(test_lane_id(), InboundLaneData::opened()); + crate::OutboundLanes::::insert(test_lane_id(), OutboundLaneData::opened()); + crate::InboundLanes::::insert( + closed_lane_id(), + InboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + crate::OutboundLanes::::insert( + closed_lane_id(), + OutboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + test() + }) } /// Prepare valid storage proof for given messages and insert appropriate header to the @@ -471,7 +494,7 @@ pub fn prepare_messages_proof( let nonces_start = messages.first().unwrap().key.nonce; let nonces_end = messages.last().unwrap().key.nonce; let (storage_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, + lane, nonces_start..=nonces_end, outbound_lane_data, UnverifiedStorageProofParams::default(), diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs index 42e1042717de07656630855c00967361be78f664..ceb1744c066588cf41ac6db429cc6bb8503dc7f1 100644 --- a/bridges/modules/messages/src/tests/pallet_tests.rs +++ b/bridges/modules/messages/src/tests/pallet_tests.rs @@ -17,20 +17,20 @@ //! Pallet-level tests. use crate::{ - outbound_lane, + active_outbound_lane, + lanes_manager::RuntimeInboundLaneStorage, outbound_lane::ReceptionConfirmationError, - tests::mock::{self, RuntimeEvent as TestEvent, *}, + tests::mock::{RuntimeEvent as TestEvent, *}, weights_ext::WeightInfoExt, - Call, Config, Error, Event, InboundLanes, MaybeOutboundLanesCount, OutboundLanes, - OutboundMessages, Pallet, PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage, - StoredInboundLaneData, + Call, Config, Error, Event, InboundLanes, LanesManagerError, OutboundLanes, OutboundMessages, + Pallet, PalletOperatingMode, PalletOwner, StoredInboundLaneData, }; use bp_messages::{ source_chain::{FromBridgedChainMessagesDeliveryProof, MessagesBridge}, - target_chain::FromBridgedChainMessagesProof, + target_chain::{FromBridgedChainMessagesProof, MessageDispatch}, BridgeMessagesCall, ChainWithMessages, DeliveredMessages, InboundLaneData, - InboundMessageDetails, LaneId, MessageKey, MessageNonce, MessagesOperatingMode, + InboundMessageDetails, LaneId, LaneState, MessageKey, MessageNonce, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState, VerificationError, }; @@ -38,15 +38,13 @@ use bp_runtime::{BasicOperatingMode, PreComputedSize, RangeInclusiveExt, Size}; use bp_test_utils::generate_owned_bridge_module_tests; use codec::Encode; use frame_support::{ - assert_noop, assert_ok, + assert_err, assert_noop, assert_ok, dispatch::Pays, storage::generator::{StorageMap, StorageValue}, - traits::Hooks, weights::Weight, }; use frame_system::{EventRecord, Pallet as System, Phase}; -use sp_core::Get; -use sp_runtime::DispatchError; +use sp_runtime::{BoundedVec, DispatchError}; fn get_ready_for_events() { System::::set_block_number(1); @@ -56,7 +54,7 @@ fn get_ready_for_events() { fn send_regular_message(lane_id: LaneId) { get_ready_for_events(); - let outbound_lane = outbound_lane::(lane_id); + let outbound_lane = active_outbound_lane::(lane_id).unwrap(); let message_nonce = outbound_lane.data().latest_generated_nonce + 1; let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) @@ -82,8 +80,9 @@ fn receive_messages_delivery_proof() { assert_ok!(Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -99,13 +98,14 @@ fn receive_messages_delivery_proof() { last_delivered_nonce: 1, }, )); + assert_ok!(Pallet::::do_try_state()); assert_eq!( System::::events(), vec![EventRecord { phase: Phase::Initialization, event: TestEvent::Messages(Event::MessagesDelivered { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), messages: DeliveredMessages::new(1), }), topics: vec![], @@ -117,14 +117,14 @@ fn receive_messages_delivery_proof() { fn pallet_rejects_transactions_if_halted() { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); PalletOperatingMode::::put(MessagesOperatingMode::Basic( BasicOperatingMode::Halted, )); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), + Pallet::::validate_message(test_lane_id(), ®ULAR_PAYLOAD), Error::::NotOperatingNormally, ); @@ -141,8 +141,9 @@ fn pallet_rejects_transactions_if_halted() { ); let delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -160,13 +161,14 @@ fn pallet_rejects_transactions_if_halted() { ), Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), ); + assert_ok!(Pallet::::do_try_state()); }); } #[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( @@ -176,7 +178,7 @@ fn receive_messages_fails_if_dispatcher_is_inactive() { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::::MessageDispatchInactive, + Error::::LanesManager(LanesManagerError::LaneDispatcherInactive), ); }); } @@ -185,14 +187,14 @@ fn receive_messages_fails_if_dispatcher_is_inactive() { fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); PalletOperatingMode::::put( MessagesOperatingMode::RejectingOutboundMessages, ); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), + Pallet::::validate_message(test_lane_id(), ®ULAR_PAYLOAD), Error::::NotOperatingNormally, ); @@ -207,8 +209,9 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { assert_ok!(Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -220,13 +223,14 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { last_delivered_nonce: 1, }, )); + assert_ok!(Pallet::::do_try_state()); }); } #[test] fn send_message_works() { run_test(|| { - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); }); } @@ -241,7 +245,7 @@ fn send_message_rejects_too_large_message() { .extra .extend_from_slice(&vec![0u8; max_outbound_payload_size as usize]); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, &message_payload.clone(),), + Pallet::::validate_message(test_lane_id(), &message_payload.clone(),), Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge), ); @@ -252,7 +256,7 @@ fn send_message_rejects_too_large_message() { assert_eq!(message_payload.encoded_size() as u32, max_outbound_payload_size); let valid_message = - Pallet::::validate_message(TEST_LANE_ID, &message_payload) + Pallet::::validate_message(test_lane_id(), &message_payload) .expect("validate_message has failed"); Pallet::::send_message(valid_message); }) @@ -269,7 +273,13 @@ fn receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + assert_eq!( + InboundLanes::::get(test_lane_id()) + .unwrap() + .0 + .last_delivered_nonce(), + 1 + ); assert!(TestDeliveryPayments::is_reward_paid(1)); }); @@ -280,8 +290,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { run_test(|| { // say we have received 10 messages && last confirmed message is 8 InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 8, relayers: vec![ unrewarded_relayer(9, 9, TEST_RELAYER_A), @@ -291,7 +302,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }, ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -313,8 +324,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { )); assert_eq!( - InboundLanes::::get(TEST_LANE_ID).0, + InboundLanes::::get(test_lane_id()).unwrap().0, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 9, relayers: vec![ unrewarded_relayer(10, 10, TEST_RELAYER_B), @@ -324,7 +336,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }, ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -335,6 +347,86 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }); } +#[test] +fn receive_messages_proof_fails_when_dispatcher_is_inactive() { + run_test(|| { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1; + for _ in 1..=latest_received_nonce { + TestMessageDispatch::emulate_enqueued_message(test_lane_id()); + } + assert!(!TestMessageDispatch::is_active(test_lane_id())); + InboundLanes::::insert( + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: latest_received_nonce, + relayers: vec![].into(), + }, + ); + + // try to delvier next message - it should fail because dispatcher is in "suspended" state + // at the beginning of the call + let messages_proof = + prepare_messages_proof(vec![message(latest_received_nonce + 1, REGULAR_PAYLOAD)], None); + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + messages_proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::LaneDispatcherInactive) + ); + assert!(!TestMessageDispatch::is_active(test_lane_id())); + }); +} + +#[test] +fn receive_messages_succeeds_when_dispatcher_becomes_inactive_in_the_middle_of_transaction() { + run_test(|| { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX / 2; + for _ in 1..=latest_received_nonce { + TestMessageDispatch::emulate_enqueued_message(test_lane_id()); + } + assert!(TestMessageDispatch::is_active(test_lane_id())); + InboundLanes::::insert( + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: latest_received_nonce, + relayers: vec![].into(), + }, + ); + + // try to delvier next `BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` messages + // - it will lead to dispatcher deactivation, but the transaction shall not fail and all + // messages must be delivered + let messages_begin = latest_received_nonce + 1; + let messages_end = + messages_begin + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + let messages_range = messages_begin..messages_end; + let messages_count = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + prepare_messages_proof( + messages_range.map(|nonce| message(nonce, REGULAR_PAYLOAD)).collect(), + None, + ), + messages_count as _, + REGULAR_PAYLOAD.declared_weight * messages_count, + ),); + assert_eq!( + inbound_unrewarded_relayers_state(test_lane_id()).last_delivered_nonce, + messages_end - 1, + ); + assert!(!TestMessageDispatch::is_active(test_lane_id())); + }); +} + #[test] fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { run_test(|| { @@ -352,7 +444,10 @@ fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enou ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -395,22 +490,64 @@ fn receive_messages_proof_rejects_proof_with_too_many_messages() { #[test] fn receive_messages_delivery_proof_works() { run_test(|| { - send_regular_message(TEST_LANE_ID); + 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).latest_received_nonce, 1,); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 1, + ); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .oldest_unpruned_nonce, + 2, + ); + }); +} + +#[test] +fn receive_messages_delivery_proof_works_on_closed_outbound_lanes() { + run_test(|| { + send_regular_message(test_lane_id()); + active_outbound_lane::(test_lane_id()) + .unwrap() + .set_state(LaneState::Closed); + receive_messages_delivery_proof(); + + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 1, + ); }); } #[test] fn receive_messages_delivery_proof_rewards_relayers() { run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); + send_regular_message(test_lane_id()); // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A let single_message_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), ..Default::default() @@ -428,6 +565,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { }, ); assert_ok!(result); + assert_ok!(Pallet::::do_try_state()); assert_eq!( result.unwrap().actual_weight.unwrap(), TestWeightInfo::receive_messages_delivery_proof_weight( @@ -445,7 +583,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { // this reports delivery of both message 1 and message 2 => reward is paid only to // TEST_RELAYER_B let two_messages_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -467,6 +605,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { }, ); assert_ok!(result); + assert_ok!(Pallet::::do_try_state()); // even though the pre-dispatch weight was for two messages, the actual weight is // for single message only assert_eq!( @@ -482,15 +621,15 @@ fn receive_messages_delivery_proof_rewards_relayers() { ); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0))); + assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((test_lane_id(), 0))); }); } #[test] fn receive_messages_delivery_proof_rejects_invalid_proof() { run_test(|| { - let mut proof = prepare_messages_delivery_proof(TEST_LANE_ID, Default::default()); - proof.lane = bp_messages::LaneId([42, 42, 42, 42]); + let mut proof = prepare_messages_delivery_proof(test_lane_id(), Default::default()); + proof.lane = bp_messages::LaneId::new(42, 84); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -508,7 +647,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i run_test(|| { // when number of relayers entries is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -534,7 +673,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i // when number of messages is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -560,7 +699,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i // when last delivered nonce is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -601,7 +740,10 @@ fn receive_messages_accepts_single_message_with_invalid_payload() { * improperly encoded) */ ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 1, + ); }); } @@ -622,7 +764,10 @@ fn receive_messages_accepts_batch_with_message_with_invalid_payload() { REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 3, + ); }); } @@ -645,7 +790,10 @@ fn actual_dispatch_weight_does_not_overflow() { ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -722,8 +870,9 @@ fn proof_size_refund_from_receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, ); InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -750,8 +899,9 @@ fn proof_size_refund_from_receive_messages_proof_works() { // if count of unrewarded relayer entries is less than maximal, then some `proof_size` // must be refunded InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -787,7 +937,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` returns @@ -796,8 +946,12 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and numer of // actually confirmed messages is `1`. let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, - InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: Default::default(), + }, ); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -821,20 +975,20 @@ fn storage_keys_computed_properly() { assert_eq!( OutboundMessages::::storage_map_final_key(MessageKey { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), nonce: 42 }), - bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, + bp_messages::storage_keys::message_key("Messages", &test_lane_id(), 42).0, ); assert_eq!( - OutboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, + OutboundLanes::::storage_map_final_key(test_lane_id()), + bp_messages::storage_keys::outbound_lane_data_key("Messages", &test_lane_id()).0, ); assert_eq!( - InboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, + InboundLanes::::storage_map_final_key(test_lane_id()), + bp_messages::storage_keys::inbound_lane_data_key("Messages", &test_lane_id()).0, ); } @@ -843,7 +997,7 @@ fn inbound_message_details_works() { run_test(|| { assert_eq!( Pallet::::inbound_message_data( - TEST_LANE_ID, + test_lane_id(), REGULAR_PAYLOAD.encode(), OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, ), @@ -852,147 +1006,15 @@ fn inbound_message_details_works() { }); } -#[test] -fn on_idle_callback_respects_remaining_weight() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - prepare_messages_delivery_proof( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 4, - relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)].into(), - }, - ), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 4, - total_messages: 4, - last_delivered_nonce: 4, - }, - )); - - // all 4 messages may be pruned now - assert_eq!(outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, 4); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 1); - System::::set_block_number(2); - - // if passed wight is too low to do anything - let dbw = DbWeight::get(); - assert_eq!(Pallet::::on_idle(0, dbw.reads_writes(1, 1)), Weight::zero(),); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 1); - - // if passed wight is enough to prune single message - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 2)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 2); - - // if passed wight is enough to prune two more messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 3)), - dbw.reads_writes(1, 3), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 4); - - // if passed wight is enough to prune many messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 5); - }); -} - -#[test] -fn on_idle_callback_is_rotating_lanes_to_prune() { - run_test(|| { - // send + receive confirmation for lane 1 - send_regular_message(TEST_LANE_ID); - receive_messages_delivery_proof(); - // send + receive confirmation for lane 2 - send_regular_message(TEST_LANE_ID_2); - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - prepare_messages_delivery_proof( - TEST_LANE_ID_2, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), - }, - ), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - - // nothing is pruned yet - assert_eq!(outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, 1); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 1); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#2.on_idle lane messages of lane 1 are pruned - let dbw = DbWeight::get(); - System::::set_block_number(2); - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 2); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#3.on_idle lane messages of lane 2 are pruned - System::::set_block_number(3); - - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!(outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, 2); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 2 - ); - }); -} - -#[test] -fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - 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, @@ -1062,12 +1084,12 @@ fn inbound_storage_extra_proof_size_bytes_works() { fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { RuntimeInboundLaneStorage { - lane_id: Default::default(), - cached_data: Some(InboundLaneData { + lane_id: LaneId::new(1, 2), + cached_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![relayer_entry(); relayer_entries].into(), last_confirmed_nonce: 0, - }), - _phantom: Default::default(), + }, } } @@ -1092,9 +1114,116 @@ fn inbound_storage_extra_proof_size_bytes_works() { } #[test] -fn maybe_outbound_lanes_count_returns_correct_value() { - assert_eq!( - MaybeOutboundLanesCount::::get(), - Some(mock::ActiveOutboundLanes::get().len() as u32) - ); +fn send_messages_fails_if_outbound_lane_is_not_opened() { + run_test(|| { + assert_noop!( + Pallet::::validate_message(unknown_lane_id(), ®ULAR_PAYLOAD), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + + assert_noop!( + Pallet::::validate_message(closed_lane_id(), ®ULAR_PAYLOAD), + Error::::LanesManager(LanesManagerError::ClosedOutboundLane), + ); + }); +} + +#[test] +fn receive_messages_proof_fails_if_inbound_lane_is_not_opened() { + run_test(|| { + let mut message = message(1, REGULAR_PAYLOAD); + message.key.lane_id = unknown_lane_id(); + let proof = prepare_messages_proof(vec![message.clone()], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + + message.key.lane_id = closed_lane_id(); + let proof = prepare_messages_proof(vec![message], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::ClosedInboundLane), + ); + }); +} + +#[test] +fn receive_messages_delivery_proof_fails_if_outbound_lane_is_unknown() { + run_test(|| { + let make_proof = |lane: LaneId| { + prepare_messages_delivery_proof( + lane, + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into(), + }, + ) + }; + + let proof = make_proof(unknown_lane_id()); + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); +} + +#[test] +fn do_try_state_for_outbound_lanes_works() { + run_test(|| { + let lane_id = test_lane_id(); + + // setup delivered nonce 1 + OutboundLanes::::insert( + lane_id, + OutboundLaneData { + state: LaneState::Opened, + oldest_unpruned_nonce: 2, + latest_received_nonce: 1, + latest_generated_nonce: 0, + }, + ); + // store message for nonce 1 + OutboundMessages::::insert( + MessageKey { lane_id, nonce: 1 }, + BoundedVec::default(), + ); + assert_err!( + Pallet::::do_try_state(), + sp_runtime::TryRuntimeError::Other("Found unpruned lanes!") + ); + + // remove message for nonce 1 + OutboundMessages::::remove(MessageKey { lane_id, nonce: 1 }); + assert_ok!(Pallet::::do_try_state()); + }) } diff --git a/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..8a3f905a8f291f98ee5832da8817c8e9dd5df1d9 100644 --- a/bridges/modules/relayers/src/benchmarking.rs +++ b/bridges/modules/relayers/src/benchmarking.rs @@ -43,7 +43,7 @@ pub trait Config: crate::Config { benchmarks! { // Benchmark `claim_rewards` call. claim_rewards { - let lane = LaneId([0, 0, 0, 0]); + let lane = LaneId::new(1, 2); let account_params = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); let relayer: T::AccountId = whitelisted_caller(); @@ -102,7 +102,7 @@ benchmarks! { crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); // create slash destination account - let lane = LaneId([0, 0, 0, 0]); + let lane = LaneId::new(1, 2); let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); T::prepare_rewards_account(slash_destination, Zero::zero()); }: { @@ -116,7 +116,7 @@ benchmarks! { // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension // is deployed at runtime level. register_relayer_reward { - let lane = LaneId([0, 0, 0, 0]); + let lane = LaneId::new(1, 2); let relayer: T::AccountId = whitelisted_caller(); let account_params = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); diff --git a/bridges/modules/relayers/src/extension/grandpa_adapter.rs b/bridges/modules/relayers/src/extension/grandpa_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..6c9ae1c2968c0a3f63db463d720ccdcfe6a356a3 --- /dev/null +++ b/bridges/modules/relayers/src/extension/grandpa_adapter.rs @@ -0,0 +1,177 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote GRANDPA chain. + +use crate::{ + extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Chain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, + SubmitFinalityProofHelper, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, + Saturating, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote +/// chains that are using GRANDPA finality. +pub struct WithGrandpaChainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeGrandpaConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-grandpa`, tracked by this extension + BridgeGrandpaPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeGrandpaPalletInstance, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig + for WithGrandpaChainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig> + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + GI: 'static, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = pallet_bridge_grandpa::BridgedBlockNumber; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = BCU::unpack(call, 2); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + + Ok(match (total_calls, relay_finality_call, msgs_call) { + (2, Some(relay_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), + (1, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the GRANDPA chain state update call, verify that it +/// has been successful. +/// +/// Only returns false when GRANDPA chain state update call has failed. +pub(crate) fn verify_submit_finality_proof_succeeded( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + GI: 'static, + C::Runtime: BridgeGrandpaConfig, + >::BridgedChain: + Chain, +{ + let Some(finality_proof_info) = call_info.submit_finality_proof_info() else { return true }; + + if !SubmitFinalityProofHelper::::was_successful( + finality_proof_info.block_number, + ) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid GRANDPA chain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` + // transaction. If relay chain header is mandatory, the GRANDPA pallet returns + // `Pays::No`, because such transaction is mandatory for operating the bridge. But + // `utility.batchAll` transaction always requires payment. But in both cases we'll + // refund relayer - either explicitly here, or using `Pays::No` if he's choosing + // to submit dedicated transaction. + + // submitter has means to include extra weight/bytes in the `submit_finality_proof` + // call, so let's subtract extra weight/size to avoid refunding for this extra stuff + call_data.extra_weight.saturating_accrue(finality_proof_info.extra_weight); + call_data.extra_size.saturating_accrue(finality_proof_info.extra_size); + + true +} diff --git a/bridges/modules/relayers/src/extension/messages_adapter.rs b/bridges/modules/relayers/src/extension/messages_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..ecb575524bb020db2e3a31383bf890b4418aaa5a --- /dev/null +++ b/bridges/modules/relayers/src/extension/messages_adapter.rs @@ -0,0 +1,94 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with any remote chain. This adapter does not refund any finality transactions. + +use crate::{extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig}; + +use bp_relayers::{ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::StaticStrProvider; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation +/// transactions. Finality transactions are not refunded. +pub struct WithMessagesExtensionConfig< + IdProvider, + Runtime, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, +>( + PhantomData<( + // signed extension identifier + IdProvider, + // runtime with `pallet-bridge-messages` pallet deployed + Runtime, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig for WithMessagesExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + BridgeMessagesConfig, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = (); + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let call = Self::check_obsolete_parsed_call(call)?; + Ok(call.call_info().map(ExtensionCallInfo::Msgs)) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/modules/relayers/src/extension/mod.rs similarity index 60% rename from bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs rename to bridges/modules/relayers/src/extension/mod.rs index 6ba3506377d0e602bf9ee706b13c248efd6afaca..e1a7abd0ad1c207ac71e60f87074a826e0b6dd0b 100644 --- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs +++ b/bridges/modules/relayers/src/extension/mod.rs @@ -14,207 +14,76 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Signed extension that refunds relayer if he has delivered some new messages. -//! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new message and all necessary underlying headers -//! (parachain or relay chain). - -use crate::messages_call_ext::{ - CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, +//! Signed extension, built around `pallet-bridge-relayers`. It is able to: +//! +//! - refund the cost of successful message delivery and confirmation transactions to the submitter +//! by registering corresponding reward in the pallet; +//! +//! - bump priority of messages delivery and confirmation transactions, signed by the registered +//! relayers. + +use crate::{Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt, LOG_TARGET}; + +use bp_messages::{ChainWithMessages, MessageNonce}; +use bp_relayers::{ + ExplicitOrAccountParams, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RewardsAccountOwner, RewardsAccountParams, }; -use bp_messages::{ChainWithMessages, LaneId, MessageNonce}; -use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::{Chain, Parachain, RangeInclusiveExt, StaticStrProvider}; -use codec::{Codec, Decode, Encode}; +use bp_runtime::{Chain, RangeInclusiveExt, StaticStrProvider}; +use codec::{Decode, Encode}; use frame_support::{ - dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, - traits::IsSubType, - weights::Weight, + dispatch::{DispatchInfo, PostDispatchInfo}, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; -use pallet_bridge_grandpa::{ - CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo, -}; -use pallet_bridge_messages::Config as MessagesConfig; -use pallet_bridge_parachains::{ - BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, - RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{CallHelper as MessagesCallHelper, Config as BridgeMessagesConfig}; +use pallet_transaction_payment::{ + Config as TransactionPaymentConfig, OnChargeTransaction, Pallet as TransactionPaymentPallet, }; -use pallet_bridge_relayers::{ - Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, -}; -use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; -use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, SignedExtension, Zero}, + traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + TransactionValidity, TransactionValidityError, ValidTransactionBuilder, }, - DispatchResult, FixedPointOperand, RuntimeDebug, + DispatchResult, RuntimeDebug, }; -use sp_std::{marker::PhantomData, vec, vec::Vec}; - -type AccountIdOf = ::AccountId; -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages -/// coming from this parachain. -pub trait RefundableParachainId { - /// The instance of the bridge parachains pallet. - type Instance: 'static; - /// The parachain Id. - type BridgedChain: Parachain; -} - -/// Implementation of `RefundableParachainId` for `trait Parachain`. -pub struct RefundableParachain(PhantomData<(Instance, Para)>); - -impl RefundableParachainId for RefundableParachain -where - Instance: 'static, - Para: Parachain, -{ - type Instance = Instance; - type BridgedChain = Para; -} - -/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages -/// coming from this lane. -pub trait RefundableMessagesLaneId { - /// The instance of the bridge messages pallet. - type Instance: 'static; - /// The messages lane id. - type Id: Get; -} +use sp_std::{fmt::Debug, marker::PhantomData}; -/// Default implementation of `RefundableMessagesLaneId`. -pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); +pub use grandpa_adapter::WithGrandpaChainExtensionConfig; +pub use messages_adapter::WithMessagesExtensionConfig; +pub use parachain_adapter::WithParachainExtensionConfig; +pub use priority::*; -impl RefundableMessagesLaneId for RefundableMessagesLane -where - Instance: 'static, - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Refund calculator. -pub trait RefundCalculator { - /// The underlying integer type in which the refund is calculated. - type Balance; - - /// Compute refund for given transaction. - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: Self::Balance, - ) -> Self::Balance; -} - -/// `RefundCalculator` implementation which refunds the actual transaction fee. -pub struct ActualFeeRefund(PhantomData); - -impl RefundCalculator for ActualFeeRefund -where - R: TransactionPaymentConfig, - CallOf: Dispatchable, - BalanceOf: FixedPointOperand, -{ - type Balance = BalanceOf; - - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: BalanceOf, - ) -> BalanceOf { - pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) - } -} +mod grandpa_adapter; +mod messages_adapter; +mod parachain_adapter; +mod priority; /// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. #[cfg_attr(test, derive(Debug, PartialEq))] -pub struct PreDispatchData { +pub struct PreDispatchData { /// Transaction submitter (relayer) account. relayer: AccountId, /// Type of the call. - call_info: CallInfo, -} - -/// Type of the call that the extension recognizes. -#[derive(RuntimeDebugNoBound, PartialEq)] -pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery/confirmation calls. - AllFinalityAndMsgs( - SubmitFinalityProofInfo, - SubmitParachainHeadsInfo, - MessagesCallInfo, - ), - /// Relay chain finality + message delivery/confirmation calls. - RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), - /// Parachain finality + message delivery/confirmation calls. - /// - /// This variant is used only when bridging with parachain. - ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), - /// Standalone message delivery/confirmation call. - Msgs(MessagesCallInfo), + call_info: ExtensionCallInfo, } -impl CallInfo { - /// Returns true if call is a message delivery call (with optional finality calls). - fn is_receive_messages_proof_call(&self) -> bool { - match self.messages_call_info() { - MessagesCallInfo::ReceiveMessagesProof(_) => true, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, - } - } - - /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option> { - match *self { - Self::AllFinalityAndMsgs(info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - +impl + PreDispatchData +{ /// Returns mutable reference to pre-dispatch `finality_target` sent to the /// `SubmitFinalityProof` call. #[cfg(test)] - fn submit_finality_proof_info_mut( + pub fn submit_finality_proof_info_mut( &mut self, - ) -> Option<&mut SubmitFinalityProofInfo> { - match *self { - Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info), + ) -> Option<&mut bp_header_chain::SubmitFinalityProofInfo> { + match self.call_info { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), + ExtensionCallInfo::RelayFinalityAndMsgs(ref mut info, _) => Some(info), _ => None, } } - - /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. - fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { - match self { - Self::AllFinalityAndMsgs(_, info, _) => Some(info), - Self::ParachainFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn messages_call_info(&self) -> &MessagesCallInfo { - match self { - Self::AllFinalityAndMsgs(_, _, info) => info, - Self::RelayFinalityAndMsgs(_, info) => info, - Self::ParachainFinalityAndMsgs(_, info) => info, - Self::Msgs(info) => info, - } - } } /// The actions on relayer account that need to be performed because of his actions. @@ -228,78 +97,93 @@ pub enum RelayerAccountAction { Slash(AccountId, RewardsAccountParams), } -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: - 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo +/// A signed extension, built around `pallet-bridge-relayers`. +/// +/// It may be incorporated into runtime to refund relayers for submitting correct +/// message delivery and confirmation transactions, optionally batched with required +/// finality proofs. +#[derive( + DefaultNoBound, + CloneNoBound, + Decode, + Encode, + EqNoBound, + PartialEqNoBound, + RuntimeDebugNoBound, + TypeInfo, +)] +#[scale_info(skip_type_params(Runtime, Config))] +pub struct BridgeRelayersSignedExtension(PhantomData<(Runtime, Config)>); + +impl BridgeRelayersSignedExtension +where + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, { - /// This chain runtime. - type Runtime: MessagesConfig<::Instance> - + RelayersConfig; - /// Messages pallet and lane reference. - type Msgs: RefundableMessagesLaneId; - /// Refund amount calculator. - type Refund: RefundCalculator::Reward>; - /// Priority boost calculator. - type Priority: Get; - /// Signed extension unique identifier. - type Id: StaticStrProvider; - - /// Unpack batch runtime call. - fn expand_call(call: &CallOf) -> Vec<&CallOf>; - - /// Given runtime call, check if it has supported format. Additionally, check if any of - /// (optionally batched) calls are obsolete and we shall reject the transaction. - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError>; - - /// Check if parsed call is already obsolete. - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError>; - - /// Called from post-dispatch and shall perform additional checks (apart from messages - /// transaction success) of given call result. - fn additional_call_result_check( - relayer: &AccountIdOf, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool; + /// Returns number of bundled messages `Some(_)`, if the given call info is a: + /// + /// - message delivery transaction; + /// + /// - with reasonable bundled messages that may be accepted by the messages pallet. + /// + /// This function is used to check whether the transaction priority should be + /// virtually boosted. The relayer registration (we only boost priority for registered + /// relayer transactions) must be checked outside. + fn bundled_messages_for_priority_boost( + call_info: Option<&ExtensionCallInfo>, + ) -> Option { + // we only boost priority of message delivery transactions + let parsed_call = match call_info { + Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, + _ => return None, + }; + + // compute total number of messages in transaction + let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); + + // a quick check to avoid invalid high-priority transactions + let max_unconfirmed_messages_in_confirmation_tx = >::BridgedChain + ::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { + return None + } + + Some(bundled_messages) + } /// Given post-dispatch information, analyze the outcome of relayer call and return /// actions that need to be performed on relayer account. fn analyze_call_result( - pre: Option>>>, + pre: Option>>, info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, result: &DispatchResult, - ) -> RelayerAccountAction, ::Reward> - { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - + ) -> RelayerAccountAction { // We don't refund anything for transactions that we don't support. let (relayer, call_info) = match pre { Some(Some(pre)) => (pre.relayer, pre.call_info), _ => return RelayerAccountAction::None, }; - // now we know that the relayer either needs to be rewarded, or slashed + // now we know that the call is supported and we may need to reward or slash relayer // => let's prepare the correspondent account that pays reward/receives slashed amount - let reward_account_params = - RewardsAccountParams::new( - ::Id::get(), - ::Instance, - >>::BridgedChain::ID, - if call_info.is_receive_messages_proof_call() { - RewardsAccountOwner::ThisChain - } else { - RewardsAccountOwner::BridgedChain - }, - ); + let lane_id = call_info.messages_call_info().lane_id(); + let reward_account_params = RewardsAccountParams::new( + lane_id, + >::BridgedChain::ID, + if call_info.is_receive_messages_proof_call() { + RewardsAccountOwner::ThisChain + } else { + RewardsAccountOwner::BridgedChain + }, + ); // prepare return value for the case if the call has failed or it has not caused // expected side effects (e.g. not all messages have been accepted) @@ -322,37 +206,19 @@ pub trait RefundSignedExtension: // We don't refund anything if the transaction has failed. if let Err(e) = result { log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", - Self::Id::STR, - ::Id::get(), + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages transaction: {:?}", + Self::IDENTIFIER, + lane_id, relayer, e, ); return slash_relayer_if_delivery_result } - // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that - // it contained. If this happens, we consider the transaction "helpful" and refund it. - let msgs_call_info = call_info.messages_call_info(); - if !MessagesCallHelper::::Instance>::was_successful(msgs_call_info) { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages call", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // do additional checks - if !Self::additional_call_result_check( - &relayer, - &call_info, - &mut extra_weight, - &mut extra_size, - ) { + // check whether the call has succeeded + let mut call_data = ExtensionCallData::default(); + if !C::check_call_result(&call_info, &mut call_data, &relayer) { return slash_relayer_if_delivery_result } @@ -364,81 +230,55 @@ pub trait RefundSignedExtension: let tip = Zero::zero(); // decrease post-dispatch weight/size using extra weight/size that we know now - let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info_weight = - post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); + let post_info_len = len.saturating_sub(call_data.extra_size as usize); + let mut post_info_weight = post_info + .actual_weight + .unwrap_or(info.weight) + .saturating_sub(call_data.extra_weight); // let's also replace the weight of slashing relayer with the weight of rewarding relayer if call_info.is_receive_messages_proof_call() { post_info_weight = post_info_weight.saturating_sub( - ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), + ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), ); } // compute the relayer refund let mut post_info = *post_info; post_info.actual_weight = Some(post_info_weight); - let refund = Self::Refund::compute_refund(info, &post_info, post_info_len, tip); + let refund = Self::compute_refund(info, &post_info, post_info_len, tip); // we can finally reward relayer RelayerAccountAction::Reward(relayer, reward_account_params, refund) } - /// Returns number of bundled messages `Some(_)`, if the given call info is a: - /// - /// - message delivery transaction; - /// - /// - with reasonable bundled messages that may be accepted by the messages pallet. - /// - /// This function is used to check whether the transaction priority should be - /// virtually boosted. The relayer registration (we only boost priority for registered - /// relayer transactions) must be checked outside. - fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { - // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; - - // compute total number of messages in transaction - let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); - - // a quick check to avoid invalid high-priority transactions - let max_unconfirmed_messages_in_confirmation_tx = ::Instance, - >>::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { - return None - } - - Some(bundled_messages) + /// Compute refund for the successful relayer transaction + fn compute_refund( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: R::Reward, + ) -> R::Reward { + TransactionPaymentPallet::::compute_actual_fee(len as _, info, post_info, tip) } } -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -pub struct RefundSignedExtensionAdapter(T); - -impl SignedExtension for RefundSignedExtensionAdapter +impl SignedExtension for BridgeRelayersSignedExtension where - CallOf: Dispatchable - + MessagesCallSubType::Instance>, + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, { - const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; + const IDENTIFIER: &'static str = C::IdProvider::STR; + type AccountId = R::AccountId; + type Call = R::RuntimeCall; type AdditionalSigned = (); - type Pre = Option>>; + type Pre = Option>; fn additional_signed(&self) -> Result<(), TransactionValidityError> { Ok(()) @@ -456,33 +296,33 @@ where // we're not calling `validate` from `pre_dispatch` directly because of performance // reasons, so if you're adding some code that may fail here, please check if it needs // to be added to the `pre_dispatch` as well - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; // the following code just plays with transaction priority and never returns an error // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { + let bundled_messages = match Self::bundled_messages_for_priority_boost(parsed_call.as_ref()) + { Some(bundled_messages) => bundled_messages, None => return Ok(Default::default()), }; // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { + if !RelayersPallet::::is_registration_active(who) { return Ok(Default::default()) } // compute priority boost - let priority_boost = crate::extensions::priority_calculator::compute_priority_boost::< - T::Priority, - >(bundled_messages); + let priority_boost = + priority::compute_priority_boost::(bundled_messages); let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); log::trace!( - target: "runtime::bridge", - "{} via {:?} has boosted priority of message delivery transaction \ + target: LOG_TARGET, + "{}.{:?}: has boosted priority of message delivery transaction \ of relayer {:?}: {} messages -> {} priority", Self::IDENTIFIER, - ::Id::get(), + parsed_call.as_ref().map(|p| p.messages_call_info().lane_id()), who, bundled_messages, priority_boost, @@ -499,14 +339,14 @@ where _len: usize, ) -> Result { // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; Ok(parsed_call.map(|call_info| { log::trace!( - target: "runtime::bridge", - "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + target: LOG_TARGET, + "{}.{:?}: parsed bridge transaction in pre-dispatch: {:?}", Self::IDENTIFIER, - ::Id::get(), + call_info.messages_call_info().lane_id(), call_info, ); PreDispatchData { relayer: who.clone(), call_info } @@ -520,28 +360,28 @@ where len: usize, result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); + let lane_id = pre + .as_ref() + .and_then(|p| p.as_ref()) + .map(|p| p.call_info.messages_call_info().lane_id()); + let call_result = Self::analyze_call_result(pre, info, post_info, len, result); match call_result { RelayerAccountAction::None => (), RelayerAccountAction::Reward(relayer, reward_account, reward) => { - RelayersPallet::::register_relayer_reward( - reward_account, - &relayer, - reward, - ); + RelayersPallet::::register_relayer_reward(reward_account, &relayer, reward); log::trace!( - target: "runtime::bridge", - "{} via {:?} has registered reward: {:?} for {:?}", + target: LOG_TARGET, + "{}.{:?}: has registered reward: {:?} for {:?}", Self::IDENTIFIER, - ::Id::get(), + lane_id, reward, relayer, ); }, RelayerAccountAction::Slash(relayer, slash_account) => - RelayersPallet::::slash_and_deregister( + RelayersPallet::::slash_and_deregister( &relayer, ExplicitOrAccountParams::Params(slash_account), ), @@ -551,416 +391,61 @@ where } } -/// Signed extension that refunds a relayer for new messages coming from a parachain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableParachainId` trait, which specifies the instance of - // the used `pallet-bridge-parachains` pallet and the bridged parachain id - Para, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - RefundBridgedGrandpaMessages< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let para_finality_call = calls - .next() - .transpose()? - .and_then(|c| c.submit_parachain_heads_info_for(Para::BridgedChain::PARACHAIN_ID)); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { - (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( - CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), - ), - (2, None, Some(para_finality_call), Some(msgs_call)) => - Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), - (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check( - relayer: &Runtime::AccountId, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool { - // check if relay chain state has been updated - let is_grandpa_call_successful = - RefundBridgedGrandpaMessages::< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >::additional_call_result_check(relayer, call_info, extra_weight, extra_size); - if !is_grandpa_call_successful { - return false - } - - // check if parachain state has been updated - if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { - if !SubmitParachainHeadsHelper::::was_successful( - para_proof_info, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", - Id::STR, - Para::BridgedChain::PARACHAIN_ID, - Msgs::Id::get(), - relayer, - ); - return false - } - } - - true - } -} - -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) -/// chain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedGrandpaMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // bridge GRANDPA pallet instance, used to track bridged chain state - GrandpaInstance, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedGrandpaMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + MessagesConfig - + RelayersConfig, - GrandpaInstance: 'static, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 2 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, msgs_call) { - (2, Some(relay_finality_call), Some(msgs_call)) => - Some(CallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), - (1, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check( - relayer: &Runtime::AccountId, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool { - // check if relay chain state has been updated - if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::was_successful( - finality_proof_info.block_number, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return false - } - - // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` - // transaction. If relay chain header is mandatory, the GRANDPA pallet returns - // `Pays::No`, because such transaction is mandatory for operating the bridge. But - // `utility.batchAll` transaction always requires payment. But in both cases we'll - // refund relayer - either explicitly here, or using `Pays::No` if he's choosing - // to submit dedicated transaction. - - // submitter has means to include extra weight/bytes in the `submit_finality_proof` - // call, so let's subtract extra weight/size to avoid refunding for this extra stuff - *extra_weight = (*extra_weight).saturating_add(finality_proof_info.extra_weight); - *extra_size = (*extra_size).saturating_add(finality_proof_info.extra_size); - } - - true - } -} - -/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation -/// transactions. Finality transactions are not refunded. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedMessages( - PhantomData<( - // runtime with `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedMessages +/// Verify that the messages pallet call, supported by extension has succeeded. +pub(crate) fn verify_messages_call_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool where - Self: 'static + Send + Sync, - Runtime: MessagesConfig + RelayersConfig, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + MessagesCallSubType, + C: ExtensionConfig, + MI: 'static, + C::Runtime: BridgeMessagesConfig, { - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - vec![call] - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let call = Self::check_obsolete_parsed_call(call)?; - Ok(call.call_info_for(Msgs::Id::get()).map(CallInfo::Msgs)) - } + let messages_call = call_info.messages_call_info(); - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_call()?; - Ok(call) + if !MessagesCallHelper::::was_successful(messages_call) { + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages call", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false } - fn additional_call_result_check( - _relayer: &Runtime::AccountId, - _call_info: &CallInfo, - _extra_weight: &mut Weight, - _extra_size: &mut u32, - ) -> bool { - // everything is checked by the `RefundTransactionExtension` - true - } + true } #[cfg(test)] -pub(crate) mod tests { +mod tests { use super::*; - use crate::{ - messages_call_ext::{ - BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, - UnrewardedRelayerOccupation, - }, - mock::*, - }; - use bp_header_chain::StoredHeaderDataBuilder; + use crate::mock::*; + + use bp_header_chain::{StoredHeaderDataBuilder, SubmitFinalityProofInfo}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, - MessageNonce, MessagesOperatingMode, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, BaseMessagesProofInfo, DeliveredMessages, + InboundLaneData, LaneId, MessageNonce, MessagesCallInfo, MessagesOperatingMode, + OutboundLaneData, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, + UnrewardedRelayer, UnrewardedRelayerOccupation, UnrewardedRelayersState, }; - use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_parachains::{BestParaHeadHash, ParaInfo, SubmitParachainHeadsInfo}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::{BasicOperatingMode, HeaderId}; + use bp_relayers::RuntimeWithUtilityPallet; + use bp_runtime::{BasicOperatingMode, HeaderId, Parachain}; use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; use frame_support::{ + __private::sp_tracing, assert_storage_noop, parameter_types, traits::{fungible::Mutate, ReservableCurrency}, weights::Weight, }; use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; - use pallet_bridge_parachains::{ - Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, - }; + use pallet_bridge_parachains::{Call as ParachainsCall, Pallet as ParachainsPallet}; + use pallet_utility::Call as UtilityCall; use sp_runtime::{ traits::{ConstU64, Header as HeaderT}, transaction_validity::{InvalidTransaction, ValidTransaction}, @@ -968,50 +453,58 @@ pub(crate) mod tests { }; parameter_types! { - pub TestLaneId: LaneId = TEST_LANE_ID; + TestParachain: u32 = BridgedUnderlyingParachain::PARACHAIN_ID; pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, + test_lane_id(), TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::ThisChain, ); pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, + test_lane_id(), TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::BridgedChain, ); + pub TestLaneId: LaneId = test_lane_id(); } + bp_runtime::generate_static_str_provider!(TestGrandpaExtension); bp_runtime::generate_static_str_provider!(TestExtension); + bp_runtime::generate_static_str_provider!(TestMessagesExtension); - type TestMessagesExtensionProvider = RefundBridgedMessages< + type TestGrandpaExtensionConfig = grandpa_adapter::WithGrandpaChainExtensionConfig< + StrTestGrandpaExtension, TestRuntime, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, + RuntimeWithUtilityPallet, + (), + (), ConstU64<1>, - StrTestExtension, >; - type TestMessagesExtension = RefundSignedExtensionAdapter; - type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages< + type TestGrandpaExtension = + BridgeRelayersSignedExtension; + type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig< + StrTestExtension, TestRuntime, + RuntimeWithUtilityPallet, + (), (), - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, ConstU64<1>, - StrTestExtension, >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; - type TestExtensionProvider = RefundBridgedParachainMessages< + type TestExtension = BridgeRelayersSignedExtension; + type TestMessagesExtensionConfig = messages_adapter::WithMessagesExtensionConfig< + StrTestMessagesExtension, TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, + (), ConstU64<1>, - StrTestExtension, >; - type TestExtension = RefundSignedExtensionAdapter; + type TestMessagesExtension = + BridgeRelayersSignedExtension; + + fn test_lane_id() -> LaneId { + LaneId::new(1, 2) + } fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); ExistentialDeposit::get().saturating_add(test_stake * 100) } @@ -1026,7 +519,7 @@ pub(crate) mod tests { TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) } - pub fn relayer_account_at_this_chain() -> ThisChainAccountId { + fn relayer_account_at_this_chain() -> ThisChainAccountId { 0 } @@ -1034,13 +527,13 @@ pub(crate) mod tests { 0 } - pub fn initialize_environment( - best_relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); - let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); pallet_bridge_grandpa::CurrentAuthoritySet::::put( StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), ); @@ -1050,7 +543,7 @@ pub(crate) mod tests { bp_test_utils::test_header::(0).build(), ); - let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_id = ParaId(TestParachain::get()); let para_info = ParaInfo { best_head_hash: BestParaHeadHash { at_relay_block_number: parachain_head_at_relay_header_number, @@ -1060,7 +553,7 @@ pub(crate) mod tests { }; pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); - let lane_id = TestLaneId::get(); + let lane_id = test_lane_id(); let in_lane_data = InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); @@ -1078,7 +571,7 @@ pub(crate) mod tests { .unwrap(); } - fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1094,7 +587,7 @@ pub(crate) mod tests { }) } - pub fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call_ex(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1113,12 +606,12 @@ pub(crate) mod tests { } fn submit_parachain_head_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1126,12 +619,12 @@ pub(crate) mod tests { } pub fn submit_parachain_head_call_ex( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads_ex { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1145,10 +638,11 @@ pub(crate) mod tests { proof: Box::new(FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: TestLaneId::get(), + lane: test_lane_id(), nonces_start: pallet_bridge_messages::InboundLanes::::get( - TEST_LANE_ID, + test_lane_id(), ) + .unwrap() .last_delivered_nonce() + 1, nonces_end: best_message, @@ -1163,7 +657,7 @@ pub(crate) mod tests { proof: FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: TestLaneId::get(), + lane: test_lane_id(), }, relayers_state: UnrewardedRelayersState { last_delivered_nonce: best_message, @@ -1173,7 +667,7 @@ pub(crate) mod tests { } fn parachain_finality_and_delivery_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1185,7 +679,7 @@ pub(crate) mod tests { } fn parachain_finality_and_confirmation_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1197,7 +691,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1209,7 +703,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1221,7 +715,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1233,7 +727,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1245,8 +739,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1259,8 +753,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1273,8 +767,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1287,8 +781,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1300,10 +794,11 @@ pub(crate) mod tests { }) } - fn all_finality_pre_dispatch_data() -> PreDispatchData { + fn all_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1314,38 +809,40 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn all_finality_pre_dispatch_data_ex() -> PreDispatchData { + #[cfg(test)] + fn all_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1356,13 +853,13 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1371,17 +868,18 @@ pub(crate) mod tests { } } - fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1392,32 +890,33 @@ pub(crate) mod tests { }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1428,7 +927,7 @@ pub(crate) mod tests { }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1437,53 +936,55 @@ pub(crate) mod tests { } } - fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1492,33 +993,35 @@ pub(crate) mod tests { } } - fn delivery_pre_dispatch_data() -> PreDispatchData { + fn delivery_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }, )), } } - fn confirmation_pre_dispatch_data() -> PreDispatchData { + fn confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }), @@ -1527,14 +1030,14 @@ pub(crate) mod tests { } fn set_bundled_range_end( - mut pre_dispatch_data: PreDispatchData, + mut pre_dispatch_data: PreDispatchData, end: MessageNonce, - ) -> PreDispatchData { + ) -> PreDispatchData { let msg_info = match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, - CallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, - CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, - CallInfo::Msgs(ref mut info) => info, + ExtensionCallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, + ExtensionCallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::Msgs(ref mut info) => info, }; if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { @@ -1545,20 +1048,17 @@ pub(crate) mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_messages_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } @@ -1571,25 +1071,29 @@ pub(crate) mod tests { fn run_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + sp_tracing::try_init_simple(); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_messages_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + ) -> Result>, TransactionValidityError> { + let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } @@ -1609,7 +1113,7 @@ pub(crate) mod tests { } fn run_post_dispatch( - pre_dispatch_data: Option>, + pre_dispatch_data: Option>, dispatch_result: DispatchResult, ) { let post_dispatch_result = TestExtension::post_dispatch( @@ -1651,46 +1155,28 @@ pub(crate) mod tests { Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); // message delivery is failing - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - assert_eq!(f(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - f(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - f(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Default::default()), - ); - assert_eq!( - f(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Default::default()), - ); - } - - // message confirmation validation is passing + assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),); assert_eq!( - ignore_priority(run_validate(message_confirmation_call(200))), + run_validate(parachain_finality_and_delivery_batch_call(200, 200)), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), + run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Default::default()), ); + // message confirmation validation is passing assert_eq!( - ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( - 200, 200 - ))), + ignore_priority(run_validate(message_confirmation_call(200))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call( - 200, 200, 200 + ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( + 200, 200 ))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( + ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(Default::default()), @@ -1706,28 +1192,25 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - let priority_of_100_messages_delivery = - f(message_delivery_call(200)).unwrap().priority; - let priority_of_200_messages_delivery = - f(message_delivery_call(300)).unwrap().priority; - assert!( - priority_of_200_messages_delivery > priority_of_100_messages_delivery, - "Invalid priorities: {} for 200 messages vs {} for 100 messages", - priority_of_200_messages_delivery, - priority_of_100_messages_delivery, - ); + let priority_of_100_messages_delivery = + run_validate(message_delivery_call(200)).unwrap().priority; + let priority_of_200_messages_delivery = + run_validate(message_delivery_call(300)).unwrap().priority; + assert!( + priority_of_200_messages_delivery > priority_of_100_messages_delivery, + "Invalid priorities: {} for 200 messages vs {} for 100 messages", + priority_of_200_messages_delivery, + priority_of_100_messages_delivery, + ); - let priority_of_100_messages_confirmation = - f(message_confirmation_call(200)).unwrap().priority; - let priority_of_200_messages_confirmation = - f(message_confirmation_call(300)).unwrap().priority; - assert_eq!( - priority_of_100_messages_confirmation, - priority_of_200_messages_confirmation - ); - } + let priority_of_100_messages_confirmation = + run_validate(message_confirmation_call(200)).unwrap().priority; + let priority_of_200_messages_confirmation = + run_validate(message_confirmation_call(300)).unwrap().priority; + assert_eq!( + priority_of_100_messages_confirmation, + priority_of_200_messages_confirmation + ); }); } @@ -1739,26 +1222,23 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - let priority_of_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - )) - .unwrap() - .priority; - let priority_of_more_than_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, - )) - .unwrap() - .priority; + let priority_of_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + )) + .unwrap() + .priority; + let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, + )) + .unwrap() + .priority; - assert!( - priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, - "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", - priority_of_max_messages_delivery, - priority_of_more_than_max_messages_delivery, - ); - } + assert!( + priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, + "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", + priority_of_max_messages_delivery, + priority_of_more_than_max_messages_delivery, + ); }); } @@ -1775,15 +1255,10 @@ pub(crate) mod tests { ignore_priority(run_validate(message_confirmation_call(200))), Ok(ValidTransaction::default()), ); - assert_eq!( ignore_priority(run_messages_validate(message_delivery_call(200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(parachain_finality_and_delivery_batch_call(200, 200))), @@ -1800,24 +1275,12 @@ pub(crate) mod tests { ignore_priority(run_validate(all_finality_and_delivery_batch_call(200, 200, 200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_delivery_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); }); } @@ -2103,13 +1566,10 @@ pub(crate) mod tests { let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (100, RelayBlockHash::default()), + at_relay_block: (100, BridgedChainHash::default()), parachains: vec![ - (ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), [1u8; 32].into()), - ( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID + 1), - [1u8; 32].into(), - ), + (ParaId(TestParachain::get()), [1u8; 32].into()), + (ParaId(TestParachain::get() + 1), [1u8; 32].into()), ], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, }), @@ -2250,7 +1710,7 @@ pub(crate) mod tests { // now repeat the same with size+weight refund: we expect smaller reward let mut pre_dispatch_data = all_finality_pre_dispatch_data(); match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, ..) => { info.extra_weight.set_ref_time( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, ); @@ -2356,7 +1816,7 @@ pub(crate) mod tests { let delivery_rewards_account_balance = Balances::free_balance(delivery_rewards_account()); - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); Balances::set_balance( &relayer_account_at_this_chain(), ExistentialDeposit::get() + test_stake * 10, @@ -2426,10 +1886,10 @@ pub(crate) mod tests { } fn run_analyze_call_result( - pre_dispatch_data: PreDispatchData, + pre_dispatch_data: PreDispatchData, dispatch_result: DispatchResult, ) -> RelayerAccountAction { - TestExtensionProvider::analyze_call_result( + TestExtension::analyze_call_result( Some(Some(pre_dispatch_data)), &dispatch_info(), &post_dispatch_info(), @@ -2494,41 +1954,29 @@ pub(crate) mod tests { } #[test] - fn messages_ext_only_parses_standalone_transactions() { + fn grandpa_ext_only_parses_valid_batches() { run_test(|| { initialize_environment(100, 100, 100); // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); // relay + parachain + message confirmation calls batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); // parachain + message delivery call batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2536,43 +1984,31 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), ); - // relay + message delivery call batch is ignored + // relay + message delivery call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), - Ok(None), - ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call_ex(200, 200) - ), - Ok(None), + Ok(Some(relay_finality_pre_dispatch_data().call_info)), ); - // relay + message confirmation call batch is ignored + // relay + message confirmation call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), - Ok(None), - ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call_ex(200, 200) - ), - Ok(None), + Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), ); // message delivery call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2580,7 +2016,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2589,66 +2025,19 @@ pub(crate) mod tests { } #[test] - fn messages_ext_rejects_calls_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_messages_pre_dispatch(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_messages_pre_dispatch(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_messages_validate(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_messages_validate(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn messages_ext_accepts_calls_with_new_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_messages_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_messages_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - - assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_messages_validate(message_confirmation_call(200)), - Ok(Default::default()), - ); - }); - } - - #[test] - fn grandpa_ext_only_parses_valid_batches() { + fn messages_ext_only_parses_standalone_transactions() { run_test(|| { initialize_environment(100, 100, 100); // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call_ex(200, 200, 200) ), Ok(None), @@ -2656,13 +2045,13 @@ pub(crate) mod tests { // relay + parachain + message confirmation calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call_ex(200, 200, 200) ), Ok(None), @@ -2670,7 +2059,7 @@ pub(crate) mod tests { // parachain + message delivery call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2678,43 +2067,43 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), ); - // relay + message delivery call batch is accepted + // relay + message delivery call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), - Ok(Some(relay_finality_pre_dispatch_data().call_info)), + Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call_ex(200, 200) ), - Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)), + Ok(None), ); - // relay + message confirmation call batch is accepted + // relay + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), + Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call_ex(200, 200) ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)), + Ok(None), ); // message delivery call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2722,7 +2111,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2730,6 +2119,53 @@ pub(crate) mod tests { }); } + #[test] + fn messages_ext_rejects_calls_with_obsolete_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_messages_pre_dispatch(message_delivery_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_messages_pre_dispatch(message_confirmation_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_messages_validate(message_delivery_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_messages_validate(message_confirmation_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn messages_ext_accepts_calls_with_new_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_messages_pre_dispatch(message_delivery_call(200)), + Ok(Some(delivery_pre_dispatch_data())), + ); + assert_eq!( + run_messages_pre_dispatch(message_confirmation_call(200)), + Ok(Some(confirmation_pre_dispatch_data())), + ); + + assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),); + assert_eq!( + run_messages_validate(message_confirmation_call(200)), + Ok(Default::default()), + ); + }); + } + #[test] fn grandpa_ext_rejects_batch_with_obsolete_relay_chain_header() { run_test(|| { @@ -2874,7 +2310,7 @@ pub(crate) mod tests { fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() { run_test(|| { let best_delivered_message = - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; initialize_environment(100, 100, best_delivered_message); // register relayer so it gets priority boost @@ -2890,6 +2326,7 @@ pub(crate) mod tests { messages: DeliveredMessages { begin: 1, end: best_delivered_message }, }] .into(), + ..Default::default() }; pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); diff --git a/bridges/modules/relayers/src/extension/parachain_adapter.rs b/bridges/modules/relayers/src/extension/parachain_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..b6f57cebc309dcf1d5e5c952395879f01933d188 --- /dev/null +++ b/bridges/modules/relayers/src/extension/parachain_adapter.rs @@ -0,0 +1,182 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote parachain. + +use crate::{ + extension::{ + grandpa_adapter::verify_submit_finality_proof_succeeded, verify_messages_call_succeeded, + }, + Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Parachain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, +}; +use pallet_bridge_parachains::{ + CallSubType as BridgeParachainsCallSubtype, Config as BridgeParachainsConfig, + SubmitParachainHeadsHelper, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote parachains. +pub struct WithParachainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeParachainsConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-parachains`, tracked by this extension + BridgeParachainsPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeParachainsPalletInstance, + BridgeMessagesPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig for WithParachainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig + + BridgeParachainsConfig + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + PI: 'static, + MI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeParachainsCallSubtype + + BridgeMessagesCallSubType, + >::BridgedChain: Parachain, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type PriorityBoostPerMessage = P; + type Reward = R::Reward; + type RemoteGrandpaChainBlockNumber = + pallet_bridge_grandpa::BridgedBlockNumber; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = BCU::unpack(call, 3); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let para_finality_call = calls.next().transpose()?.and_then(|c| { + let r = c.submit_parachain_heads_info_for( + >::BridgedChain::PARACHAIN_ID, + ); + r + }); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { + (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::AllFinalityAndMsgs( + relay_finality_call, + para_finality_call, + msgs_call, + )), + (2, None, Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), + (1, None, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::( + call_info, call_data, relayer, + ) && verify_submit_parachain_head_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the parachain state update call, verify that it +/// has been successful. +/// +/// Only returns false when parachain state update call has failed. +pub(crate) fn verify_submit_parachain_head_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + PI: 'static, + C::Runtime: BridgeParachainsConfig, +{ + let Some(para_proof_info) = call_info.submit_parachain_heads_info() else { return true }; + + if !SubmitParachainHeadsHelper::::was_successful(para_proof_info) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid parachain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + true +} diff --git a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs b/bridges/modules/relayers/src/extension/priority.rs similarity index 96% rename from bridges/bin/runtime-common/src/extensions/priority_calculator.rs rename to bridges/modules/relayers/src/extension/priority.rs index 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..b9627774db1ec9fcc828713904e1e60e24a7ba1d 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -36,13 +36,13 @@ pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; -pub mod benchmarking; - mod mock; mod payment_adapter; mod stake_adapter; mod weights_ext; +pub mod benchmarking; +pub mod extension; pub mod weights; /// The target that will be used when publishing logs related to this pallet. @@ -492,7 +492,7 @@ mod tests { get_ready_for_events(); Pallet::::register_relayer_reward( - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), ®ULAR_RELAYER, 100, ); @@ -502,9 +502,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardRegistered { + event: TestEvent::BridgeRelayers(RewardRegistered { relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + rewards_account_params: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -519,7 +519,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::root(), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), DispatchError::BadOrigin, ); @@ -532,7 +532,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), Error::::NoRewardForRelayer, ); @@ -544,13 +544,13 @@ mod tests { run_test(|| { RelayerRewards::::insert( FAILING_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 100, ); assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(FAILING_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), Error::::FailedToPayReward, ); @@ -564,15 +564,15 @@ mod tests { RelayerRewards::::insert( REGULAR_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 100, ); assert_ok!(Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() )); assert_eq!( - RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(REGULAR_RELAYER, test_reward_account_param()), None ); @@ -581,9 +581,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardPaid { + event: TestEvent::BridgeRelayers(RewardPaid { relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + rewards_account_params: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -595,16 +595,17 @@ mod tests { #[test] fn pay_reward_from_account_actually_pays_reward() { type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; + type PayLaneRewardFromAccount = + bp_relayers::PayRewardFromAccount; run_test(|| { let in_lane_0 = RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain, ); let out_lane_1 = RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), + LaneId::new(1, 3), *b"test", RewardsAccountOwner::BridgedChain, ); @@ -676,7 +677,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() }, }), @@ -744,7 +745,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -808,7 +809,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -870,7 +871,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), + event: TestEvent::BridgeRelayers(Event::Deregistered { + relayer: REGISTER_RELAYER + }), topics: vec![], }), ); diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 3124787896c3e1ee20014fc21b87ccbc19e6a2c2..de1d292b7c0f022d9b94213dd3a8ecea814f409a 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -18,51 +18,184 @@ use crate as pallet_bridge_relayers; -use bp_messages::LaneId; +use bp_header_chain::ChainWithGrandpa; +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + ChainWithMessages, LaneId, MessageNonce, +}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::{ PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, }; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ - derive_impl, parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight, + derive_impl, parameter_types, + traits::fungible::Mutate, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, +}; +use pallet_transaction_payment::Multiplier; +use sp_core::{ConstU64, ConstU8, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, ConstU32}, + BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; -use sp_runtime::BuildStorage; -pub type AccountId = u64; -pub type Balance = u64; -pub type BlockNumber = u64; +/// Account identifier at `ThisChain`. +pub type ThisChainAccountId = u64; +/// Balance at `ThisChain`. +pub type ThisChainBalance = u64; +/// Block number at `ThisChain`. +pub type ThisChainBlockNumber = u32; +/// Hash at `ThisChain`. +pub type ThisChainHash = H256; +/// Hasher at `ThisChain`. +pub type ThisChainHasher = BlakeTwo256; +/// Header of `ThisChain`. +pub type ThisChainHeader = sp_runtime::generic::Header; +/// Block of `ThisChain`. +pub type ThisChainBlock = frame_system::mocking::MockBlockU32; + +/// Account identifier at the `BridgedChain`. +pub type BridgedChainAccountId = u128; +/// Balance at the `BridgedChain`. +pub type BridgedChainBalance = u128; +/// Block number at the `BridgedChain`. +pub type BridgedChainBlockNumber = u32; +/// Hash at the `BridgedChain`. +pub type BridgedChainHash = H256; +/// Hasher at the `BridgedChain`. +pub type BridgedChainHasher = BlakeTwo256; +/// Header of the `BridgedChain`. +pub type BridgedChainHeader = + sp_runtime::generic::Header; + +/// Bridged chain id used in tests. +pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; +/// Maximal extrinsic size at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + +/// Underlying chain of `ThisChain`. +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + const ID: ChainId = *b"tuch"; + + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hasher = ThisChainHasher; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithMessages for ThisUnderlyingChain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +/// Underlying chain of `BridgedChain`. +pub struct BridgedUnderlyingParachain; + +impl Chain for BridgedUnderlyingParachain { + const ID: ChainId = TEST_BRIDGED_CHAIN_ID; + + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithGrandpa for BridgedUnderlyingParachain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; +} + +impl ChainWithMessages for BridgedUnderlyingParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +impl Parachain for BridgedUnderlyingParachain { + const PARACHAIN_ID: u32 = 42; + const MAX_HEADER_SIZE: u32 = 1_024; +} pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - AccountId, - BlockNumber, + ThisChainAccountId, + ThisChainBlockNumber, Balances, ReserveId, Stake, Lease, >; -type Block = frame_system::mocking::MockBlock; - frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, + Utility: pallet_utility, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } } parameter_types! { + pub const BridgedParasPalletName: &'static str = "Paras"; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; + pub const ExistentialDeposit: ThisChainBalance = 1; pub const ReserveId: [u8; 8] = *b"brdgrlrs"; - pub const Stake: Balance = 1_000; - pub const Lease: BlockNumber = 8; + pub const Stake: ThisChainBalance = 1_000; + pub const Lease: ThisChainBlockNumber = 8; + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub const TransactionBaseFee: ThisChainBalance = 0; + pub const TransactionByteFee: ThisChainBalance = 1; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; + type Block = ThisChainBlock; + // TODO: remove when https://github.com/paritytech/polkadot-sdk/pull/4543 merged + type BlockHashCount = ConstU32<10>; + type AccountData = pallet_balances::AccountData; type DbWeight = DbWeight; } @@ -72,9 +205,74 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } +impl pallet_utility::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] +impl pallet_transaction_payment::Config for TestRuntime { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + TestRuntime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = BridgedUnderlyingParachain; + type MaxFreeHeadersPerBlock = ConstU32<4>; + type FreeHeadersInterval = ConstU32<1_024>; + type HeadersToKeep = ConstU32<8>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgesGrandpaPalletInstance = (); + type ParasPalletName = BridgedParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<8>; + type MaxParaHeadDataSize = ConstU32<1024>; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + + type OutboundPayload = Vec; + + type InboundPayload = Vec; + type DeliveryPayments = (); + + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + TestRuntime, + (), + ConstU64<100_000>, + >; + type OnMessagesDelivered = (); + + type MessageDispatch = DummyMessageDispatch; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingParachain; + type BridgedHeaderChain = BridgeGrandpa; +} + impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type Reward = Balance; + type Reward = ThisChainBalance; type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); @@ -82,9 +280,9 @@ impl pallet_bridge_relayers::Config for TestRuntime { #[cfg(feature = "runtime-benchmarks")] impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Balance) { + fn prepare_rewards_account(account_params: RewardsAccountParams, reward: ThisChainBalance) { let rewards_account = - bp_relayers::PayRewardFromAccount::::rewards_account( + bp_relayers::PayRewardFromAccount::::rewards_account( account_params, ); Self::deposit_account(rewards_account, reward); @@ -95,35 +293,31 @@ impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { } } -/// Message lane that we're using in tests. -pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = - RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); - /// Regular relayer that may receive rewards. -pub const REGULAR_RELAYER: AccountId = 1; +pub const REGULAR_RELAYER: ThisChainAccountId = 1; /// Relayer that can't receive rewards. -pub const FAILING_RELAYER: AccountId = 2; +pub const FAILING_RELAYER: ThisChainAccountId = 2; /// Relayer that is able to register. -pub const REGISTER_RELAYER: AccountId = 42; +pub const REGISTER_RELAYER: ThisChainAccountId = 42; /// Payment procedure that rejects payments to the `FAILING_RELAYER`. pub struct TestPaymentProcedure; impl TestPaymentProcedure { - pub fn rewards_account(params: RewardsAccountParams) -> AccountId { - PayRewardFromAccount::<(), AccountId>::rewards_account(params) + pub fn rewards_account(params: RewardsAccountParams) -> ThisChainAccountId { + PayRewardFromAccount::<(), ThisChainAccountId>::rewards_account(params) } } -impl PaymentProcedure for TestPaymentProcedure { +impl PaymentProcedure for TestPaymentProcedure { type Error = (); fn pay_reward( - relayer: &AccountId, + relayer: &ThisChainAccountId, _lane_id: RewardsAccountParams, - _reward: Balance, + _reward: ThisChainBalance, ) -> Result<(), Self::Error> { match *relayer { FAILING_RELAYER => Err(()), @@ -132,6 +326,40 @@ impl PaymentProcedure for TestPaymentProcedure { } } +/// Dummy message dispatcher. +pub struct DummyMessageDispatch; + +impl DummyMessageDispatch { + pub fn deactivate(lane: LaneId) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); + } +} + +impl MessageDispatch for DummyMessageDispatch { + type DispatchPayload = Vec; + type DispatchLevelResult = (); + + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) + } + + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + Weight::zero() + } + + fn dispatch( + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } + } +} + +/// Reward account params that we are using in tests. +pub fn test_reward_account_param() -> RewardsAccountParams { + RewardsAccountParams::new(LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain) +} + /// Return test externalities to use in tests. pub fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs index f75c409aca4f3ef85fab748e4dc41af12b545562..3693793a3e5cac36d7fc46574abf2153249e4467 100644 --- a/bridges/modules/relayers/src/payment_adapter.rs +++ b/bridges/modules/relayers/src/payment_adapter.rs @@ -103,11 +103,11 @@ mod tests { use super::*; use crate::{mock::*, RelayerRewards}; - const RELAYER_1: AccountId = 1; - const RELAYER_2: AccountId = 2; - const RELAYER_3: AccountId = 3; + const RELAYER_1: ThisChainAccountId = 1; + const RELAYER_2: ThisChainAccountId = 2; + const RELAYER_3: ThisChainAccountId = 3; - fn relayers_rewards() -> RelayersRewards { + fn relayers_rewards() -> RelayersRewards { vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() } @@ -117,16 +117,16 @@ mod tests { register_relayers_rewards::( &RELAYER_2, relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 50, ); assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); }); @@ -138,20 +138,20 @@ mod tests { register_relayers_rewards::( &RELAYER_3, relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 50, ); assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); assert_eq!( - RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_3, test_reward_account_param()), None ); }); diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs index 7ba90d91dfd94e49bf0ff6ee8fcc06f80e287c41..0c965e9e6bff8182e9c45498faa3cb4535a87b62 100644 --- a/bridges/modules/relayers/src/stake_adapter.rs +++ b/bridges/modules/relayers/src/stake_adapter.rs @@ -80,7 +80,7 @@ mod tests { use frame_support::traits::fungible::Mutate; - fn test_stake() -> Balance { + fn test_stake() -> ThisChainBalance { Stake::get() } @@ -130,7 +130,7 @@ mod tests { #[test] fn repatriate_reserved_works() { run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary = test_reward_account_param(); let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); let mut expected_balance = ExistentialDeposit::get(); @@ -186,7 +186,7 @@ mod tests { #[test] fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() { run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary = test_reward_account_param(); let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); Balances::mint_into(&3, test_stake() * 2).unwrap(); diff --git a/bridges/modules/xcm-bridge-hub-router/Cargo.toml b/bridges/modules/xcm-bridge-hub-router/Cargo.toml index 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..7ba524e95b1d0799debf0041214a2ffe19dd34ca 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -30,12 +30,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_xcm_bridge_hub_router::{ - BridgeState, XcmChannelStatusProvider, MINIMAL_DELIVERY_FEE_FACTOR, -}; +pub use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; use codec::Encode; use frame_support::traits::Get; -use sp_core::H256; use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; use sp_std::vec::Vec; use xcm::prelude::*; @@ -49,6 +46,9 @@ pub mod weights; mod mock; +/// Minimal delivery fee factor. +pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); + /// The factor that is used to increase current message fee factor when bridge experiencing /// some lags. const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 @@ -77,11 +77,16 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; /// Benchmarks results from runtime we're plugged into. type WeightInfo: WeightInfo; /// Universal location of this runtime. type UniversalLocation: Get; + /// Relative location of the supported sibling bridge hub. + type SiblingBridgeHubLocation: Get; /// The bridged network that this config is for if specified. /// Also used for filtering `Bridges` by `BridgedNetworkId`. /// If not specified, allows all networks pass through. @@ -93,13 +98,10 @@ pub mod pallet { /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; - /// Origin of the sibling bridge hub that is allowed to report bridge status. - type BridgeHubOrigin: EnsureOrigin; /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. type ToBridgeHubSender: SendXcm + InspectMessageQueues; - /// Underlying channel with the sibling bridge hub. It must match the channel, used - /// by the `Self::ToBridgeHubSender`. - type WithBridgeHubChannel: XcmChannelStatusProvider; + /// Local XCM channel manager. + type LocalXcmChannelManager: XcmChannelStatusProvider; /// Additional fee that is paid for every byte of the outbound message. type ByteFee: Get; @@ -113,118 +115,118 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { - // TODO: make sure that `WithBridgeHubChannel::is_congested` returns true if either - // of XCM channels (outbound/inbound) is suspended. Because if outbound is suspended - // that is definitely congestion. If inbound is suspended, then we are not able to - // receive the "report_bridge_status" signal (that maybe sent by the bridge hub). - - // if the channel with sibling/child bridge hub is suspended, we don't change - // anything - if T::WithBridgeHubChannel::is_congested() { + // if XCM channel is still congested, we don't change anything + if T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) { return T::WeightInfo::on_initialize_when_congested() } - // if bridge has reported congestion, we don't change anything - let mut bridge = Self::bridge(); - if bridge.is_congested { + // if we can't decrease the delivery fee factor anymore, we don't change anything + let mut delivery_fee_factor = Self::delivery_fee_factor(); + if delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { return T::WeightInfo::on_initialize_when_congested() } - // if fee factor is already minimal, we don't change anything - if bridge.delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { - return T::WeightInfo::on_initialize_when_congested() - } - - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - MINIMAL_DELIVERY_FEE_FACTOR.max(bridge.delivery_fee_factor / EXPONENTIAL_FEE_BASE); + let previous_factor = delivery_fee_factor; + delivery_fee_factor = + MINIMAL_DELIVERY_FEE_FACTOR.max(delivery_fee_factor / EXPONENTIAL_FEE_BASE); log::info!( target: LOG_TARGET, - "Bridge queue is uncongested. Decreased fee factor from {} to {}", + "Bridge channel is uncongested. Decreased fee factor from {} to {}", previous_factor, - bridge.delivery_fee_factor, + delivery_fee_factor, ); + Self::deposit_event(Event::DeliveryFeeFactorDecreased { + new_value: delivery_fee_factor, + }); + + DeliveryFeeFactor::::put(delivery_fee_factor); - Bridge::::put(bridge); T::WeightInfo::on_initialize_when_non_congested() } } - #[pallet::call] - impl, I: 'static> Pallet { - /// Notification about congested bridge queue. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::report_bridge_status())] - pub fn report_bridge_status( - origin: OriginFor, - // this argument is not currently used, but to ease future migration, we'll keep it - // here - bridge_id: H256, - is_congested: bool, - ) -> DispatchResult { - let _ = T::BridgeHubOrigin::ensure_origin(origin)?; - - log::info!( - target: LOG_TARGET, - "Received bridge status from {:?}: congested = {}", - bridge_id, - is_congested, - ); - - Bridge::::mutate(|bridge| { - bridge.is_congested = is_congested; - }); - Ok(()) - } + /// Initialization value for the delivery fee factor. + #[pallet::type_value] + pub fn InitialFactor() -> FixedU128 { + MINIMAL_DELIVERY_FEE_FACTOR } - /// Bridge that we are using. + /// The number to multiply the base delivery fee by. /// - /// **bridges-v1** assumptions: all outbound messages through this router are using single lane - /// and to single remote consensus. If there is some other remote consensus that uses the same - /// bridge hub, the separate pallet instance shall be used, In `v2` we'll have all required - /// primitives (lane-id aka bridge-id, derived from XCM locations) to support multiple bridges - /// by the same pallet instance. + /// This factor is shared by all bridges, served by this pallet. For example, if this + /// chain (`Config::UniversalLocation`) opens two bridges ( + /// `X2(GlobalConsensus(Config::BridgedNetworkId::get()), Parachain(1000))` and + /// `X2(GlobalConsensus(Config::BridgedNetworkId::get()), Parachain(2000))`), then they + /// both will be sharing the same fee factor. This is because both bridges are sharing + /// the same local XCM channel with the child/sibling bridge hub, which we are using + /// to detect congestion: + /// + /// ```nocompile + /// ThisChain --- Local XCM channel --> Sibling Bridge Hub ------ + /// | | + /// | | + /// | | + /// Lane1 Lane2 + /// | | + /// | | + /// | | + /// \ / | + /// Parachain1 <-- Local XCM channel --- Remote Bridge Hub <------ + /// | + /// | + /// Parachain1 <-- Local XCM channel --------- + /// ``` + /// + /// If at least one of other channels is congested, the local XCM channel with sibling + /// bridge hub eventually becomes congested too. And we have no means to detect - which + /// bridge exactly causes the congestion. So the best solution here is not to make + /// any differences between all bridges, started by this chain. #[pallet::storage] - #[pallet::getter(fn bridge)] - pub type Bridge, I: 'static = ()> = StorageValue<_, BridgeState, ValueQuery>; + #[pallet::getter(fn delivery_fee_factor)] + pub type DeliveryFeeFactor, I: 'static = ()> = + StorageValue<_, FixedU128, ValueQuery, InitialFactor>; impl, I: 'static> Pallet { /// Called when new message is sent (queued to local outbound XCM queue) over the bridge. pub(crate) fn on_message_sent_to_bridge(message_size: u32) { - log::trace!( - target: LOG_TARGET, - "on_message_sent_to_bridge - message_size: {message_size:?}", - ); - let _ = Bridge::::try_mutate(|bridge| { - let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested(); - let is_bridge_congested = bridge.is_congested; - - // if outbound queue is not congested AND bridge has not reported congestion, do - // nothing - if !is_channel_with_bridge_hub_congested && !is_bridge_congested { - return Err(()) - } - - // ok - we need to increase the fee factor, let's do that - let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) - .saturating_mul(MESSAGE_SIZE_FEE_BASE); - let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - bridge.delivery_fee_factor.saturating_mul(total_factor); + // if outbound channel is not congested, do nothing + if !T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) { + return + } + // ok - we need to increase the fee factor, let's do that + let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) + .saturating_mul(MESSAGE_SIZE_FEE_BASE); + let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); + DeliveryFeeFactor::::mutate(|f| { + let previous_factor = *f; + *f = f.saturating_mul(total_factor); log::info!( target: LOG_TARGET, "Bridge channel is congested. Increased fee factor from {} to {}", previous_factor, - bridge.delivery_fee_factor, + f, ); - - Ok(()) + Self::deposit_event(Event::DeliveryFeeFactorIncreased { new_value: *f }); + *f }); } } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Delivery fee factor has been decreased. + DeliveryFeeFactorDecreased { + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + }, + /// Delivery fee factor has been increased. + DeliveryFeeFactorIncreased { + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + }, + } } /// We'll be using `SovereignPaidRemoteExporter` to send remote messages over the sibling/child @@ -259,17 +261,25 @@ impl, I: 'static> ExporterFor for Pallet { } // ensure that the message is sent to the expected bridged network and location. - let Some((bridge_hub_location, maybe_payment)) = - T::Bridges::exporter_for(network, remote_location, message) - else { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?} and remote_location {:?}!", - T::BridgedNetworkId::get(), - network, - remote_location, - ); - return None + let (bridge_hub_location, maybe_payment) = match T::Bridges::exporter_for( + network, + remote_location, + message, + ) { + Some((bridge_hub_location, maybe_payment)) + if bridge_hub_location.eq(&T::SiblingBridgeHubLocation::get()) => + (bridge_hub_location, maybe_payment), + _ => { + log::trace!( + target: LOG_TARGET, + "Router configured with bridged_network_id {:?} and sibling_bridge_hub_location: {:?} does not support bridging to network {:?} and remote_location {:?}!", + T::BridgedNetworkId::get(), + T::SiblingBridgeHubLocation::get(), + network, + remote_location, + ); + return None + }, }; // take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset` @@ -279,8 +289,8 @@ impl, I: 'static> ExporterFor for Pallet { invalid_asset => { log::error!( target: LOG_TARGET, - "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} which is not \ - compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", + "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} \ + which is not compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", T::BridgedNetworkId::get(), T::FeeAsset::get(), invalid_asset, @@ -300,18 +310,18 @@ impl, I: 'static> ExporterFor for Pallet { let message_size = message.encoded_size(); let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get()); let fee_sum = base_fee.saturating_add(message_fee); - let fee_factor = Self::bridge().delivery_fee_factor; - let fee = fee_factor.saturating_mul_int(fee_sum); + let fee_factor = Self::delivery_fee_factor(); + let fee = fee_factor.saturating_mul_int(fee_sum); let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None }; log::info!( target: LOG_TARGET, - "Validate send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", + "Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", (network, remote_location), message_size, fee, - fee_factor + fee_factor, ); Some((bridge_hub_location, fee)) @@ -398,6 +408,10 @@ impl, I: 'static> SendXcm for Pallet { } impl, I: 'static> InspectMessageQueues for Pallet { + fn clear_messages() { + ViaBridgeHubExporter::::clear_messages() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { ViaBridgeHubExporter::::get_messages() } @@ -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); - }) - } - - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(FixedU128::from_rational(125, 100))); + assert_eq!(XcmBridgeHubRouter::delivery_fee_factor(), old_delivery_fee_factor); - // it should not decrease, because bridge congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); + assert_eq!(System::events(), vec![]); }) } #[test] fn fee_factor_is_decreased_from_on_initialize_when_xcm_channel_is_uncongested() { run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); + let initial_fee_factor = FixedU128::from_rational(125, 100); + DeliveryFeeFactor::::put(initial_fee_factor); - // it should eventually decreased to one - while XcmBridgeHubRouter::bridge().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR { + // it shold eventually decreased to one + while XcmBridgeHubRouter::delivery_fee_factor() > MINIMAL_DELIVERY_FEE_FACTOR { XcmBridgeHubRouter::on_initialize(One::one()); } // verify that it doesn't decreases anymore XcmBridgeHubRouter::on_initialize(One::one()); + assert_eq!(XcmBridgeHubRouter::delivery_fee_factor(), MINIMAL_DELIVERY_FEE_FACTOR); + + // check emitted event + let first_system_event = System::events().first().cloned(); assert_eq!( - XcmBridgeHubRouter::bridge(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR) + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter(Event::DeliveryFeeFactorDecreased { + new_value: initial_fee_factor / EXPONENTIAL_FEE_BASE, + }), + topics: vec![], + }) ); }) } @@ -577,7 +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,9 +628,20 @@ mod tests { .map(drop)); assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); + assert!(old_delivery_fee_factor < XcmBridgeHubRouter::delivery_fee_factor()); + + // check emitted event + let first_system_event = System::events().first().cloned(); + assert!(matches!( + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter( + Event::DeliveryFeeFactorIncreased { .. } + ), + .. + }) + )); }); } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 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..2412bb0f3bb0cb14ea00c702d701e92e5286cb43 --- /dev/null +++ b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs @@ -0,0 +1,267 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as inbound +//! bridge messages dispatcher. Internally, it just forwards inbound blob to the +//! XCM-level blob dispatcher, which pushes message to some other queue (e.g. +//! to HRMP queue with the sibling target chain). +//! +//! This code is executed at the target bridge hub. + +use crate::{Config, Pallet, LOG_TARGET}; + +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + LaneId, +}; +use bp_runtime::messages::MessageDispatchResult; +use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload}; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt}; +use scale_info::TypeInfo; +use sp_runtime::SaturatedConversion; +use xcm::prelude::*; +use xcm_builder::{DispatchBlob, DispatchBlobError}; + +/// Message dispatch result type for single message. +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + /// We've been unable to decode message payload. + InvalidPayload, + /// Message has been dispatched. + Dispatched, + /// Message has **NOT** been dispatched because of given error. + NotDispatched(#[codec(skip)] Option), +} + +/// An easy way to access associated messages pallet weights. +type MessagesPalletWeights = + >::BridgeMessagesPalletInstance>>::WeightInfo; + +impl, I: 'static> MessageDispatch for Pallet +where + T: BridgeMessagesConfig, +{ + type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; + + fn is_active(lane: LaneId) -> bool { + Pallet::::bridge_by_lane_id(&lane) + .and_then(|(_, bridge)| bridge.bridge_origin_relative_location.try_as().cloned().ok()) + .map(|recipient: Location| !T::LocalXcmChannelManager::is_congested(&recipient)) + .unwrap_or(false) + } + + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + match message.data.payload { + Ok(ref payload) => { + let payload_size = payload.encoded_size().saturated_into(); + MessagesPalletWeights::::message_dispatch_weight(payload_size) + }, + Err(_) => Weight::zero(), + } + } + + fn dispatch( + message: DispatchMessage, + ) -> MessageDispatchResult { + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - payload error: {e:?} for lane_id: {} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + return MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + } + }, + }; + let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::NotDispatched(Some(e)) + }, + }; + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + + use bp_messages::{target_chain::DispatchMessageData, MessageKey}; + use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState}; + use frame_support::assert_ok; + use pallet_bridge_messages::InboundLaneStorage; + use xcm_executor::traits::ConvertLocation; + + fn bridge() -> (Box, LaneId) { + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + (locations, lane_id) + } + + fn run_test_with_opened_bridge(test: impl FnOnce()) { + run_test(|| { + let (bridge, lane_id) = bridge(); + + if !Bridges::::contains_key(bridge.bridge_id()) { + // insert bridge + Bridges::::insert( + bridge.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + bridge.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + bridge.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + bridge.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location( + bridge.bridge_origin_relative_location(), + ) + .expect("valid accountId"), + deposit: 0, + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, bridge.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + test(); + }); + } + + fn invalid_message() -> DispatchMessage> { + DispatchMessage { + key: MessageKey { lane_id: LaneId::new(1, 2), nonce: 1 }, + data: DispatchMessageData { payload: Err(codec::Error::from("test")) }, + } + } + + fn valid_message() -> DispatchMessage> { + DispatchMessage { + key: MessageKey { lane_id: LaneId::new(1, 2), nonce: 1 }, + data: DispatchMessageData { payload: Ok(vec![42]) }, + } + } + + #[test] + fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() { + run_test_with_opened_bridge(|| { + TestLocalXcmChannelManager::make_congested(); + assert!(!XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() { + run_test_with_opened_bridge(|| { + assert!(XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero()); + }); + } + + #[test] + fn dispatch_weight_is_non_zero_if_we_have_decoded_message() { + run_test(|| { + assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero()); + }); + } + + #[test] + fn message_is_not_dispatched_when_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(invalid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + }, + ); + assert!(!TestBlobDispatcher::is_dispatched()); + }); + } + + #[test] + fn message_is_dispatched_when_we_have_decoded_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(valid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched, + }, + ); + assert!(TestBlobDispatcher::is_dispatched()); + }); + } +} diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs index 5bca3ad8e2777c2e9243867fa491922592482c9e..b42ae1e267f4be55d5fd0efff7e39cd95bc19cc0 100644 --- a/bridges/modules/xcm-bridge-hub/src/exporter.rs +++ b/bridges/modules/xcm-bridge-hub/src/exporter.rs @@ -22,14 +22,29 @@ use crate::{Config, Pallet, LOG_TARGET}; -use bp_messages::source_chain::MessagesBridge; -use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bridge_runtime_common::messages_xcm_extension::{LocalXcmQueueManager, SenderAndLane}; -use pallet_bridge_messages::{Config as BridgeMessagesConfig, Pallet as BridgeMessagesPallet}; +use crate::{BridgeOf, Bridges}; + +use bp_messages::{ + source_chain::{MessagesBridge, OnMessagesDelivered}, + LaneId, MessageNonce, +}; +use bp_xcm_bridge_hub::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload}; +use frame_support::{ensure, traits::Get}; +use pallet_bridge_messages::{ + Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet, +}; use xcm::prelude::*; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::ExportXcm; +/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we +/// suspend a bridge. +const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; + +/// After we have suspended the bridge, we wait until number of messages in the outbound bridge +/// queue drops to this count, before sending resuming the bridge. +const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; + /// An easy way to access `HaulBlobExporter`. pub type PalletAsHaulBlobExporter = HaulBlobExporter< DummyHaulBlob, @@ -45,7 +60,8 @@ where T: BridgeMessagesConfig, { type Ticket = ( - SenderAndLane, + BridgeId, + BridgeOf, as MessagesBridge>::SendMessageArgs, XcmHash, ); @@ -57,12 +73,46 @@ where destination: &mut Option, message: &mut Option>, ) -> Result<(Self::Ticket, Assets), SendError> { - // Find supported lane_id. - let sender_and_lane = Self::lane_for( - universal_source.as_ref().ok_or(SendError::MissingArgument)?, - (&network, destination.as_ref().ok_or(SendError::MissingArgument)?), - ) - .ok_or(SendError::NotApplicable)?; + log::trace!( + target: LOG_TARGET, + "Validate for network: {network:?}, channel: {channel:?}, universal_source: {universal_source:?}, destination: {destination:?}" + ); + + // `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so + // let's save them before + let bridge_origin_universal_location = + universal_source.clone().take().ok_or(SendError::MissingArgument)?; + // Note: watch out this is `ExportMessage::destination`, which is relative to the `network`, + // which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with + // `Self::bridge_locations` which requires **universal** location for destination. + let bridge_destination_universal_location = { + let dest = destination.clone().take().ok_or(SendError::MissingArgument)?; + match dest.global_consensus() { + Ok(dest_network) => { + log::trace!( + target: LOG_TARGET, + "Destination: {dest:?} is already universal, checking dest_network: {dest_network:?} and network: {network:?} if matches: {:?}", + dest_network == network + ); + ensure!(dest_network == network, SendError::Unroutable); + // ok, `dest` looks like a universal location, so let's use it + dest + }, + Err(_) => { + // `dest` is not a universal location, so we need to prepend it with + // `GlobalConsensus`. + dest.pushed_front_with(GlobalConsensus(network)).map_err(|error_data| { + log::error!( + target: LOG_TARGET, + "Destination: {:?} is not a universal and prepending with {:?} failed!", + error_data.0, + error_data.1, + ); + SendError::Unroutable + })? + }, + } + }; // check if we are able to route the message. We use existing `HaulBlobExporter` for that. // It will make all required changes and will encode message properly, so that the @@ -75,43 +125,215 @@ where message, )?; - let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) + // prepare the origin relative location + let bridge_origin_relative_location = + bridge_origin_universal_location.relative_to(&T::UniversalLocation::get()); + + // then we are able to compute the `BridgeId` and find `LaneId` used to send messages + let locations = Self::bridge_locations( + bridge_origin_relative_location, + bridge_destination_universal_location.into(), + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Validate `bridge_locations` with error: {e:?}", + ); + SendError::Unroutable + })?; + let bridge = Self::bridge(locations.bridge_id()).ok_or(SendError::Unroutable)?; + + let bridge_message = MessagesPallet::::validate_message(bridge.lane_id, &blob) .map_err(|e| { - log::debug!( + match e { + Error::LanesManager(ref ei) => + log::error!(target: LOG_TARGET, "LanesManager: {ei:?}"), + Error::MessageRejectedByPallet(ref ei) => + log::error!(target: LOG_TARGET, "MessageRejectedByPallet: {ei:?}"), + Error::ReceptionConfirmation(ref ei) => + log::error!(target: LOG_TARGET, "ReceptionConfirmation: {ei:?}"), + _ => (), + }; + + log::error!( target: LOG_TARGET, - "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", + "XCM message {:?} cannot be exported because of bridge error: {:?} on bridge {:?} and laneId: {:?}", id, e, - sender_and_lane.lane, + locations, + bridge.lane_id, ); SendError::Transport("BridgeValidateError") })?; - Ok(((sender_and_lane, bridge_message, id), price)) + Ok(((*locations.bridge_id(), bridge, bridge_message, id), price)) } - fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result { - let lane_id = sender_and_lane.lane; + fn deliver( + (bridge_id, bridge, bridge_message, id): Self::Ticket, + ) -> Result { let artifacts = MessagesPallet::::send_message(bridge_message); log::info!( target: LOG_TARGET, - "XCM message {:?} has been enqueued at bridge {:?} with nonce {}", + "XCM message {:?} has been enqueued at bridge {:?} and lane_id: {:?} with nonce {}", id, - lane_id, + bridge_id, + bridge.lane_id, artifacts.nonce, ); - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_message_enqueued( - &sender_and_lane, - artifacts.enqueued_messages, - ); + // maybe we need switch to congested state + Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages); Ok(id) } } +impl, I: 'static> OnMessagesDelivered for Pallet { + fn on_messages_delivered(lane_id: LaneId, enqueued_messages: MessageNonce) { + Self::on_bridge_messages_delivered(lane_id, enqueued_messages); + } +} + +impl, I: 'static> Pallet { + /// Called when new message is pushed onto outbound bridge queue. + fn on_bridge_message_enqueued( + bridge_id: BridgeId, + bridge: BridgeOf, + enqueued_messages: MessageNonce, + ) { + // if the bridge queue is not congested, we don't want to do anything + let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; + if !is_congested { + return + } + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/2006 we either need fishermens + // to watch this rule violation (suspended, but keep sending new messages), or we need a + // hard limit for that like other XCM queues have + + // check if the lane is already suspended. If it is, do nothing. We still accept new + // messages to the suspended bridge, hoping that it'll be actually resumed soon + if bridge.state == BridgeState::Suspended { + return + } + + // else - suspend the bridge + let bridge_origin_relative_location = match bridge.bridge_origin_relative_location.try_as() + { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(_) => { + log::debug!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} origin location {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + + return + }, + }; + let suspend_result = + T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id); + match suspend_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Suspended the bridge {:?}, originated by the {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + }, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to suspended the bridge {:?}, originated by the {:?}: {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and remember that we have suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + } + + /// Must be called whenever we receive a message delivery confirmation. + fn on_bridge_messages_delivered(lane_id: LaneId, enqueued_messages: MessageNonce) { + // if the bridge queue is still congested, we don't want to do anything + let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; + if is_congested { + return + } + + // if we have not suspended the bridge before (or it is closed), we don't want to do + // anything + let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) { + Some(bridge) if bridge.1.state == BridgeState::Suspended => bridge, + _ => { + // if there is no bridge or it has been closed, then we don't need to send resume + // signal to the local origin - it has closed bridge itself, so it should have + // alrady pruned everything else + return + }, + }; + + // else - resume the bridge + let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into(); + let bridge_origin_relative_location = match bridge_origin_relative_location { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} location for lane_id: {:?}, error {:?}", + bridge_id, + lane_id, + e, + ); + + return + }, + }; + + let resume_result = + T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id); + match resume_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Resumed the bridge {:?} and lane_id: {:?}, originated by the {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + ); + }, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to resume the bridge {:?} and lane_id: {:?}, originated by the {:?}: {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and forget that we have previously suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Opened; + }); + } +} + /// Dummy implementation of the `HaulBlob` trait that is never called. /// /// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that @@ -130,30 +352,203 @@ impl HaulBlob for DummyHaulBlob { #[cfg(test)] mod tests { use super::*; - use crate::mock::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + use bp_runtime::RangeInclusiveExt; + use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState}; use frame_support::assert_ok; - use xcm_executor::traits::export_xcm; + use pallet_bridge_messages::InboundLaneStorage; + use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter}; + use xcm_executor::traits::{export_xcm, ConvertLocation}; fn universal_source() -> InteriorLocation { - [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() + SiblingUniversalLocation::get() } fn bridged_relative_destination() -> InteriorLocation { BridgedRelativeDestination::get() } + fn bridged_universal_destination() -> InteriorLocation { + BridgedUniversalDestination::get() + } + + fn open_lane() -> (BridgeLocations, LaneId) { + // open expected outbound lane + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if !Bridges::::contains_key(locations.bridge_id()) { + // insert bridge + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new(SiblingLocation::get().into()), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location( + locations.bridge_origin_relative_location(), + ) + .expect("valid accountId"), + deposit: 0, + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, locations.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + (*locations, lane_id) + } + + fn open_lane_and_send_regular_message() -> (BridgeId, LaneId) { + let (locations, lane_id) = open_lane(); + + // now let's try to enqueue message using our `ExportXcm` implementation + export_xcm::( + BridgedRelayNetwork::get(), + 0, + locations.bridge_origin_universal_location().clone(), + locations.bridge_destination_universal_location().clone(), + vec![Instruction::ClearOrigin].into(), + ) + .unwrap(); + + (*locations.bridge_id(), lane_id) + } + #[test] - fn export_works() { + fn exporter_works() { run_test(|| { - assert_ok!(export_xcm::( - BridgedRelayNetwork::get(), - 0, - universal_source(), - bridged_relative_destination(), - vec![Instruction::ClearOrigin].into(), - )); - }) + let (_, lane_id) = open_lane_and_send_regular_message(); + + // double check that the message has been pushed to the expected lane + // (it should already been checked during `send_message` call) + assert!(!LanesManagerOf::::new() + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD { + open_lane_and_send_regular_message(); + } + + open_lane_and_send_regular_message(); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + }); + } + + #[test] + fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD { + open_lane_and_send_regular_message(); + } + + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + + open_lane_and_send_regular_message(); + assert!(TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Suspended); + }); + } + + #[test] + fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Suspended); + }); + } + + #[test] + fn bridge_is_not_resumed_if_it_was_not_suspended_before() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn bridge_is_resumed_when_enough_messages_are_delivered() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD, + ); + + assert!(TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(bridge_id).unwrap().state, BridgeState::Opened); + }); } #[test] @@ -186,23 +581,56 @@ mod tests { #[test] fn exporter_computes_correct_lane_id() { run_test(|| { - let expected_lane_id = TEST_LANE_ID; + assert_ne!(bridged_universal_destination(), bridged_relative_destination()); - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut Some(bridged_relative_destination()), - &mut Some(Vec::new().into()), - ) - .unwrap() - .0 - .0 - .lane, - expected_lane_id, - ); - }) + let locations = BridgeLocations::bridge_locations( + UniversalLocation::get(), + SiblingLocation::get(), + bridged_universal_destination(), + BridgedRelayNetwork::get(), + ) + .unwrap(); + let expected_bridge_id = locations.bridge_id(); + let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if LanesManagerOf::::new() + .create_outbound_lane(expected_lane_id) + .is_ok() + { + Bridges::::insert( + expected_bridge_id, + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: [0u8; 32].into(), + deposit: 0, + lane_id: expected_lane_id, + }, + ); + } + + let ticket = XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + // Note: The `ExportMessage` expects relative `InteriorLocation` in the + // `BridgedRelayNetwork`. + &mut Some(bridged_relative_destination()), + &mut Some(Vec::new().into()), + ) + .unwrap() + .0; + assert_eq!(&ticket.0, expected_bridge_id); + assert_eq!(ticket.1.lane_id, expected_lane_id); + }); } #[test] @@ -210,30 +638,44 @@ mod tests { run_test(|| { // valid routable destination let dest = Location::new(2, BridgedUniversalDestination::get()); - let expected_lane_id = TEST_LANE_ID; + + // open bridge + let (_, expected_lane_id) = open_lane(); // check before - no messages assert_eq!( pallet_bridge_messages::Pallet::::outbound_lane_data( expected_lane_id ) + .unwrap() .queued_messages() .saturating_len(), 0 ); + // send `ExportMessage(message)` by `UnpaidRemoteExporter`. + TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); + assert_ok!(send_xcm::< + UnpaidRemoteExporter< + NetworkExportTable, + TestExportXcmWithXcmOverBridge, + UniversalLocation, + >, + >(dest.clone(), Xcm::<()>::default())); + // send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`. TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); - assert_ok!(send_xcm::(dest, Xcm::<()>::default())); + assert_ok!(send_xcm::(dest.clone(), Xcm::<()>::default())); // check after - a message ready to be relayed assert_eq!( pallet_bridge_messages::Pallet::::outbound_lane_data( expected_lane_id ) + .unwrap() .queued_messages() .saturating_len(), - 1 + 2 ); }) } diff --git a/bridges/modules/xcm-bridge-hub/src/lib.rs b/bridges/modules/xcm-bridge-hub/src/lib.rs index 60b988497fc59e94cbfe1a6e30cd6f3039d8c331..02d578386a7501894ac2b96400578ecf22f2ff77 100644 --- a/bridges/modules/xcm-bridge-hub/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub/src/lib.rs @@ -14,19 +14,156 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Module that adds XCM support to bridge pallets. +//! Module that adds XCM support to bridge pallets. The pallet allows to dynamically +//! open and close bridges between local (to this pallet location) and remote XCM +//! destinations. +//! +//! The `pallet_xcm_bridge_hub` pallet is used to manage (open, close) bridges between chains from +//! different consensuses. The new extrinsics `fn open_bridge` and `fn close_bridge` are introduced. +//! Other chains can manage channels with different bridged global consensuses. +//! +//! # Concept of `lane` and `LaneId` +//! +//! There is another `pallet_bridge_messages` pallet that handles inbound/outbound lanes for +//! messages. Each lane is a unique connection between two chains from different consensuses and is +//! identified by `LaneId`. `LaneId` is generated once when a new bridge is requested by `fn +//! open_bridge`. It is generated by `BridgeLocations::calculate_lane_id` based on the following +//! parameters: +//! - Source `bridge_origin_universal_location` (latest XCM) +//! - Destination `bridge_destination_universal_location` (latest XCM) +//! - XCM version (both sides of the bridge must use the same parameters to generate the same +//! `LaneId`) +//! - `bridge_origin_universal_location`, `bridge_destination_universal_location` is converted to +//! the `Versioned*` structs +//! +//! `LaneId` is expected to never change because: +//! - We need the same `LaneId` on both sides of the bridge, as `LaneId` is part of the message key +//! proofs. +//! - Runtime upgrades are entirely asynchronous. +//! - We already have a running production Polkadot/Kusama bridge that uses `LaneId([0, 0, 0, 0])`. +//! +//! `LaneId` is backward compatible, meaning it can be encoded/decoded from the older format `[u8; +//! 4]` used for static lanes, as well as the new format `H256` generated by +//! `BridgeLocations::calculate_lane_id`. +//! +//! # Concept of `bridge` and `BridgeId` +//! +//! The `pallet_xcm_bridge_hub` pallet needs to store some metadata about opened bridges. The bridge +//! (or bridge metadata) is stored under the `BridgeId` key. +//! +//! `BridgeId` is generated from `bridge_origin_relative_location` and +//! `bridge_origin_universal_location` using the `latest` XCM structs. `BridgeId` is not transferred +//! over the bridge; it is only important for local consensus. It essentially serves as an index/key +//! to bridge metadata. All the XCM infrastructure around `XcmExecutor`, `SendXcm`, `ExportXcm` use +//! the `latest` XCM, so `BridgeId` must remain compatible with the `latest` XCM. For example, we +//! have an `ExportXcm` implementation in `exporter.rs` that handles the `ExportMessage` instruction +//! with `universal_source` and `destination` (latest XCM), so we need to create `BridgeId` and the +//! corresponding `LaneId`. +//! +//! # Migrations and State +//! +//! This pallet implements `try_state`, ensuring compatibility and checking everything so we know if +//! any migration is needed. `do_try_state` checks for `BridgeId` compatibility, which is +//! recalculated on runtime upgrade. Upgrading to a new XCM version should not break anything, +//! except removing older XCM versions. In such cases, we need to add migration for `BridgeId` and +//! stored `Versioned*` structs and update `LaneToBridge` mapping, but this won't affect `LaneId` +//! over the bridge. +//! +//! # How to Open a Bridge? +//! +//! The `pallet_xcm_bridge_hub` pallet has the extrinsic `fn open_bridge` and an important +//! configuration `pallet_xcm_bridge_hub::Config::OpenBridgeOrigin`, which translates the call's +//! origin to the XCM `Location` and converts it to the `bridge_origin_universal_location`. With the +//! current setup, this origin/location is expected to be either the relay chain or a sibling +//! parachain as one side of the bridge. Another parameter is +//! `bridge_destination_universal_location`, which is the other side of the bridge from a different +//! global consensus. +//! +//! Every bridge between two XCM locations has a dedicated lane in associated +//! messages pallet. Assuming that this pallet is deployed at the bridge hub +//! parachain and there's a similar pallet at the bridged network, the dynamic +//! bridge lifetime is as follows: +//! +//! 1) the sibling parachain opens a XCMP channel with this bridge hub; +//! +//! 2) the sibling parachain funds its sovereign parachain account at this bridge hub. It shall hold +//! enough funds to pay for the bridge (see `BridgeDeposit`); +//! +//! 3) the sibling parachain opens the bridge by sending XCM `Transact` instruction with the +//! `open_bridge` call. The `BridgeDeposit` amount is reserved on the sovereign account of +//! sibling parachain; +//! +//! 4) at the other side of the bridge, the same thing (1, 2, 3) happens. Parachains that need to +//! connect over the bridge need to coordinate the moment when they start sending messages over +//! the bridge. Otherwise they may lose messages and/or bundled assets; +//! +//! 5) when either side wants to close the bridge, it sends the XCM `Transact` with the +//! `close_bridge` call. The bridge is closed immediately if there are no queued messages. +//! Otherwise, the owner must repeat the `close_bridge` call to prune all queued messages first. +//! +//! The pallet doesn't provide any mechanism for graceful closure, because it always involves +//! some contract between two connected chains and the bridge hub knows nothing about that. It +//! is the task for the connected chains to make sure that all required actions are completed +//! before the closure. In the end, the bridge hub can't even guarantee that all messages that +//! are delivered to the destination, are processed in the way their sender expects. So if we +//! can't guarantee that, we shall not care about more complex procedures and leave it to the +//! participating parties. +//! +//! # Example +//! +//! Example of opening a bridge between some random parachains from Polkadot and Kusama: +//! +//! 0. Let's have: +//! - BridgeHubPolkadot with `UniversalLocation` = `[GlobalConsensus(Polkadot), Parachain(1002)]` +//! - BridgeHubKusama with `UniversalLocation` = `[GlobalConsensus(Kusama), Parachain(1002)]` +//! 1. The Polkadot local sibling parachain `Location::new(1, Parachain(1234))` must send some DOTs +//! to its sovereign account on BridgeHubPolkadot to cover `BridgeDeposit`, fees for `Transact`, +//! and the existential deposit. +//! 2. Send a call to the BridgeHubPolkadot from the local sibling parachain: `Location::new(1, +//! Parachain(1234))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Kusama), +//! Parachain(4567)].into()), ); ) ``` +//! 3. Check the stored bridge metadata and generated `LaneId`. +//! 4. The Kusama local sibling parachain `Location::new(1, Parachain(4567))` must send some KSMs to +//! its sovereign account +//! on BridgeHubKusama to cover `BridgeDeposit`, fees for `Transact`, and the existential deposit. +//! 5. Send a call to the BridgeHubKusama from the local sibling parachain: `Location::new(1, +//! Parachain(4567))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( +//! VersionedInteriorLocation::V4([GlobalConsensus(Polkadot), Parachain(1234)].into()), ); ) ``` +//! 6. Check the stored bridge metadata and generated `LaneId`. +//! 7. Both `LaneId`s from steps 3 and 6 must be the same (see above _Concept of `lane` and +//! `LaneId`_). +//! 8. Run the bridge messages relayer for `LaneId`. +//! 9. Send messages from both sides. +//! +//! The opening bridge holds the configured `BridgeDeposit` from the origin's sovereign account, but +//! this deposit is returned when the bridge is closed with `fn close_bridge`. #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -use bridge_runtime_common::messages_xcm_extension::XcmBlobHauler; -use pallet_bridge_messages::Config as BridgeMessagesConfig; +use bp_messages::{LaneId, LaneState, MessageNonce}; +use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt}; +pub use bp_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; +use bp_xcm_bridge_hub::{BridgeLocations, BridgeLocationsError, LocalXcmChannelManager}; +use frame_support::{traits::fungible::MutateHold, DefaultNoBound}; +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError}; +use sp_runtime::traits::Zero; +use sp_std::{boxed::Box, vec::Vec}; use xcm::prelude::*; +use xcm_builder::DispatchBlob; +use xcm_executor::traits::ConvertLocation; +pub use bp_xcm_bridge_hub::XcmAsPlainPayload; +pub use dispatcher::XcmBlobMessageDispatchResult; pub use exporter::PalletAsHaulBlobExporter; pub use pallet::*; +mod dispatcher; mod exporter; +pub mod migration; mod mock; /// The target that will be used when publishing logs related to this pallet. @@ -35,15 +172,29 @@ pub const LOG_TARGET: &str = "runtime::bridge-xcm"; #[frame_support::pallet] pub mod pallet { use super::*; - use bridge_runtime_common::messages_xcm_extension::SenderAndLane; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::BlockNumberFor; + use frame_support::{ + pallet_prelude::*, + traits::{tokens::Precision, Contains}, + }; + use frame_system::pallet_prelude::{BlockNumberFor, *}; + + /// The reason for this pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The funds are held as a deposit for opened bridge. + #[codec(index = 0)] + BridgeDeposit, + } #[pallet::config] #[pallet::disable_frame_system_supertrait_check] pub trait Config: BridgeMessagesConfig { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Runtime's universal location. type UniversalLocation: Get; // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and @@ -56,63 +207,1513 @@ pub mod pallet { /// `BridgedNetworkId` consensus. type BridgeMessagesPalletInstance: 'static; - /// Price of single message export to the bridged consensus (`Self::BridgedNetworkId`). + /// Price of single message export to the bridged consensus (`Self::BridgedNetwork`). type MessageExportPrice: Get; /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; - /// Get point-to-point links with bridged consensus (`Self::BridgedNetworkId`). - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type Lanes: Get>; - /// Support for point-to-point links - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type LanesSupport: XcmBlobHauler; + /// The origin that is allowed to call privileged operations on the pallet, e.g. open/close + /// bridge for location that coresponds to `Self::BridgeOriginAccountIdConverter` and + /// `Self::BridgedNetwork`. + type AdminOrigin: EnsureOrigin<::RuntimeOrigin>; + /// A set of XCM locations within local consensus system that are allowed to open + /// bridges with remote destinations. + type OpenBridgeOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Location, + >; + /// A converter between a location and a sovereign account. + type BridgeOriginAccountIdConverter: ConvertLocation>>; + + /// Amount of this chain native tokens that is reserved on the sibling parachain account + /// when bridge open request is registered. + #[pallet::constant] + type BridgeDeposit: Get>>; + /// Currency used to pay for bridge registration. + type Currency: MutateHold< + AccountIdOf>, + Balance = BalanceOf>, + Reason = Self::RuntimeHoldReason, + >; + /// The overarching runtime hold reason. + type RuntimeHoldReason: From>; + /// Do not hold `Self::BridgeDeposit` for the location of `Self::OpenBridgeOrigin`. + /// For example, it is possible to make an exception for a system parachain or relay. + type AllowWithoutBridgeDeposit: Contains; + + /// Local XCM channel manager. + type LocalXcmChannelManager: LocalXcmChannelManager; + /// XCM-level dispatcher for inbound bridge messages. + type BlobDispatcher: DispatchBlob; } + /// An alias for the bridge metadata. + pub type BridgeOf = Bridge>; + /// An alias for this chain. + pub type ThisChainOf = + pallet_bridge_messages::ThisChainOf>::BridgeMessagesPalletInstance>; + /// An alias for the associated lanes manager. + pub type LanesManagerOf = + pallet_bridge_messages::LanesManager>::BridgeMessagesPalletInstance>; + #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn integrity_test() { assert!( - Self::bridged_network_id().is_some(), + Self::bridged_network_id().is_ok(), "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`", T::BridgedNetwork::get() ) } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Open a bridge between two locations. + /// + /// The caller must be within the `T::OpenBridgeOrigin` filter (presumably: a sibling + /// parachain or a parent relay chain). The `bridge_destination_universal_location` must be + /// a destination within the consensus of the `T::BridgedNetwork` network. + /// + /// The `BridgeDeposit` amount is reserved on the caller account. This deposit + /// is unreserved after bridge is closed. + /// + /// The states after this call: bridge is `Opened`, outbound lane is `Opened`, inbound lane + /// is `Opened`. + #[pallet::call_index(0)] + #[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl + pub fn open_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> DispatchResult { + // check and compute required bridge locations and laneId + let xcm_version = bridge_destination_universal_location.identify_version(); + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "calculate_lane_id error: {e:?}", + ); + Error::::BridgeLocations(e) + })?; + + Self::do_open_bridge(locations, lane_id, true) + } + + /// Try to close the bridge. + /// + /// Can only be called by the "owner" of this side of the bridge, meaning that the + /// inbound XCM channel with the local origin chain is working. + /// + /// Closed bridge is a bridge without any traces in the runtime storage. So this method + /// first tries to prune all queued messages at the outbound lane. When there are no + /// outbound messages left, outbound and inbound lanes are purged. After that, funds + /// are returned back to the owner of this side of the bridge. + /// + /// The number of messages that we may prune in a single call is limited by the + /// `may_prune_messages` argument. If there are more messages in the queue, the method + /// prunes exactly `may_prune_messages` and exits early. The caller may call it again + /// until outbound queue is depleted and get his funds back. + /// + /// The states after this call: everything is either `Closed`, or purged from the + /// runtime storage. + #[pallet::call_index(1)] + #[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl + pub fn close_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + may_prune_messages: MessageNonce, + ) -> DispatchResult { + // compute required bridge locations + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1760 - may do refund here, if + // bridge/lanes are already closed + for messages that are not pruned + + // update bridge metadata - this also guarantees that the bridge is in the proper state + let bridge = + Bridges::::try_mutate_exists(locations.bridge_id(), |bridge| match bridge { + Some(bridge) => { + bridge.state = BridgeState::Closed; + Ok(bridge.clone()) + }, + None => Err(Error::::UnknownBridge), + })?; + + // close inbound and outbound lanes + let lanes_manager = LanesManagerOf::::new(); + let mut inbound_lane = lanes_manager + .any_state_inbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + let mut outbound_lane = lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + + // now prune queued messages + let mut pruned_messages = 0; + for _ in outbound_lane.queued_messages() { + if pruned_messages == may_prune_messages { + break + } + + outbound_lane.remove_oldest_unpruned_message(); + pruned_messages += 1; + } + + // if there are outbound messages in the queue, just update states and early exit + if !outbound_lane.queued_messages().is_empty() { + // update lanes state. Under normal circumstances, following calls shall never fail + inbound_lane.set_state(LaneState::Closed); + outbound_lane.set_state(LaneState::Closed); + + // write something to log + let enqueued_messages = outbound_lane.queued_messages().saturating_len(); + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + bridge.lane_id, + enqueued_messages, + ); + + // deposit the `ClosingBridge` event + Self::deposit_event(Event::::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + pruned_messages, + enqueued_messages, + }); + + return Ok(()) + } + + // else we have pruned all messages, so lanes and the bridge itself may gone + inbound_lane.purge(); + outbound_lane.purge(); + Bridges::::remove(locations.bridge_id()); + LaneToBridge::::remove(bridge.lane_id); + + // return deposit + let released_deposit = T::Currency::release( + &HoldReason::BridgeDeposit.into(), + &bridge.bridge_owner_account, + bridge.deposit, + Precision::BestEffort, + ) + .map_err(|e| { + // we can't do anything here - looks like funds have been (partially) unreserved + // before by someone else. Let's not fail, though - it'll be worse for the caller + log::error!( + target: LOG_TARGET, + "Failed to unreserve during the bridge {:?} closure with error: {e:?}", + locations.bridge_id(), + ); + e + }) + .ok() + .unwrap_or(BalanceOf::>::zero()); + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned", + locations.bridge_id(), + bridge.lane_id, + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit the `BridgePruned` event + Self::deposit_event(Event::::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + bridge_deposit: released_deposit, + pruned_messages, + }); + + Ok(()) + } } impl, I: 'static> Pallet { - /// Returns dedicated/configured lane identifier. - pub(crate) fn lane_for( - source: &InteriorLocation, - dest: (&NetworkId, &InteriorLocation), - ) -> Option { - let source = source.clone().relative_to(&T::UniversalLocation::get()); - - // Check that we have configured a point-to-point lane for 'source' and `dest`. - T::Lanes::get() - .into_iter() - .find_map(|(lane_source, (lane_dest_network, lane_dest))| { - if lane_source.location == source && - &lane_dest_network == dest.0 && - Self::bridged_network_id().as_ref() == Some(dest.0) && - &lane_dest == dest.1 - { - Some(lane_source) - } else { - None - } - }) + pub(crate) fn do_open_bridge( + locations: Box, + lane_id: LaneId, + create_lanes: bool, + ) -> Result<(), DispatchError> { + // reserve balance on the origin's sovereign account (if needed) + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .ok_or(Error::::InvalidBridgeOriginAccount)?; + let deposit = if T::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location(), + ) { + BalanceOf::>::zero() + } else { + let deposit = T::BridgeDeposit::get(); + T::Currency::hold( + &HoldReason::BridgeDeposit.into(), + &bridge_owner_account, + deposit, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to hold bridge deposit: {deposit:?} \ + from bridge_owner_account: {bridge_owner_account:?} derived from \ + bridge_origin_relative_location: {:?} with error: {e:?}", + locations.bridge_origin_relative_location(), + ); + Error::::FailedToReserveBridgeDeposit + })?; + deposit + }; + + // save bridge metadata + Bridges::::try_mutate(locations.bridge_id(), |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(BridgeOf:: { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit, + lane_id, + }); + Ok(()) + }, + })?; + // save lane to bridge mapping + LaneToBridge::::try_mutate(lane_id, |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(*locations.bridge_id()); + Ok(()) + }, + })?; + + if create_lanes { + // create new lanes. Under normal circumstances, following calls shall never fail + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .create_inbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + lanes_manager + .create_outbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + } + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit `BridgeOpened` event + Self::deposit_event(Event::::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: deposit, + local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone(), + ), + lane_id, + }); + + Ok(()) + } + } + + impl, I: 'static> Pallet { + /// Return bridge endpoint locations and dedicated lane identifier. This method converts + /// runtime `origin` argument to relative `Location` using the `T::OpenBridgeOrigin` + /// converter. + pub fn bridge_locations_from_origin( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> Result, sp_runtime::DispatchError> { + Self::bridge_locations( + T::OpenBridgeOrigin::ensure_origin(origin)?, + (*bridge_destination_universal_location) + .try_into() + .map_err(|_| Error::::UnsupportedXcmVersion)?, + ) + } + + /// Return bridge endpoint locations and dedicated **bridge** identifier (`BridgeId`). + pub fn bridge_locations( + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + ) -> Result, sp_runtime::DispatchError> { + BridgeLocations::bridge_locations( + T::UniversalLocation::get(), + bridge_origin_relative_location, + bridge_destination_universal_location, + Self::bridged_network_id()?, + ) + .map_err(|e| { + log::trace!( + target: LOG_TARGET, + "bridge_locations error: {e:?}", + ); + Error::::BridgeLocations(e).into() + }) + } + + /// Return bridge metadata by lane_id + pub fn bridge_by_lane_id(lane_id: &LaneId) -> Option<(BridgeId, BridgeOf)> { + LaneToBridge::::get(lane_id) + .and_then(|bridge_id| Self::bridge(bridge_id).map(|bridge| (bridge_id, bridge))) } + } + impl, I: 'static> Pallet { /// Returns some `NetworkId` if contains `GlobalConsensus` junction. - fn bridged_network_id() -> Option { + fn bridged_network_id() -> Result { match T::BridgedNetwork::get().take_first_interior() { - Some(GlobalConsensus(network)) => Some(network), - _ => None, + Some(GlobalConsensus(network)) => Ok(network), + _ => Err(Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeDestination, + ) + .into()), + } + } + } + + #[cfg(any(test, feature = "try-runtime", feature = "std"))] + impl, I: 'static> Pallet { + /// Ensure the correctness of the state of this pallet. + pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_std::collections::btree_set::BTreeSet; + + let mut lanes = BTreeSet::new(); + + // check all known bridge configurations + for (bridge_id, bridge) in Bridges::::iter() { + lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?); + } + ensure!( + lanes.len() == Bridges::::iter().count(), + "Invalid `Bridges` configuration, probably two bridges handle the same laneId!" + ); + ensure!( + lanes.len() == LaneToBridge::::iter().count(), + "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!" + ); + + // check connected `pallet_bridge_messages` state. + Self::do_try_state_for_messages() + } + + /// Ensure the correctness of the state of the bridge. + pub fn do_try_state_for_bridge( + bridge_id: BridgeId, + bridge: BridgeOf, + ) -> Result { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}"); + + // check `BridgeId` points to the same `LaneId` and vice versa. + ensure!( + Some(bridge_id) == LaneToBridge::::get(bridge.lane_id), + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!" + ); + + // check `pallet_bridge_messages` state for that `LaneId`. + let lanes_manager = LanesManagerOf::::new(); + ensure!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(), + "Inbound lane not found!", + ); + ensure!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(), + "Outbound lane not found!", + ); + + // check that `locations` are convertible to the `latest` XCM. + let bridge_origin_relative_location_as_latest: &Location = + bridge.bridge_origin_relative_location.try_as().map_err(|_| { + "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!" + })?; + let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + + // check `BridgeId` does not change + ensure!( + bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest), + "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!" + ); + + // check bridge account owner + ensure!( + T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account), + "`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!" + ); + + Ok(bridge.lane_id) + } + + /// Ensure the correctness of the state of the connected `pallet_bridge_messages` instance. + pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> { + // check that all `InboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::InboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!" + ); + } + + // check that all `OutboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::OutboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!" + ); } + + Ok(()) } } + + /// All registered bridges. + #[pallet::storage] + #[pallet::getter(fn bridge)] + pub type Bridges, I: 'static = ()> = + StorageMap<_, Identity, BridgeId, BridgeOf>; + /// All registered `lane_id` and `bridge_id` mappings. + #[pallet::storage] + pub type LaneToBridge, I: 'static = ()> = + StorageMap<_, Identity, LaneId, BridgeId>; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig, I: 'static = ()> { + /// Opened bridges. + /// + /// Keep in mind that we are **NOT** reserving any amount for the bridges opened at + /// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using + /// other pallets genesis configuration or some other means. + pub opened_bridges: Vec<(Location, InteriorLocation)>, + /// Dummy marker. + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData<(T, I)>, + } + + #[pallet::genesis_build] + impl, I: 'static> BuildGenesisConfig for GenesisConfig + where + T: frame_system::Config>>, + { + fn build(&self) { + for (bridge_origin_relative_location, bridge_destination_universal_location) in + &self.opened_bridges + { + let locations = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone().into(), + ) + .expect("Invalid genesis configuration"); + let lane_id = + locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"); + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .expect("Invalid genesis configuration"); + + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit: Zero::zero(), + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, locations.bridge_id()); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .create_inbound_lane(lane_id) + .expect("Invalid genesis configuration"); + lanes_manager + .create_outbound_lane(lane_id) + .expect("Invalid genesis configuration"); + } + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The bridge between two locations has been opened. + BridgeOpened { + /// Bridge identifier. + bridge_id: BridgeId, + /// Amount of deposit held. + bridge_deposit: BalanceOf>, + + /// Universal location of local bridge endpoint. + local_endpoint: Box, + /// Universal location of remote bridge endpoint. + remote_endpoint: Box, + /// Lane identifier. + lane_id: LaneId, + }, + /// Bridge is going to be closed, but not yet fully pruned from the runtime storage. + ClosingBridge { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: LaneId, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + /// Number of enqueued messages that need to be pruned in follow up calls. + enqueued_messages: MessageNonce, + }, + /// Bridge has been closed and pruned from the runtime storage. It now may be reopened + /// again by any participant. + BridgePruned { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: LaneId, + /// Amount of deposit released. + bridge_deposit: BalanceOf>, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + }, + } + + #[pallet::error] + pub enum Error { + /// Bridge locations error. + BridgeLocations(BridgeLocationsError), + /// Invalid local bridge origin account. + InvalidBridgeOriginAccount, + /// The bridge is already registered in this pallet. + BridgeAlreadyExists, + /// The local origin already owns a maximal number of bridges. + TooManyBridgesForLocalOrigin, + /// Trying to close already closed bridge. + BridgeAlreadyClosed, + /// Lanes manager error. + LanesManager(LanesManagerError), + /// Trying to access unknown bridge. + UnknownBridge, + /// The bridge origin can't pay the required amount for opening the bridge. + FailedToReserveBridgeDeposit, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use mock::*; + + use bp_messages::LaneId; + use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec}; + use frame_system::{EventRecord, Phase}; + use sp_core::H256; + use sp_runtime::TryRuntimeError; + + fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId { + let bridge_owner_account = + LocationToAccountId::convert_location(locations.bridge_origin_relative_location()) + .unwrap(); + assert_ok!(Balances::mint_into(&bridge_owner_account, balance)); + bridge_owner_account + } + + fn mock_open_bridge_from_with( + origin: RuntimeOrigin, + deposit: Balance, + with: InteriorLocation, + ) -> (BridgeOf, BridgeLocations) { + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + let bridge_owner_account = + fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get()); + Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap(); + + let bridge = Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit, + lane_id, + }; + Bridges::::insert(locations.bridge_id(), bridge.clone()); + LaneToBridge::::insert(bridge.lane_id, locations.bridge_id()); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(bridge.lane_id).unwrap(); + lanes_manager.create_outbound_lane(bridge.lane_id).unwrap(); + + assert_ok!(XcmOverBridge::do_try_state()); + + (bridge, *locations) + } + + fn mock_open_bridge_from( + origin: RuntimeOrigin, + deposit: Balance, + ) -> (BridgeOf, BridgeLocations) { + mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location()) + } + + fn enqueue_message(lane: LaneId) { + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .active_outbound_lane(lane) + .unwrap() + .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages")); + } + + #[test] + fn open_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn open_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn open_bridge_fails_if_destination_is_not_remote() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)] + .into() + ), + ), + Error::::BridgeLocations(BridgeLocationsError::DestinationIsLocal), + ); + }); + } + + #[test] + fn open_bridge_fails_if_outside_of_bridged_consensus() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [ + GlobalConsensus(NonBridgedRelayNetwork::get()), + Parachain(BRIDGED_ASSET_HUB_ID) + ] + .into() + ), + ), + Error::::BridgeLocations( + BridgeLocationsError::UnreachableDestination + ), + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_has_no_sovereign_account() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::origin_without_sovereign_account(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::InvalidBridgeOriginAccount, + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::FailedToReserveBridgeDeposit, + ); + }); + } + + #[test] + fn open_bridge_fails_if_it_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: [0u8; 32].into(), + deposit: 0, + lane_id, + }, + ); + + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeAlreadyExists, + ); + }) + } + + #[test] + fn open_bridge_fails_if_its_lanes_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + let lanes_manager = LanesManagerOf::::new(); + + lanes_manager.create_inbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::LanesManager(LanesManagerError::InboundLaneAlreadyExists), + ); + + lanes_manager.active_inbound_lane(lane_id).unwrap().purge(); + lanes_manager.create_outbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::LanesManager( + LanesManagerError::OutboundLaneAlreadyExists + ), + ); + }) + } + + #[test] + fn open_bridge_works() { + run_test(|| { + // in our test runtime, we expect that bridge may be opened by parent relay chain + // and any sibling parachain + let origins = [ + (OpenBridgeOrigin::parent_relay_chain_origin(), 0), + (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()), + ]; + + // check that every origin may open the bridge + let lanes_manager = LanesManagerOf::::new(); + let existential_deposit = ExistentialDeposit::get(); + for (origin, expected_deposit) in origins { + // reset events + System::set_block_number(1); + System::reset_events(); + + // compute all other locations + let xcm_version = xcm::latest::VERSION; + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new( + VersionedInteriorLocation::from(bridged_asset_hub_universal_location()) + .into_version(xcm_version) + .expect("valid conversion"), + ), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm_version).unwrap(); + + // ensure that there's no bridge and lanes in the storage + assert_eq!(Bridges::::get(locations.bridge_id()), None); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(lane_id), None); + + // give enough funds to the sovereign account of the bridge origin + let bridge_owner_account = fund_origin_sovereign_account( + &locations, + expected_deposit + existential_deposit, + ); + assert_eq!( + Balances::free_balance(&bridge_owner_account), + expected_deposit + existential_deposit + ); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0); + + // now open the bridge + assert_ok!(XcmOverBridge::open_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + )); + + // ensure that everything has been set up in the runtime storage + assert_eq!( + Bridges::::get(locations.bridge_id()), + Some(Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into() + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: expected_deposit, + lane_id + }), + ); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + LaneToBridge::::get(lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit); + + // ensure that the proper event is deposited + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: expected_deposit, + local_endpoint: Box::new( + locations.bridge_origin_universal_location().clone() + ), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone() + ), + lane_id + }), + topics: vec![], + }), + ); + + // check state + assert_ok!(XcmOverBridge::do_try_state()); + } + }); + } + + #[test] + fn close_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn close_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn close_bridge_fails_if_its_lanes_are_unknown() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + + let (_, locations) = mock_open_bridge_from(origin.clone(), 0); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); + } + + #[test] + fn close_bridge_works() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let expected_deposit = BridgeDeposit::get(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit); + System::set_block_number(1); + + // remember owner balances + let free_balance = Balances::free_balance(&bridge.bridge_owner_account); + let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account); + + // enqueue some messages + for _ in 0..32 { + enqueue_message(bridge.lane_id); + } + + // now call the `close_bridge`, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 16, + ),); + + // as a result, the bridge and lanes are switched to the `Closed` state, some messages + // are pruned, but funds are not unreserved + let lanes_manager = LanesManagerOf::::new(); + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(16) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + pruned_messages: 16, + enqueued_messages: 16, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 8, + ),); + + // nothing is changed (apart from the pruned messages) + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(8) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + pruned_messages: 8, + enqueued_messages: 8, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again that will prune all remaining messages and the + // bridge + assert_ok!(XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 9, + ),); + + // there's no traces of bridge in the runtime storage and funds are unreserved + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + None + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(bridge.lane_id), None); + assert_eq!( + Balances::free_balance(&bridge.bridge_owner_account), + free_balance + reserved_balance + ); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id, + bridge_deposit: expected_deposit, + pruned_messages: 8, + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn do_try_state_works() { + use sp_runtime::Either; + + let bridge_origin_relative_location = SiblingLocation::get(); + let bridge_origin_universal_location = SiblingUniversalLocation::get(); + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let bridge_owner_account = + LocationToAccountId::convert_location(&bridge_origin_relative_location) + .expect("valid accountId"); + let bridge_owner_account_mismatch = + LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId"); + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here); + let lane_id = LaneId::from_inner(Either::Left(H256::default())); + let lane_id_mismatch = LaneId::from_inner(Either::Left(H256::from([1u8; 32]))); + + let test_bridge_state = |id, + bridge, + (lane_id, bridge_id), + (inbound_lane_id, outbound_lane_id), + expected_error: Option| { + Bridges::::insert(id, bridge); + LaneToBridge::::insert(lane_id, bridge_id); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(inbound_lane_id).unwrap(); + lanes_manager.create_outbound_lane(outbound_lane_id).unwrap(); + + let result = XcmOverBridge::do_try_state(); + if let Some(e) = expected_error { + assert_err!(result, e); + } else { + assert_ok!(result); + } + }; + let cleanup = |bridge_id, lane_ids| { + Bridges::::remove(bridge_id); + for lane_id in lane_ids { + LaneToBridge::::remove(lane_id); + let lanes_manager = LanesManagerOf::::new(); + if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) { + lane.purge(); + } + if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) { + lane.purge(); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + }; + + run_test(|| { + // ok state + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + None, + ); + cleanup(bridge_id, vec![lane_id]); + + // error - missing `LaneToBridge` mapping + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other( + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!", + )), + ); + cleanup(bridge_id, vec![lane_id]); + + // error bridge owner account cannot be calculated + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account_mismatch.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")), + ); + cleanup(bridge_id, vec![lane_id]); + + // error when (bridge_origin_universal_location + bridge_destination_universal_location) + // produces different `BridgeId` + test_bridge_state( + bridge_id_mismatch, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account_mismatch.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!")), + ); + cleanup(bridge_id_mismatch, vec![lane_id]); + + // missing inbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id_mismatch, lane_id), + Some(TryRuntimeError::Other("Inbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing outbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id_mismatch), + Some(TryRuntimeError::Other("Outbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing bridge for inbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_inbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + + // missing bridge for outbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_outbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + }); + } + + #[test] + fn ensure_encoding_compatibility() { + use codec::Encode; + + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let may_prune_messages = 13; + + assert_eq!( + bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ) + } + .encode(), + Call::::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ) + } + .encode() + ); + assert_eq!( + bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode(), + Call::::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode() + ); + } } diff --git a/bridges/modules/xcm-bridge-hub/src/migration.rs b/bridges/modules/xcm-bridge-hub/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..c9d8b67176a5114668f2a7a2ac9e2aaaa88f0fdc --- /dev/null +++ b/bridges/modules/xcm-bridge-hub/src/migration.rs @@ -0,0 +1,146 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::{Config, Pallet, LOG_TARGET}; +use bp_messages::LaneId; +use frame_support::{ + traits::{Get, OnRuntimeUpgrade, StorageVersion}, + weights::Weight, +}; +use xcm::prelude::{InteriorLocation, Location}; + +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + +/// This migration does not modify storage but can be used to open a bridge and link it to the +/// specified LaneId. This is useful when we want to open a bridge and use a custom LaneId instead +/// of the pre-calculated one provided by the `fn open_bridge extrinsic`. +/// Or perhaps if you want to ensure that your runtime (e.g., for testing) always has an open +/// bridge. +pub struct OpenBridgeForLane< + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, +>( + core::marker::PhantomData<( + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, + )>, +); +impl< + T: Config, + I: 'static, + Lane: Get, + CreateLane: Get, + SourceRelativeLocation: Get, + BridgedUniversalLocation: Get, + > OnRuntimeUpgrade + for OpenBridgeForLane +{ + fn on_runtime_upgrade() -> Weight { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + let create_lane = CreateLane::get(); + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - going to open bridge with lane_id: {lane_id:?} (create_lane: {create_lane:?}) \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + let locations = match Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) { + Ok(locations) => locations, + Err(e) => { + log::error!( + target: LOG_TARGET, + "OpenBridgeForLane - on_runtime_upgrade failed to construct bridge_locations with error: {e:?}" + ); + return T::DbWeight::get().reads(0) + }, + }; + + // check if already exists + if let Some((bridge_id, bridge)) = Pallet::::bridge_by_lane_id(&lane_id) { + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - bridge: {bridge:?} with bridge_id: {bridge_id:?} already exist for lane_id: {lane_id:?}!" + ); + if &bridge_id != locations.bridge_id() { + log::warn!( + target: LOG_TARGET, + "OpenBridgeForLane - check you parameters, because a different bridge: {bridge:?} \ + with bridge_id: {bridge_id:?} exist for lane_id: {lane_id:?} for requested \ + bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?} !", + ); + } + + return T::DbWeight::get().reads(2) + } + + if let Err(e) = Pallet::::do_open_bridge(locations, lane_id, create_lane) { + log::error!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge failed with error: {e:?}"); + T::DbWeight::get().reads(6) + } else { + log::info!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge passed!"); + T::DbWeight::get().reads_writes(6, 4) + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + + // check that requested bridge is stored + let Ok(locations) = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) else { + return Err(sp_runtime::DispatchError::Other("Invalid locations!")) + }; + let Some((bridge_id, _)) = Pallet::::bridge_by_lane_id(&lane_id) else { + return Err(sp_runtime::DispatchError::Other("Missing bridge!")) + }; + frame_support::ensure!( + locations.bridge_id() == &bridge_id, + "Bridge is not stored correctly!" + ); + + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - post_upgrade found opened bridge with lane_id: {lane_id:?} \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + Ok(()) + } +} diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 0cca32ba9e5f55a80baa4168f483ea33ba69ce58..aff3526b5589cfff395525cdfb5a4b58bcec0a26 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -23,13 +23,14 @@ use bp_messages::{ ChainWithMessages, LaneId, MessageNonce, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf}; -use bridge_runtime_common::messages_xcm_extension::{SenderAndLane, XcmBlobHauler}; +use bp_xcm_bridge_hub::{BridgeId, LocalXcmChannelManager}; use codec::Encode; use frame_support::{ assert_ok, derive_impl, parameter_types, - traits::{Everything, NeverEnsureOrigin}, + traits::{EnsureOrigin, Equals, Everything, OriginTrait}, weights::RuntimeDbWeight, }; +use polkadot_parachain_primitives::primitives::Sibling; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, @@ -39,27 +40,26 @@ use sp_runtime::{ use sp_std::cell::RefCell; use xcm::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, FixedWeightBounds, InspectMessageQueues, NetworkExportTable, - NetworkExportTableItem, + AllowUnpaidExecutionFrom, DispatchBlob, DispatchBlobError, FixedWeightBounds, + InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, ParentIsPreset, + SiblingParachainConvertsVia, }; use xcm_executor::XcmExecutor; pub type AccountId = AccountId32; pub type Balance = u64; - type Block = frame_system::mocking::MockBlock; pub const SIBLING_ASSET_HUB_ID: u32 = 2001; pub const THIS_BRIDGE_HUB_ID: u32 = 2002; pub const BRIDGED_ASSET_HUB_ID: u32 = 1001; -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Event}, Messages: pallet_bridge_messages::{Pallet, Call, Event}, - XcmOverBridge: pallet_xcm_bridge_hub::{Pallet}, + XcmOverBridge: pallet_xcm_bridge_hub::{Pallet, Call, HoldReason, Event}, XcmOverBridgeRouter: pallet_xcm_bridge_hub_router, } } @@ -82,25 +82,22 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; -} - impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = TestMessagesWeights; - type ActiveOutboundLanes = ActiveOutboundLanes; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingChain; + type BridgedHeaderChain = BridgedHeaderChain; + type OutboundPayload = Vec; type InboundPayload = Vec; + type DeliveryPayments = (); type DeliveryConfirmationPayments = (); type OnMessagesDelivered = (); - type MessageDispatch = TestMessageDispatch; - type ThisChain = ThisUnderlyingChain; - type BridgedChain = BridgedUnderlyingChain; - type BridgedHeaderChain = BridgedHeaderChain; + type MessageDispatch = TestMessageDispatch; } pub struct TestMessagesWeights; @@ -127,8 +124,8 @@ impl pallet_bridge_messages::WeightInfo for TestMessagesWeights { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { Weight::zero() } - fn receive_single_n_bytes_message_proof_with_dispatch(_: u32) -> Weight { - Weight::zero() + fn receive_single_n_bytes_message_proof_with_dispatch(_n: u32) -> Weight { + Weight::from_parts(1, 0) } } @@ -153,6 +150,7 @@ parameter_types! { Parachain(THIS_BRIDGE_HUB_ID), ].into(); pub SiblingLocation: Location = Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]); + pub SiblingUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into(); pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); @@ -161,7 +159,6 @@ parameter_types! { pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; pub const BridgeDeposit: Balance = 100_000; - pub const Penalty: Balance = 1_000; // configuration for pallet_xcm_bridge_hub_router pub BridgeHubLocation: Location = Here.into(); @@ -178,7 +175,14 @@ parameter_types! { pub UnitWeightCost: Weight = Weight::from_parts(10, 10); } +/// **Universal** `InteriorLocation` of bridged asset hub. +pub fn bridged_asset_hub_universal_location() -> InteriorLocation { + BridgedUniversalDestination::get() +} + impl pallet_xcm_bridge_hub::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = BridgedRelayNetworkLocation; type BridgeMessagesPalletInstance = (); @@ -186,25 +190,35 @@ impl pallet_xcm_bridge_hub::Config for TestRuntime { type MessageExportPrice = (); type DestinationVersion = AlwaysLatest; - type Lanes = TestLanes; - type LanesSupport = TestXcmBlobHauler; + type AdminOrigin = frame_system::EnsureNever<()>; + type OpenBridgeOrigin = OpenBridgeOrigin; + type BridgeOriginAccountIdConverter = LocationToAccountId; + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type AllowWithoutBridgeDeposit = Equals; + + type LocalXcmChannelManager = TestLocalXcmChannelManager; + + type BlobDispatcher = TestBlobDispatcher; } impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type UniversalLocation = UniversalLocation; + type SiblingBridgeHubLocation = BridgeHubLocation; type BridgedNetworkId = BridgedRelayNetwork; type Bridges = NetworkExportTable; type DestinationVersion = AlwaysLatest; - type BridgeHubOrigin = NeverEnsureOrigin; type ToBridgeHubSender = TestExportXcmWithXcmOverBridge; + type LocalXcmChannelManager = TestLocalXcmChannelManager; type ByteFee = ConstU128<0>; type FeeAsset = BridgeFeeAsset; - - type WithBridgeHubChannel = (); } pub struct XcmConfig; @@ -281,6 +295,10 @@ impl SendXcm for TestExportXcmWithXcmOverBridge { } } impl InspectMessageQueues for TestExportXcmWithXcmOverBridge { + fn clear_messages() { + todo!() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { todo!() } @@ -291,29 +309,149 @@ impl TestExportXcmWithXcmOverBridge { } } +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, +); + parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: SiblingLocation::get(), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedRelativeDestination::get())) - ]; + pub ParentRelayChainLocation: Location = Location { parents: 1, interior: Here }; +} +pub struct OpenBridgeOrigin; + +impl OpenBridgeOrigin { + pub fn parent_relay_chain_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([0u8; 32].into()) + } + + pub fn parent_relay_chain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([1u8; 32].into()) + } + + pub fn sibling_parachain_origin() -> RuntimeOrigin { + let mut account = [0u8; 32]; + account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]); + RuntimeOrigin::signed(account.into()) + } + + pub fn sibling_parachain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([2u8; 32].into()) + } + + pub fn origin_without_sovereign_account() -> RuntimeOrigin { + RuntimeOrigin::signed([3u8; 32].into()) + } + + pub fn disallowed_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([42u8; 32].into()) + } +} + +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 TestXcmBlobHauler; -impl XcmBlobHauler for TestXcmBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - type ToSourceChainSender = (); - type CongestedMessage = (); - type UncongestedMessage = (); +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 +473,15 @@ impl Chain for ThisUnderlyingChain { } impl ChainWithMessages for ThisUnderlyingChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; - + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithThisChainBridgeMessages"; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; } -pub struct BridgedUnderlyingChain; pub type BridgedHeaderHash = H256; pub type BridgedChainHeader = SubstrateHeader; +pub struct BridgedUnderlyingChain; impl Chain for BridgedUnderlyingChain { const ID: ChainId = *b"bgdc"; type BlockNumber = u64; @@ -368,9 +505,18 @@ impl Chain for BridgedUnderlyingChain { } impl ChainWithMessages for BridgedUnderlyingChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithBridgedChainBridgeMessages"; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; +} + +pub struct BridgedHeaderChain; +impl bp_header_chain::HeaderChain for BridgedHeaderChain { + fn finalized_header_state_root( + _hash: HashOf, + ) -> Option> { + unreachable!() + } } /// Test message dispatcher. @@ -386,8 +532,9 @@ impl MessageDispatch for TestMessageDispatch { type DispatchPayload = Vec; type DispatchLevelResult = (); - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&(b"inactive").encode()[..]) != Some(false) + fn is_active(lane: LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) } fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { @@ -401,15 +548,6 @@ impl MessageDispatch for TestMessageDispatch { } } -pub struct BridgedHeaderChain; -impl bp_header_chain::HeaderChain for BridgedHeaderChain { - fn finalized_header_state_root( - _hash: HashOf, - ) -> Option> { - unreachable!() - } -} - /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { sp_io::TestExternalities::new( diff --git a/bridges/primitives/header-chain/src/call_info.rs b/bridges/primitives/header-chain/src/call_info.rs new file mode 100644 index 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/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..c8f06ed8cb7c34e11a37fb7613e3dfad6bc45538 --- /dev/null +++ b/bridges/primitives/messages/src/call_info.rs @@ -0,0 +1,172 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-messages` pallet. + +use crate::{source_chain, target_chain, LaneId, MessageNonce, UnrewardedRelayersState}; + +use bp_runtime::{AccountIdOf, HashOf}; +use codec::{Decode, Encode}; +use frame_support::weights::Weight; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; +use sp_std::ops::RangeInclusive; + +/// The `BridgeMessagesCall` used to bridge with a given chain. +pub type BridgeMessagesCallOf = BridgeMessagesCall< + AccountIdOf, + target_chain::FromBridgedChainMessagesProof>, + source_chain::FromBridgedChainMessagesDeliveryProof>, +>; + +/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeMessagesCall { + /// `pallet-bridge-messages::Call::receive_messages_proof` + #[codec(index = 2)] + receive_messages_proof { + /// Account id of relayer at the **bridged** chain. + relayer_id_at_bridged_chain: AccountId, + /// Messages proof. + proof: MessagesProof, + /// A number of messages in the proof. + messages_count: u32, + /// Total dispatch weight of messages in the proof. + dispatch_weight: Weight, + }, + /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` + #[codec(index = 3)] + receive_messages_delivery_proof { + /// Messages delivery proof. + proof: MessagesDeliveryProof, + /// "Digest" of unrewarded relayers state at the bridged chain. + relayers_state: UnrewardedRelayersState, + }, +} + +/// Generic info about a messages delivery/confirmation proof. +#[derive(PartialEq, RuntimeDebug)] +pub struct BaseMessagesProofInfo { + /// Message lane, used by the call. + pub lane_id: LaneId, + /// Nonces of messages, included in the call. + /// + /// For delivery transaction, it is nonces of bundled messages. For confirmation + /// transaction, it is nonces that are to be confirmed during the call. + pub bundled_range: RangeInclusive, + /// Nonce of the best message, stored by this chain before the call is dispatched. + /// + /// For delivery transaction, it is the nonce of best delivered message before the call. + /// For confirmation transaction, it is the nonce of best confirmed message before the call. + pub best_stored_nonce: MessageNonce, +} + +impl BaseMessagesProofInfo { + /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. + pub fn appends_to_stored_nonce(&self) -> bool { + Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) + } +} + +/// Occupation state of the unrewarded relayers vector. +#[derive(PartialEq, RuntimeDebug)] +#[cfg_attr(test, derive(Default))] +pub struct UnrewardedRelayerOccupation { + /// The number of remaining unoccupied entries for new relayers. + pub free_relayer_slots: MessageNonce, + /// The number of messages that we are ready to accept. + pub free_message_slots: MessageNonce, +} + +/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesProofInfo { + /// Base messages proof info + pub base: BaseMessagesProofInfo, + /// State of unrewarded relayers vector. + pub unrewarded_relayers: UnrewardedRelayerOccupation, +} + +impl ReceiveMessagesProofInfo { + /// Returns true if: + /// + /// - either inbound lane is ready to accept bundled messages; + /// + /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed + /// messages and/or unrewarded relayers. + pub fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { + // if dispatcher is inactive, we don't accept any delivery transactions + if !is_dispatcher_active { + return true + } + + // transactions with zero bundled nonces are not allowed, unless they're message + // delivery transactions, which brings reward confirmations required to unblock + // the lane + if self.base.bundled_range.is_empty() { + let empty_transactions_allowed = + // we allow empty transactions when we can't accept delivery from new relayers + self.unrewarded_relayers.free_relayer_slots == 0 || + // or if we can't accept new messages at all + self.unrewarded_relayers.free_message_slots == 0; + + return !empty_transactions_allowed + } + + // otherwise we require bundled messages to continue stored range + !self.base.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); + +impl ReceiveMessagesDeliveryProofInfo { + /// Returns true if outbound lane is ready to accept confirmations of bundled messages. + pub fn is_obsolete(&self) -> bool { + self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call +/// which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub enum MessagesCallInfo { + /// Messages delivery call info. + ReceiveMessagesProof(ReceiveMessagesProofInfo), + /// Messages delivery confirmation call info. + ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), +} + +impl MessagesCallInfo { + /// Returns lane, used by the call. + pub fn lane_id(&self) -> LaneId { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.lane_id, + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.lane_id, + } + } + + /// Returns range of messages, bundled with the call. + pub fn bundled_messages(&self) -> RangeInclusive { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), + } + } +} diff --git a/bridges/primitives/messages/src/lane.rs b/bridges/primitives/messages/src/lane.rs new file mode 100644 index 0000000000000000000000000000000000000000..6d4ca402eb345302d2a3072b0ddb30011f2a0015 --- /dev/null +++ b/bridges/primitives/messages/src/lane.rs @@ -0,0 +1,281 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Primitives of messages module, that represents lane id. + +use codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen}; +use frame_support::sp_runtime::Either; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::{RuntimeDebug, TypeId, H256}; +use sp_io::hashing::blake2_256; + +/// Bridge lane identifier. +/// +/// Lane connects two endpoints at both sides of the bridge. We assume that every endpoint +/// has its own unique identifier. We want lane identifiers to be **the same on the both sides +/// of the bridge** (and naturally unique across global consensus if endpoints have unique +/// identifiers). So lane id is the hash (`blake2_256`) of **ordered** encoded locations +/// concatenation (separated by some binary data). I.e.: +/// +/// ```nocompile +/// let endpoint1 = X2(GlobalConsensus(NetworkId::Rococo), Parachain(42)); +/// let endpoint2 = X2(GlobalConsensus(NetworkId::Wococo), Parachain(777)); +/// +/// let final_lane_key = if endpoint1 < endpoint2 { +/// (endpoint1, VALUES_SEPARATOR, endpoint2) +/// } else { +/// (endpoint2, VALUES_SEPARATOR, endpoint1) +/// }.using_encoded(blake2_256); +/// ``` +/// +/// Note: For backwards compatibility reasons, we also handle the older format `[u8; 4]`. +#[derive( + Clone, + Copy, + Decode, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct LaneId(InnerLaneId); + +impl LaneId { + /// Create lane identifier from two locations. + pub fn new(endpoint1: T, endpoint2: T) -> Self { + const VALUES_SEPARATOR: [u8; 31] = *b"bridges-lane-id-value-separator"; + + LaneId(InnerLaneId::Hash( + if endpoint1 < endpoint2 { + (endpoint1, VALUES_SEPARATOR, endpoint2) + } else { + (endpoint2, VALUES_SEPARATOR, endpoint1) + } + .using_encoded(blake2_256) + .into(), + )) + } + + /// Create lane identifier from given hash. + /// + /// There's no `From` implementation for the `LaneId`, because using this conversion + /// in a wrong way (i.e. computing hash of endpoints manually) may lead to issues. So we + /// want the call to be explicit. + pub const fn from_inner(inner: Either) -> Self { + LaneId(match inner { + Either::Left(hash) => InnerLaneId::Hash(hash), + Either::Right(array) => InnerLaneId::Array(array), + }) + } +} + +impl core::fmt::Display for LaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(&self.0, f) + } +} + +impl core::fmt::Debug for LaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.0, f) + } +} + +impl AsRef<[u8]> for LaneId { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl TypeId for LaneId { + const TYPE_ID: [u8; 4] = *b"blan"; +} + +#[derive( + Clone, Copy, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize, +)] +enum InnerLaneId { + /// Old format (for backwards compatibility). + Array([u8; 4]), + /// New format 32-byte hash generated by `blake2_256`. + Hash(H256), +} + +impl Encode for InnerLaneId { + fn encode(&self) -> sp_std::vec::Vec { + match self { + InnerLaneId::Array(array) => array.encode(), + InnerLaneId::Hash(hash) => hash.encode(), + } + } +} + +impl Decode for InnerLaneId { + fn decode(input: &mut I) -> Result { + // check backwards compatibly first + if input.remaining_len() == Ok(Some(4)) { + let array: [u8; 4] = Decode::decode(input)?; + return Ok(InnerLaneId::Array(array)) + } + + // else check new format + H256::decode(input).map(InnerLaneId::Hash) + } +} + +impl core::fmt::Display for InnerLaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + InnerLaneId::Array(array) => write!(f, "Array({:?})", array), + InnerLaneId::Hash(hash) => write!(f, "Hash({:?})", hash), + } + } +} + +impl core::fmt::Debug for InnerLaneId { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + InnerLaneId::Array(array) => array.fmt(fmt), + InnerLaneId::Hash(hash) => hash.fmt(fmt), + } + } +} + +impl AsRef<[u8]> for InnerLaneId { + fn as_ref(&self) -> &[u8] { + match self { + InnerLaneId::Array(array) => array.as_ref(), + InnerLaneId::Hash(hash) => hash.as_ref(), + } + } +} + +/// Lane state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum LaneState { + /// Lane is opened and messages may be sent/received over it. + Opened, + /// Lane is closed and all attempts to send/receive messages to/from this lane + /// will fail. + /// + /// Keep in mind that the lane has two ends and the state of the same lane at + /// its ends may be different. Those who are controlling/serving the lane + /// and/or sending messages over the lane, have to coordinate their actions on + /// both ends to make sure that lane is operating smoothly on both ends. + Closed, +} + +impl LaneState { + /// Returns true if lane state allows sending/receiving messages. + pub fn is_active(&self) -> bool { + matches!(*self, LaneState::Opened) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lane_id_debug_format_matches_inner_hash_format() { + assert_eq!( + format!("{:?}", LaneId(InnerLaneId::Hash(H256::from([1u8; 32])))), + format!("{:?}", H256::from([1u8; 32])), + ); + assert_eq!( + format!("{:?}", LaneId(InnerLaneId::Array([0, 0, 0, 1]))), + format!("{:?}", [0, 0, 0, 1]), + ); + } + + #[test] + fn lane_id_as_ref_works() { + assert_eq!( + "0101010101010101010101010101010101010101010101010101010101010101", + hex::encode(LaneId(InnerLaneId::Hash(H256::from([1u8; 32]))).as_ref()), + ); + assert_eq!("00000001", hex::encode(LaneId(InnerLaneId::Array([0, 0, 0, 1])).as_ref()),); + } + + #[test] + fn lane_id_encode_decode_works() { + let test_encode_decode = |expected_hex, lane_id: LaneId| { + let enc = lane_id.encode(); + let decoded_lane_id = LaneId::decode(&mut &enc[..]).expect("decodable"); + assert_eq!(lane_id, decoded_lane_id); + + assert_eq!(expected_hex, hex::encode(lane_id.as_ref()),); + assert_eq!(expected_hex, hex::encode(decoded_lane_id.as_ref()),); + + let hex_bytes = hex::decode(expected_hex).expect("valid hex"); + let hex_decoded_lane_id = LaneId::decode(&mut &hex_bytes[..]).expect("decodable"); + assert_eq!(hex_decoded_lane_id, lane_id); + assert_eq!(hex_decoded_lane_id, decoded_lane_id); + }; + + test_encode_decode( + "0101010101010101010101010101010101010101010101010101010101010101", + LaneId(InnerLaneId::Hash(H256::from([1u8; 32]))), + ); + test_encode_decode("00000001", LaneId(InnerLaneId::Array([0, 0, 0, 1]))); + } + + #[test] + fn lane_id_is_generated_using_ordered_endpoints() { + assert_eq!(LaneId::new(1, 2), LaneId::new(2, 1)); + } + + #[test] + fn lane_id_is_different_for_different_endpoints() { + assert_ne!(LaneId::new(1, 2), LaneId::new(1, 3)); + } + + #[test] + fn lane_id_is_different_even_if_arguments_has_partial_matching_encoding() { + /// Some artificial type that generates the same encoding for different values + /// concatenations. I.e. the encoding for `(Either::Two(1, 2), Either::Two(3, 4))` + /// is the same as encoding of `(Either::Three(1, 2, 3), Either::One(4))`. + /// In practice, this type is not useful, because you can't do a proper decoding. + /// But still there may be some collisions even in proper types. + #[derive(Eq, Ord, PartialEq, PartialOrd)] + enum Either { + Three(u64, u64, u64), + Two(u64, u64), + One(u64), + } + + impl codec::Encode for Either { + fn encode(&self) -> Vec { + match *self { + Self::One(a) => a.encode(), + Self::Two(a, b) => (a, b).encode(), + Self::Three(a, b, c) => (a, b, c).encode(), + } + } + } + + assert_ne!( + LaneId::new(Either::Two(1, 2), Either::Two(3, 4)), + LaneId::new(Either::Three(1, 2, 3), Either::One(4)), + ); + } +} diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index 9984f8ac3222780ea2fd6af56dd896beb264b2d5..7eb0c562939531a9762e8864babf5f7843d070ab 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -31,9 +31,17 @@ pub use frame_support::weights::Weight; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use source_chain::RelayersRewards; -use sp_core::{RuntimeDebug, TypeId}; +use sp_core::RuntimeDebug; use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; +pub use call_info::{ + BaseMessagesProofInfo, BridgeMessagesCall, BridgeMessagesCallOf, MessagesCallInfo, + ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayerOccupation, +}; +pub use lane::{LaneId, LaneState}; + +mod call_info; +mod lane; pub mod source_chain; pub mod storage_keys; pub mod target_chain; @@ -165,46 +173,9 @@ impl OperatingMode for MessagesOperatingMode { } } -/// Lane id which implements `TypeId`. -#[derive( - Clone, - Copy, - Decode, - Default, - Encode, - Eq, - Ord, - PartialOrd, - PartialEq, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -pub struct LaneId(pub [u8; 4]); - -impl core::fmt::Debug for LaneId { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - self.0.fmt(fmt) - } -} - -impl AsRef<[u8]> for LaneId { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl TypeId for LaneId { - const TYPE_ID: [u8; 4] = *b"blan"; -} - /// Message nonce. Valid messages will never have 0 nonce. pub type MessageNonce = u64; -/// Message id as a tuple. -pub type BridgeMessageId = (LaneId, MessageNonce); - /// Opaque message payload. We only decode this payload when it is dispatched. pub type MessagePayload = Vec; @@ -229,6 +200,11 @@ pub struct Message { /// Inbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct InboundLaneData { + /// Inbound lane state. + /// + /// If state is `Closed`, then all attempts to deliver messages to this end will fail. + pub state: LaneState, + /// Identifiers of relayers and messages that they have delivered to this lane (ordered by /// message nonce). /// @@ -261,11 +237,20 @@ pub struct InboundLaneData { impl Default for InboundLaneData { fn default() -> Self { - InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } + InboundLaneData { + state: LaneState::Closed, + relayers: VecDeque::new(), + last_confirmed_nonce: 0, + } } } impl InboundLaneData { + /// Returns default inbound lane data with opened state. + pub fn opened() -> Self { + InboundLaneData { state: LaneState::Opened, ..Default::default() } + } + /// Returns approximate size of the struct, given a number of entries in the `relayers` set and /// size of each entry. /// @@ -351,7 +336,7 @@ pub struct UnrewardedRelayer { } /// Received messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct ReceivedMessages { /// Id of the lane which is receiving messages. pub lane: LaneId, @@ -464,6 +449,10 @@ impl From<&InboundLaneData> for UnrewardedRelayersState { /// Outbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct OutboundLaneData { + /// Lane state. + /// + /// If state is `Closed`, then all attempts to send messages messages at this end will fail. + pub state: LaneState, /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated /// message if all sent messages are already pruned. pub oldest_unpruned_nonce: MessageNonce, @@ -473,9 +462,17 @@ pub struct OutboundLaneData { pub latest_generated_nonce: MessageNonce, } +impl OutboundLaneData { + /// Returns default outbound lane data with opened state. + pub fn opened() -> Self { + OutboundLaneData { state: LaneState::Opened, ..Default::default() } + } +} + impl Default for OutboundLaneData { fn default() -> Self { OutboundLaneData { + state: LaneState::Closed, // it is 1 because we're pruning everything in [oldest_unpruned_nonce; // latest_received_nonce] oldest_unpruned_nonce: 1, @@ -514,32 +511,6 @@ where relayers_rewards } -/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeMessagesCall { - /// `pallet-bridge-messages::Call::receive_messages_proof` - #[codec(index = 2)] - receive_messages_proof { - /// Account id of relayer at the **bridged** chain. - relayer_id_at_bridged_chain: AccountId, - /// Messages proof. - proof: MessagesProof, - /// A number of messages in the proof. - messages_count: u32, - /// Total dispatch weight of messages in the proof. - dispatch_weight: Weight, - }, - /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` - #[codec(index = 3)] - receive_messages_delivery_proof { - /// Messages delivery proof. - proof: MessagesDeliveryProof, - /// "Digest" of unrewarded relayers state at the bridged chain. - relayers_state: UnrewardedRelayersState, - }, -} - /// Error that happens during message verification. #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] pub enum VerificationError { @@ -569,9 +540,16 @@ pub enum VerificationError { mod tests { use super::*; + #[test] + fn lane_is_closed_by_default() { + assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed); + assert_eq!(OutboundLaneData::default().state, LaneState::Closed); + } + #[test] fn total_unrewarded_messages_does_not_overflow() { let lane_data = InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, UnrewardedRelayer { @@ -599,6 +577,7 @@ mod tests { for (relayer_entries, messages_count) in test_cases { let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); let actual_size = InboundLaneData { + state: LaneState::Opened, relayers: (1u8..=relayer_entries) .map(|i| UnrewardedRelayer { relayer: i, @@ -626,9 +605,4 @@ mod tests { assert!(delivered_messages.contains_message(150)); assert!(!delivered_messages.contains_message(151)); } - - #[test] - fn lane_id_debug_format_matches_inner_array_format() { - assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); - } } diff --git a/bridges/primitives/messages/src/storage_keys.rs b/bridges/primitives/messages/src/storage_keys.rs index 8eedf8fcc7ac98ae300ca0485a0827afd3cd1bb5..ff62dab078e748db0827fc0c1e16c6508dfeb813 100644 --- a/bridges/primitives/messages/src/storage_keys.rs +++ b/bridges/primitives/messages/src/storage_keys.rs @@ -72,6 +72,7 @@ pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { #[cfg(test)] mod tests { use super::*; + use frame_support::sp_runtime::Either; use hex_literal::hex; #[test] @@ -91,7 +92,17 @@ mod tests { fn storage_message_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted messages proofs. - let storage_key = message_key("BridgeMessages", &LaneId(*b"test"), 42).0; + let storage_key = message_key("BridgeMessages", &LaneId::new(1, 2), 42).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea70e9bdb8f50c68d12f06eabb57759ee5eb1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea02a00000000000000").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = + message_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test")), 42).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), @@ -104,7 +115,18 @@ mod tests { fn outbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted outbound lane state proofs. - let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId::new(1, 2)).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1fd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = + outbound_lane_data_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test"))) + .0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), @@ -117,7 +139,17 @@ mod tests { fn inbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted inbound lane state proofs. - let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId::new(1, 2)).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fabd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = + inbound_lane_data_key("BridgeMessages", &LaneId::from_inner(Either::Right(*b"test"))).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs index 74fecb9d9f0d89420d2ca54a4356e23c9130e614..67868ff7c7cd3745dc3e44b1b6b7d8f45f5605f8 100644 --- a/bridges/primitives/messages/src/target_chain.rs +++ b/bridges/primitives/messages/src/target_chain.rs @@ -23,7 +23,7 @@ use codec::{Decode, Encode, Error as CodecError}; use frame_support::weights::Weight; use scale_info::TypeInfo; use sp_core::RuntimeDebug; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*}; +use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; /// Messages proof from bridged chain. /// @@ -59,7 +59,7 @@ impl Size for FromBridgedChainMessagesProof = BTreeMap>; +pub type ProvedMessages = (LaneId, ProvedLaneMessages); /// Proved messages from single lane of the source chain. #[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] @@ -103,7 +103,7 @@ pub trait MessageDispatch { /// /// We check it in the messages delivery transaction prologue. So if it becomes `false` /// after some portion of messages is already dispatched, it doesn't fail the whole transaction. - fn is_active() -> bool; + fn is_active(lane: LaneId) -> bool; /// Estimate dispatch weight. /// @@ -179,7 +179,7 @@ impl MessageDispatch for ForbidInboundMessages bool { + fn is_active(_: LaneId) -> bool { false } diff --git a/bridges/primitives/parachains/src/call_info.rs b/bridges/primitives/parachains/src/call_info.rs new file mode 100644 index 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..366ee7aa948e8e788f260255e524d7efce93c6f8 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/Cargo.toml @@ -14,7 +14,9 @@ workspace = true codec = { features = ["derive"], workspace = true } parity-util-mem = { optional = true, workspace = true } scale-info = { features = ["derive"], workspace = true } -serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde = { optional = true, features = [ + "derive", +], workspace = true, default-features = true } # Bridge Dependencies diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml index 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..5ab8e6cde96b408f6415768053eebf14c2b82ae4 --- /dev/null +++ b/bridges/primitives/relayers/src/extension.rs @@ -0,0 +1,191 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! All runtime calls, supported by `pallet-bridge-relayers` when it acts as a signed +//! extension. + +use bp_header_chain::SubmitFinalityProofInfo; +use bp_messages::MessagesCallInfo; +use bp_parachains::SubmitParachainHeadsInfo; +use bp_runtime::StaticStrProvider; +use frame_support::{ + dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebugNoBound, +}; +use frame_system::Config as SystemConfig; +use pallet_utility::{Call as UtilityCall, Pallet as UtilityPallet}; +use sp_runtime::{ + traits::Get, + transaction_validity::{TransactionPriority, TransactionValidityError}, + RuntimeDebug, +}; +use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; + +/// Type of the call that the signed extension recognizes. +#[derive(PartialEq, RuntimeDebugNoBound)] +pub enum ExtensionCallInfo { + /// Relay chain finality + parachain finality + message delivery/confirmation calls. + AllFinalityAndMsgs( + SubmitFinalityProofInfo, + SubmitParachainHeadsInfo, + MessagesCallInfo, + ), + /// Relay chain finality + message delivery/confirmation calls. + RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), + /// Parachain finality + message delivery/confirmation calls. + /// + /// This variant is used only when bridging with parachain. + ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), + /// Standalone message delivery/confirmation call. + Msgs(MessagesCallInfo), +} + +impl + ExtensionCallInfo +{ + /// Returns true if call is a message delivery call (with optional finality calls). + pub fn is_receive_messages_proof_call(&self) -> bool { + match self.messages_call_info() { + MessagesCallInfo::ReceiveMessagesProof(_) => true, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, + } + } + + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. + pub fn submit_finality_proof_info( + &self, + ) -> Option> { + match *self { + Self::AllFinalityAndMsgs(info, _, _) => Some(info), + Self::RelayFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. + pub fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { + match self { + Self::AllFinalityAndMsgs(_, info, _) => Some(info), + Self::ParachainFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. + pub fn messages_call_info(&self) -> &MessagesCallInfo { + match self { + Self::AllFinalityAndMsgs(_, _, info) => info, + Self::RelayFinalityAndMsgs(_, info) => info, + Self::ParachainFinalityAndMsgs(_, info) => info, + Self::Msgs(info) => info, + } + } +} + +/// Extra post-dispatch data, associated with the supported runtime call. +#[derive(Default, RuntimeDebug)] +pub struct ExtensionCallData { + /// Extra weight, consumed by the call. We have some assumptions about normal weight + /// that may be consumed by expected calls. If the actual weight is larger than that, + /// we do not refund relayer for this extra weight. + pub extra_weight: Weight, + /// Extra size, consumed by the call. We have some assumptions about normal size + /// of the encoded call. If the actual size is larger than that, we do not refund relayer + /// for this extra size. + pub extra_size: u32, +} + +/// Signed extension configuration. +/// +/// The single `pallet-bridge-relayers` instance may be shared by multiple messages +/// pallet instances, bridging with different remote networks. We expect every instance +/// of the messages pallet to add a separate signed extension to runtime. So it must +/// have a separate configuration. +pub trait ExtensionConfig { + /// Unique identifier of the signed extension that will use this configuration. + type IdProvider: StaticStrProvider; + /// Runtime that optionally supports batched calls. We assume that batched call + /// succeeds if and only if all of its nested calls succeed. + type Runtime: frame_system::Config; + /// Messages pallet instance. + type BridgeMessagesPalletInstance: 'static; + /// Additional priority that is added to base message delivery transaction priority + /// for every additional bundled message. + type PriorityBoostPerMessage: Get; + /// Type of reward, that the `pallet-bridge-relayers` is using. + type Reward; + /// Block number for the remote **GRANDPA chain**. Mind that this chain is not + /// necessarily the chain that we are bridging with. If we are bridging with + /// parachain, it must be its parent relay chain. If we are bridging with the + /// GRANDPA chain, it must be it. + type RemoteGrandpaChainBlockNumber: Clone + Copy + Debug; + + /// Given runtime call, check if it is supported by the signed extension. Additionally, + /// check if call (or any of batched calls) are obsolete. + fn parse_and_check_for_obsolete_call( + call: &::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + >; + + /// Check if runtime call is already obsolete. + fn check_obsolete_parsed_call( + call: &::RuntimeCall, + ) -> Result<&::RuntimeCall, TransactionValidityError>; + + /// Given runtime call info, check that this call has been successful and has updated + /// runtime storage accordingly. + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, + ) -> bool; +} + +/// Something that can unpack batch calls (all-or-nothing flavor) of given size. +pub trait BatchCallUnpacker { + /// Unpack batch call with no more than `max_packed_calls` calls. + fn unpack(call: &Runtime::RuntimeCall, max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall>; +} + +/// An `BatchCallUnpacker` adapter for runtimes with utility pallet. +pub struct RuntimeWithUtilityPallet(PhantomData); + +impl BatchCallUnpacker for RuntimeWithUtilityPallet +where + Runtime: pallet_utility::Config::RuntimeCall>, + ::RuntimeCall: + IsSubType, Runtime>>, +{ + fn unpack( + call: &::RuntimeCall, + max_packed_calls: u32, + ) -> Vec<&::RuntimeCall> { + match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) + if calls.len() <= max_packed_calls as usize => + calls.iter().collect(), + Some(_) => vec![], + None => vec![call], + } + } +} + +impl BatchCallUnpacker for () { + fn unpack(call: &Runtime::RuntimeCall, _max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall> { + vec![call] + } +} diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs index 436f33db4008013572592939b63c0e02e92b8e99..1e63c89ecd70434de754e1442c193d65bb5ec182 100644 --- a/bridges/primitives/relayers/src/lib.rs +++ b/bridges/primitives/relayers/src/lib.rs @@ -19,6 +19,10 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +pub use extension::{ + BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RuntimeWithUtilityPallet, +}; pub use registration::{ExplicitOrAccountParams, Registration, StakeAndSlash}; use bp_messages::LaneId; @@ -32,6 +36,7 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, marker::PhantomData}; +mod extension; mod registration; /// The owner of the sovereign account that should pay the rewards. @@ -57,9 +62,12 @@ pub enum RewardsAccountOwner { /// parameters to identify the account that pays a reward to the relayer. #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] pub struct RewardsAccountParams { - lane_id: LaneId, - bridged_chain_id: ChainId, + // **IMPORTANT NOTE**: the order of fields here matters - we are using + // `into_account_truncating` and lane id is already `32` byte, so if other fields are encoded + // after it, they're simply dropped. So lane id shall be the last field. owner: RewardsAccountOwner, + bridged_chain_id: ChainId, + lane_id: LaneId, } impl RewardsAccountParams { @@ -162,21 +170,21 @@ mod tests { fn different_lanes_are_using_different_accounts() { assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), + LaneId::new(1, 3), *b"test", RewardsAccountOwner::ThisChain )), - hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700074657374a43e8951aa302c133beb5f85821a21645f07b487270ef3") .into(), ); } @@ -185,21 +193,21 @@ mod tests { fn different_directions_are_using_different_accounts() { assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::ThisChain )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); assert_eq!( PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + LaneId::new(1, 2), *b"test", RewardsAccountOwner::BridgedChain )), - hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") + hex_literal::hex!("627261700174657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); } diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 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/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..44a90a57d4fbc6c423973ab89d6756b5832da1ff 100644 --- a/bridges/primitives/xcm-bridge-hub/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub/src/lib.rs @@ -19,6 +19,678 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +use bp_messages::LaneId; +use bp_runtime::{AccountIdOf, BalanceOf, Chain}; +pub use call_info::XcmBridgeHubCall; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + ensure, sp_runtime::RuntimeDebug, CloneNoBound, PalletError, PartialEqNoBound, + RuntimeDebugNoBound, +}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use sp_io::hashing::blake2_256; +use sp_std::boxed::Box; +use xcm::{ + latest::prelude::*, prelude::XcmVersion, IntoVersion, VersionedInteriorLocation, + VersionedLocation, +}; + +mod call_info; + /// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound /// and outbound payloads. pub type XcmAsPlainPayload = sp_std::vec::Vec; + +/// Bridge identifier - used **only** for communicating with sibling/parent chains in the same +/// consensus. +/// +/// For example, `SendXcm` implementations (which use the `latest` XCM) can use it to identify a +/// bridge and the corresponding `LaneId` that is used for over-consensus communication between +/// bridge hubs. +/// +/// This identifier is constructed from the `latest` XCM, so it is expected to ensure migration to +/// the `latest` XCM version. This could change the `BridgeId`, but it will not affect the `LaneId`. +/// In other words, `LaneId` will never change, while `BridgeId` could change with (every) XCM +/// upgrade. +#[derive( + Clone, + Copy, + Decode, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + RuntimeDebug, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct BridgeId(H256); + +impl BridgeId { + /// Create bridge identifier from two universal locations. + /// + /// Note: The `BridgeId` is constructed from `latest` XCM, so if stored, you need to ensure + /// compatibility with newer XCM versions. + pub fn new( + universal_source: &InteriorLocation, + universal_destination: &InteriorLocation, + ) -> Self { + const VALUES_SEPARATOR: [u8; 33] = *b"bridges-bridge-id-value-separator"; + + BridgeId( + (universal_source, VALUES_SEPARATOR, universal_destination) + .using_encoded(blake2_256) + .into(), + ) + } +} + +/// Local XCM channel manager. +pub trait LocalXcmChannelManager { + /// Error that may be returned when suspending/resuming the bridge. + type Error: sp_std::fmt::Debug; + + /// Returns true if the channel with given location is currently congested. + /// + /// The `with` is guaranteed to be in the same consensus. However, it may point to something + /// below the chain level - like the contract or pallet instance, for example. + fn is_congested(with: &Location) -> bool; + + /// Suspend the bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>; + + /// Resume the previously suspended bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>; +} + +impl LocalXcmChannelManager for () { + type Error = (); + + fn is_congested(_with: &Location) -> bool { + false + } + + fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + Ok(()) + } + + fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Bridge state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum BridgeState { + /// Bridge is opened. Associated lanes are also opened. + Opened, + /// Bridge is suspended. Associated lanes are opened. + /// + /// We keep accepting messages to the bridge. The only difference with the `Opened` state + /// is that we have sent the "Suspended" message/signal to the local bridge origin. + Suspended, + /// Bridge is closed. Associated lanes are also closed. + /// After all outbound messages will be pruned, the bridge will vanish without any traces. + Closed, +} + +/// Bridge metadata. +#[derive( + CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound, +)] +#[scale_info(skip_type_params(ThisChain))] +pub struct Bridge { + /// Relative location of the bridge origin chain. This is expected to be **convertible** to the + /// `latest` XCM, so the check and migration needs to be ensured. + pub bridge_origin_relative_location: Box, + + /// See [`BridgeLocations::bridge_origin_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_origin_universal_location: Box, + /// See [`BridgeLocations::bridge_destination_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_destination_universal_location: Box, + + /// Current bridge state. + pub state: BridgeState, + /// Account with the reserved funds. Derived from `self.bridge_origin_relative_location`. + pub bridge_owner_account: AccountIdOf, + /// Reserved amount on the sovereign account of the sibling bridge origin. + pub deposit: BalanceOf, + + /// Mapping to the unique `LaneId`. + pub lane_id: LaneId, +} + +/// Locations of bridge endpoints at both sides of the bridge. +#[derive(Clone, RuntimeDebug, PartialEq, Eq)] +pub struct BridgeLocations { + /// Relative (to this bridge hub) location of this side of the bridge. + bridge_origin_relative_location: Location, + /// Universal (unique) location of this side of the bridge. + bridge_origin_universal_location: InteriorLocation, + /// Universal (unique) location of other side of the bridge. + bridge_destination_universal_location: InteriorLocation, + /// An identifier of the dedicated bridge message lane. + bridge_id: BridgeId, +} + +/// Errors that may happen when we check bridge locations. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum BridgeLocationsError { + /// Origin or destination locations are not universal. + NonUniversalLocation, + /// Bridge origin location is not supported. + InvalidBridgeOrigin, + /// Bridge destination is not supported (in general). + InvalidBridgeDestination, + /// Destination location is within the same global consensus. + DestinationIsLocal, + /// Destination network is not the network we are bridged with. + UnreachableDestination, + /// Destination location is unsupported. We only support bridges with relay + /// chain or its parachains. + UnsupportedDestinationLocation, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, +} + +impl BridgeLocations { + /// Given XCM locations, generate lane id and universal locations of bridge endpoints. + /// + /// The `here_universal_location` is the universal location of the bridge hub runtime. + /// + /// The `bridge_origin_relative_location` is the relative (to the `here_universal_location`) + /// location of the bridge endpoint at this side of the bridge. It may be the parent relay + /// chain or the sibling parachain. All junctions below parachain level are dropped. + /// + /// The `bridge_destination_universal_location` is the universal location of the bridge + /// destination. It may be the parent relay or the sibling parachain of the **bridged** + /// bridge hub. All junctions below parachain level are dropped. + /// + /// Why we drop all junctions between parachain level - that's because the lane is a bridge + /// between two chains. All routing under this level happens when the message is delivered + /// to the bridge destination. So at bridge level we don't care about low level junctions. + /// + /// Returns error if `bridge_origin_relative_location` is outside of `here_universal_location` + /// local consensus OR if `bridge_destination_universal_location` is not a universal location. + pub fn bridge_locations( + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + expected_remote_network: NetworkId, + ) -> Result, BridgeLocationsError> { + fn strip_low_level_junctions( + location: InteriorLocation, + ) -> Result { + let mut junctions = location.into_iter(); + + let global_consensus = junctions + .next() + .filter(|junction| matches!(junction, GlobalConsensus(_))) + .ok_or(BridgeLocationsError::NonUniversalLocation)?; + + // we only expect `Parachain` junction here. There are other junctions that + // may need to be supported (like `GeneralKey` and `OnlyChild`), but now we + // only support bridges with relay and parachans + // + // if there's something other than parachain, let's strip it + let maybe_parachain = + junctions.next().filter(|junction| matches!(junction, Parachain(_))); + Ok(match maybe_parachain { + Some(parachain) => [global_consensus, parachain].into(), + None => [global_consensus].into(), + }) + } + + // ensure that the `here_universal_location` and `bridge_destination_universal_location` + // are universal locations within different consensus systems + let local_network = here_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + let remote_network = bridge_destination_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + ensure!(local_network != remote_network, BridgeLocationsError::DestinationIsLocal); + ensure!( + remote_network == expected_remote_network, + BridgeLocationsError::UnreachableDestination + ); + + // get universal location of endpoint, located at this side of the bridge + let bridge_origin_universal_location = here_universal_location + .within_global(bridge_origin_relative_location.clone()) + .map_err(|_| BridgeLocationsError::InvalidBridgeOrigin)?; + // strip low-level junctions within universal locations + let bridge_origin_universal_location = + strip_low_level_junctions(bridge_origin_universal_location)?; + let bridge_destination_universal_location = + strip_low_level_junctions(bridge_destination_universal_location)?; + + // we know that the `bridge_destination_universal_location` starts from the + // `GlobalConsensus` and we know that the `bridge_origin_universal_location` + // is also within the `GlobalConsensus`. So we know that the lane id will be + // the same on both ends of the bridge + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location, + bridge_origin_universal_location, + bridge_destination_universal_location, + bridge_id, + })) + } + + /// Getter for `bridge_origin_relative_location` + pub fn bridge_origin_relative_location(&self) -> &Location { + &self.bridge_origin_relative_location + } + + /// Getter for `bridge_origin_universal_location` + pub fn bridge_origin_universal_location(&self) -> &InteriorLocation { + &self.bridge_origin_universal_location + } + + /// Getter for `bridge_destination_universal_location` + pub fn bridge_destination_universal_location(&self) -> &InteriorLocation { + &self.bridge_destination_universal_location + } + + /// Getter for `bridge_id` + pub fn bridge_id(&self) -> &BridgeId { + &self.bridge_id + } + + /// Generates the exact same `LaneId` on the both bridge hubs. + /// + /// Note: Use this **only** when opening a new bridge. + pub fn calculate_lane_id( + &self, + xcm_version: XcmVersion, + ) -> Result { + // a tricky helper struct that adds required `Ord` support for + // `VersionedInteriorLocation` + #[derive(Eq, PartialEq, Ord, PartialOrd)] + struct EncodedVersionedInteriorLocation(sp_std::vec::Vec); + impl Encode for EncodedVersionedInteriorLocation { + fn encode(&self) -> sp_std::vec::Vec { + self.0.clone() + } + } + + let universal_location1 = + VersionedInteriorLocation::from(self.bridge_origin_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + let universal_location2 = + VersionedInteriorLocation::from(self.bridge_destination_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + + Ok(LaneId::new( + EncodedVersionedInteriorLocation(universal_location1.encode()), + EncodedVersionedInteriorLocation(universal_location2.encode()), + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const LOCAL_NETWORK: NetworkId = Kusama; + const REMOTE_NETWORK: NetworkId = Polkadot; + const UNREACHABLE_NETWORK: NetworkId = Rococo; + const SIBLING_PARACHAIN: u32 = 1000; + const LOCAL_BRIDGE_HUB: u32 = 1001; + const REMOTE_PARACHAIN: u32 = 2000; + + struct SuccessfulTest { + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + + bridge_origin_universal_location: InteriorLocation, + bridge_destination_universal_location: InteriorLocation, + + expected_remote_network: NetworkId, + } + + fn run_successful_test(test: SuccessfulTest) -> BridgeLocations { + let locations = BridgeLocations::bridge_locations( + test.here_universal_location, + test.bridge_origin_relative_location.clone(), + test.bridge_destination_universal_location.clone(), + test.expected_remote_network, + ); + assert_eq!( + locations, + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location: test.bridge_origin_relative_location, + bridge_origin_universal_location: test.bridge_origin_universal_location.clone(), + bridge_destination_universal_location: test + .bridge_destination_universal_location + .clone(), + bridge_id: BridgeId::new( + &test.bridge_origin_universal_location, + &test.bridge_destination_universal_location, + ), + })), + ); + + *locations.unwrap() + } + + // successful tests that with various origins and destinations + + #[test] + fn at_relay_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + // successful tests that show that we are ignoring low-level junctions of bridge origins + + #[test] + fn low_level_junctions_at_bridge_origin_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [PalletInstance(0)].into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn low_level_junctions_at_bridge_destination_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn calculate_lane_id_works() { + let from_local_to_remote = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + let from_remote_to_local = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(REMOTE_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(REMOTE_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + + expected_remote_network: LOCAL_NETWORK, + }); + + assert_ne!( + from_local_to_remote.calculate_lane_id(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id(xcm::latest::VERSION - 1), + ); + assert_eq!( + from_local_to_remote.calculate_lane_id(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id(xcm::latest::VERSION), + ); + } + + // negative tests + + #[test] + fn bridge_locations_fails_when_here_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [Parachain(1000)].into(), + Here.into(), + [GlobalConsensus(REMOTE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_local() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(LOCAL_NETWORK), OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::DestinationIsLocal), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_unreachable() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(UNREACHABLE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::UnreachableDestination), + ); + } +} diff --git a/bridges/relays/client-substrate/Cargo.toml b/bridges/relays/client-substrate/Cargo.toml index 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/lib-substrate-relay/Cargo.toml b/bridges/relays/lib-substrate-relay/Cargo.toml index b0f93e5b5485f24b230b9b2868c6301b6ed64181..89115cfeee92dc133eeb84914ec4063ee056f8a3 100644 --- a/bridges/relays/lib-substrate-relay/Cargo.toml +++ b/bridges/relays/lib-substrate-relay/Cargo.toml @@ -22,6 +22,7 @@ num-traits = { workspace = true, default-features = true } rbtag = { workspace = true } structopt = { workspace = true } strum = { features = ["derive"], workspace = true, default-features = true } +rustc-hex = { workspace = true } thiserror = { workspace = true } # Bridge dependencies diff --git a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs index 5631285b3c544de0e3caf85a6b74b7ee31601c56..28b0eb0ad526789d44ec6936e01e269f4e93a816 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs @@ -22,7 +22,7 @@ use crate::{ messages::{MessagesRelayLimits, SubstrateMessageLane}, parachains::SubstrateParachainsPipeline, }; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain, }; diff --git a/bridges/relays/lib-substrate-relay/src/cli/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/mod.rs index ddb3e416dc32628b5a85f27f322d0568cbee1c10..ef8403ff68ee1ea2d849e5f221c439ef537e8d35 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/mod.rs @@ -16,13 +16,14 @@ //! Deal with CLI args of substrate-to-substrate relay. -use codec::{Decode, Encode}; +use bp_messages::LaneId; use rbtag::BuildInfo; +use sp_core::H256; +use sp_runtime::Either; +use std::str::FromStr; use structopt::StructOpt; use strum::{EnumString, VariantNames}; -use bp_messages::LaneId; - pub mod bridge; pub mod chain_schema; pub mod detect_equivocations; @@ -42,45 +43,36 @@ pub type DefaultClient = relay_substrate_client::RpcWithCachingClient; /// Lane id. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HexLaneId(pub [u8; 4]); +pub struct HexLaneId(Either); impl From for LaneId { fn from(lane_id: HexLaneId) -> LaneId { - LaneId(lane_id.0) + LaneId::from_inner(lane_id.0) } } -impl std::str::FromStr for HexLaneId { - type Err = hex::FromHexError; - - fn from_str(s: &str) -> Result { - let mut lane_id = [0u8; 4]; - hex::decode_to_slice(s, &mut lane_id)?; - Ok(HexLaneId(lane_id)) - } -} - -/// Nicer formatting for raw bytes vectors. -#[derive(Default, Encode, Decode, PartialEq, Eq)] -pub struct HexBytes(pub Vec); - -impl std::str::FromStr for HexBytes { - type Err = hex::FromHexError; +impl FromStr for HexLaneId { + type Err = rustc_hex::FromHexError; fn from_str(s: &str) -> Result { - Ok(Self(hex::decode(s)?)) - } -} - -impl std::fmt::Debug for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "0x{self}") - } -} - -impl std::fmt::Display for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", hex::encode(&self.0)) + // check `H256` variant at first + match H256::from_str(s) { + Ok(hash) => Ok(HexLaneId(Either::Left(hash))), + Err(hash_error) => { + // check backwards compatible + let mut lane_id = [0u8; 4]; + match hex::decode_to_slice(s, &mut lane_id) { + Ok(_) => Ok(HexLaneId(Either::Right(lane_id))), + Err(array_error) => { + log::error!( + target: "bridge", + "Failed to parse `HexLaneId` as hex string: {s:?} - hash_error: {hash_error:?}, array_error: {array_error:?}", + ); + Err(hash_error) + }, + } + }, + } } } @@ -182,15 +174,32 @@ mod tests { use super::*; #[test] - fn hex_bytes_display_matches_from_str_for_clap() { - // given - let hex = HexBytes(vec![1, 2, 3, 4]); - let display = format!("{hex}"); - - // when - let hex2: HexBytes = display.parse().unwrap(); - - // then - assert_eq!(hex.0, hex2.0); + fn hex_lane_id_from_str_works() { + // hash variant + assert!(HexLaneId::from_str( + "101010101010101010101010101010101010101010101010101010101010101" + ) + .is_err()); + assert!(HexLaneId::from_str( + "00101010101010101010101010101010101010101010101010101010101010101" + ) + .is_err()); + assert_eq!( + LaneId::from( + HexLaneId::from_str( + "0101010101010101010101010101010101010101010101010101010101010101" + ) + .unwrap() + ), + LaneId::from_inner(Either::Left(H256::from([1u8; 32]))) + ); + + // array variant + assert!(HexLaneId::from_str("0000001").is_err()); + assert!(HexLaneId::from_str("000000001").is_err()); + assert_eq!( + LaneId::from(HexLaneId::from_str("00000001").unwrap()), + LaneId::from_inner(Either::Right([0, 0, 0, 1])) + ); } } diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs index 338dda3c63309acbefd2616d052ae5dc4bf1d1e0..3786976bed9b0723af47b6f81da1b813405fa95c 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs @@ -55,7 +55,7 @@ use sp_core::Pair; #[derive(Debug, PartialEq, StructOpt)] pub struct HeadersAndMessagesSharedParams { /// Hex-encoded lane identifiers that should be served by the complex relay. - #[structopt(long, default_value = "00000000")] + #[structopt(long)] pub lane: Vec, /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) /// are relayed. @@ -359,6 +359,8 @@ mod tests { use crate::{cli::chain_schema::RuntimeVersionType, declare_chain_cli_schema}; use relay_substrate_client::{ChainRuntimeVersion, Parachain, SimpleRuntimeVersion}; + use sp_core::H256; + use sp_runtime::Either; #[test] // We need `#[allow(dead_code)]` because some of the methods generated by the macros @@ -422,7 +424,7 @@ mod tests { "--polkadot-port", "9944", "--lane", - "00000000", + "0000000000000000000000000000000000000000000000000000000000000000", "--prometheus-host", "0.0.0.0", ]); @@ -432,7 +434,7 @@ mod tests { res, BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages { shared: HeadersAndMessagesSharedParams { - lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], + lane: vec![HexLaneId(Either::Left(H256::from([0x00u8; 32])))], only_mandatory_headers: false, only_free_headers: false, prometheus_params: PrometheusParams { diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs index 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..34d5226e90c59981ffca14f7bafafc3eb64a266c 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs @@ -37,8 +37,8 @@ use relay_utils::UniqueSaturatedInto; /// Messages relaying params. #[derive(StructOpt)] pub struct RelayMessagesParams { - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, #[structopt(flatten)] source: SourceConnectionParams, @@ -59,8 +59,8 @@ pub struct RelayMessagesRangeParams { /// This header must be previously proved to the target chain. #[structopt(long)] at_source_block: u128, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, /// Nonce (inclusive) of the first message to relay. #[structopt(long)] @@ -88,8 +88,8 @@ pub struct RelayMessagesDeliveryConfirmationParams { /// delivery proof. This header must be previously proved to the source chain. #[structopt(long)] at_target_block: u128, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, #[structopt(flatten)] source: SourceConnectionParams, diff --git a/bridges/relays/lib-substrate-relay/src/messages/mod.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs index e52b7020666941c9a8d9937655496acf451c5379..28bc5c7f5e8e60875ac5e294225a0b9285c5f7ae 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs @@ -644,7 +644,7 @@ where FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: Default::default(), + lane: LaneId::new(1, 2), nonces_start: 1, nonces_end: messages as u64, }, @@ -706,7 +706,7 @@ mod tests { let receive_messages_proof = FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: LaneId::new(1, 2), nonces_start: 0, nonces_end: 0, }; @@ -761,7 +761,7 @@ mod tests { let receive_messages_delivery_proof = FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: LaneId::new(1, 2), }; let relayers_state = UnrewardedRelayersState { unrewarded_relayer_entries: 0, @@ -838,7 +838,6 @@ mod tests { type ThisChain = ThisUnderlyingChain; type BridgedChain = BridgedUnderlyingChain; type BridgedHeaderChain = BridgedHeaderChain; - type ActiveOutboundLanes = (); type OutboundPayload = Vec; type InboundPayload = Vec; type DeliveryPayments = (); diff --git a/bridges/relays/lib-substrate-relay/src/messages/source.rs b/bridges/relays/lib-substrate-relay/src/messages/source.rs index b75fc86d5eee20d247de3cbc26f324ed46e9ad2d..2c49df3452abbaa60f40c313b0860729570216fb 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/source.rs @@ -661,7 +661,7 @@ mod tests { } let maybe_batches = - split_msgs_to_refine::(Default::default(), msgs_to_refine); + split_msgs_to_refine::(LaneId::new(1, 2), msgs_to_refine); match expected_batches { Ok(expected_batches) => { let batches = maybe_batches.unwrap(); diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs index 4579222a2c681c49e076f67d2eacaeb1dc8b9fca..2ef86f48ecbeb6a3c5e6084f16ab9e50352fed4f 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -31,11 +31,11 @@ use async_std::{ sync::{Arc, Mutex}, }; use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; use relay_substrate_client::{ is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, diff --git a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs index 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/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs index b681d86d2ae8fadb6b0d5b5277de5a1285533d36..995499092c3ee2f51fdac54340767e39d8d218d9 100644 --- a/bridges/relays/messages/src/message_lane_loop.rs +++ b/bridges/relays/messages/src/message_lane_loop.rs @@ -276,7 +276,7 @@ pub struct ClientsState { /// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs /// sync loop. pub fn metrics_prefix(lane: &LaneId) -> String { - format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane)) + format!("{}_to_{}_MessageLane_{:?}", P::SOURCE_NAME, P::TARGET_NAME, lane) } /// Run message lane service loop. @@ -957,7 +957,7 @@ pub(crate) mod tests { }; let _ = run( Params { - lane: LaneId([0, 0, 0, 0]), + lane: LaneId::new(1, 2), source_tick: Duration::from_millis(100), target_tick: Duration::from_millis(100), reconnect_delay: Duration::from_millis(0), @@ -1274,4 +1274,12 @@ pub(crate) mod tests { assert!(!result.target_to_source_header_requirements.is_empty()); assert!(!result.source_to_target_header_requirements.is_empty()); } + + #[test] + fn metrics_prefix_is_valid() { + assert!(MessageLaneLoopMetrics::new(Some(&metrics_prefix::( + &LaneId::new(1, 2) + ))) + .is_ok()); + } } diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs index 0fd1d72c7075bc29632280615f203e0c0028359b..59ca458e6667cd29a7e133fe9e95bf0e265f33cd 100644 --- a/bridges/relays/parachains/src/parachains_loop.rs +++ b/bridges/relays/parachains/src/parachains_loop.rs @@ -496,7 +496,7 @@ where }, (AvailableHeader::Missing, Some(_)) => { // parachain/parathread has been offboarded removed from the system. It needs to - // be propageted to the target client + // be propagated to the target client true }, (AvailableHeader::Missing, None) => { diff --git a/bridges/relays/utils/Cargo.toml b/bridges/relays/utils/Cargo.toml index beb03b9381d4f8289f6119fd57c94b04de9faa44..4c25566607dcdb574dce7f36183867fee0d29742 100644 --- a/bridges/relays/utils/Cargo.toml +++ b/bridges/relays/utils/Cargo.toml @@ -11,7 +11,7 @@ publish = false workspace = true [dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } async-std = { workspace = true } async-trait = { workspace = true } backoff = { workspace = true } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs index 6894977c21f4a2a78b1c7f67eb0b36a791936b92..84b1476931c976bfac8b06202d82affeb4cc0301 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -33,7 +33,10 @@ mod tests; mod benchmarking; use frame_support::{ - dispatch::DispatchResult, pallet_prelude::OptionQuery, traits::Get, transactional, + dispatch::{DispatchResult, PostDispatchInfo}, + pallet_prelude::OptionQuery, + traits::Get, + transactional, }; use frame_system::ensure_signed; use snowbridge_beacon_primitives::{ @@ -82,6 +85,9 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; #[pallet::constant] type ForkVersions: Get; + /// Minimum gap between finalized headers for an update to be free. + #[pallet::constant] + type FreeHeadersInterval: Get; type WeightInfo: WeightInfo; } @@ -204,11 +210,10 @@ pub mod pallet { #[transactional] /// Submits a new finalized beacon header update. The update may contain the next /// sync committee. - pub fn submit(origin: OriginFor, update: Box) -> DispatchResult { + pub fn submit(origin: OriginFor, update: Box) -> DispatchResultWithPostInfo { ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); - Self::process_update(&update)?; - Ok(()) + Self::process_update(&update) } /// Halt or resume all pallet operations. May only be called by root. @@ -280,10 +285,9 @@ pub mod pallet { Ok(()) } - pub(crate) fn process_update(update: &Update) -> DispatchResult { + pub(crate) fn process_update(update: &Update) -> DispatchResultWithPostInfo { Self::verify_update(update)?; - Self::apply_update(update)?; - Ok(()) + Self::apply_update(update) } /// References and strictly follows @@ -432,8 +436,9 @@ pub mod pallet { /// Reference and strictly follows DispatchResult { + /// SyncCommitteePrepared type. Stores the provided finalized header. Updates are free + /// if the certain conditions specified in `check_refundable` are met. + fn apply_update(update: &Update) -> DispatchResultWithPostInfo { let latest_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()) .ok_or(Error::::NotBootstrapped)?; @@ -465,11 +470,17 @@ pub mod pallet { }); }; + let pays_fee = Self::check_refundable(update, latest_finalized_state.slot); + let actual_weight = match update.next_sync_committee_update { + None => T::WeightInfo::submit(), + Some(_) => T::WeightInfo::submit_with_sync_committee(), + }; + if update.finalized_header.slot > latest_finalized_state.slot { Self::store_finalized_header(update.finalized_header, update.block_roots_root)?; } - Ok(()) + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) } /// Computes the signing root for a given beacon header and domain. The hash tree root @@ -634,11 +645,31 @@ pub mod pallet { config::SLOTS_PER_EPOCH as u64, )); let domain_type = config::DOMAIN_SYNC_COMMITTEE.to_vec(); - // Domains are used for for seeds, for signatures, and for selecting aggregators. + // Domains are used for seeds, for signatures, and for selecting aggregators. let domain = Self::compute_domain(domain_type, fork_version, validators_root)?; // Hash tree root of SigningData - object root + domain let signing_root = Self::compute_signing_root(header, domain)?; Ok(signing_root) } + + /// Updates are free if the update is successful and the interval between the latest + /// finalized header in storage and the newly imported header is large enough. All + /// successful sync committee updates are free. + pub(super) fn check_refundable(update: &Update, latest_slot: u64) -> Pays { + // If the sync committee was successfully updated, the update may be free. + if update.next_sync_committee_update.is_some() { + return Pays::No; + } + + // If the latest finalized header is larger than the minimum slot interval, the header + // import transaction is free. + if update.finalized_header.slot >= + latest_slot.saturating_add(T::FreeHeadersInterval::get() as u64) + { + return Pays::No; + } + + Pays::Yes + } } } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 96298d4fa8962641d6ec8bab0ef67b751ab749cc..be456565d407a944eb853ec9bd2921f3d81a3862 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -10,6 +10,7 @@ use sp_std::default::Default; use std::{fs::File, path::PathBuf}; type Block = frame_system::mocking::MockBlock; +use frame_support::traits::ConstU32; use sp_runtime::BuildStorage; fn load_fixture(basename: String) -> Result @@ -108,9 +109,12 @@ parameter_types! { }; } +pub const FREE_SLOTS_INTERVAL: u32 = config::SLOTS_PER_EPOCH as u32; + impl ethereum_beacon_client::Config for Test { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32; type WeightInfo = (); } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs index c16743b75ea40b793947240773d159da908dab01..82a3b8224470c12ecdd8243f9d7053adc824e6d4 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -1,21 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork use crate::{ - functions::compute_period, sync_committee_sum, verify_merkle_branch, BeaconHeader, - CompactBeaconState, Error, FinalizedBeaconState, LatestFinalizedBlockRoot, NextSyncCommittee, - SyncCommitteePrepared, -}; - -use crate::mock::{ - get_message_verification_payload, load_checkpoint_update_fixture, - load_finalized_header_update_fixture, load_next_finalized_header_update_fixture, - load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, + functions::compute_period, + mock::{ + get_message_verification_payload, load_checkpoint_update_fixture, + load_finalized_header_update_fixture, load_next_finalized_header_update_fixture, + load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, + }, + sync_committee_sum, verify_merkle_branch, BeaconHeader, CompactBeaconState, Error, + FinalizedBeaconState, LatestFinalizedBlockRoot, NextSyncCommittee, SyncCommitteePrepared, }; pub use crate::mock::*; use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT}; -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::{assert_err, assert_noop, assert_ok, pallet_prelude::Pays}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, Fork, ForkVersions, NextSyncCommitteeUpdate, VersionedExecutionPayloadHeader, @@ -129,6 +128,39 @@ pub fn compute_domain_bls() { }); } +#[test] +pub fn may_refund_call_fee() { + let finalized_update = Box::new(load_next_finalized_header_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + new_tester().execute_with(|| { + let free_headers_interval: u64 = crate::mock::FREE_SLOTS_INTERVAL as u64; + // Not free, smaller than the allowed free header interval + assert_eq!( + EthereumBeaconClient::check_refundable( + &finalized_update.clone(), + finalized_update.finalized_header.slot + free_headers_interval + ), + Pays::Yes + ); + // Is free, larger than the minimum interval + assert_eq!( + EthereumBeaconClient::check_refundable( + &finalized_update, + finalized_update.finalized_header.slot - (free_headers_interval + 2) + ), + Pays::No + ); + // Is free, valid sync committee update + assert_eq!( + EthereumBeaconClient::check_refundable( + &sync_committee_update, + finalized_update.finalized_header.slot + ), + Pays::No + ); + }); +} + #[test] pub fn verify_merkle_branch_for_finalized_root() { new_tester().execute_with(|| { @@ -340,7 +372,9 @@ fn submit_update_in_current_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::Yes); let block_root: H256 = update.finalized_header.hash_tree_root().unwrap(); assert!(>::contains_key(block_root)); }); @@ -357,7 +391,9 @@ fn submit_update_with_sync_committee_in_current_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); }); } @@ -374,20 +410,21 @@ fn reject_submit_update_in_next_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - sync_committee_update.clone() - )); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + // check an update in the next period is rejected - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()), - Error::::SyncCommitteeUpdateRequired - ); + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_err!(second_result, Error::::SyncCommitteeUpdateRequired); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); + // submit update with next sync committee - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - next_sync_committee_update - )); + let third_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_sync_committee_update); + assert_ok!(third_result); + assert_eq!(third_result.unwrap().pays_fee, Pays::No); // check same header in the next period can now be submitted successfully assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); let block_root: H256 = update.finalized_header.clone().hash_tree_root().unwrap(); @@ -407,10 +444,9 @@ fn submit_update_with_invalid_header_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidHeaderMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidHeaderMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -426,10 +462,9 @@ fn submit_update_with_invalid_block_roots_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidBlockRootsRootMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidBlockRootsRootMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -447,10 +482,9 @@ fn submit_update_with_invalid_next_sync_committee_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidSyncCommitteeMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidSyncCommitteeMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -464,14 +498,14 @@ fn submit_update_with_skipped_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - sync_committee_update.clone() - )); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::SkippedSyncCommitteePeriod - ); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(second_result, Error::::SkippedSyncCommitteePeriod); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -487,9 +521,16 @@ fn submit_update_with_sync_committee_in_next_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone())); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); + assert_ok!(second_result); + assert_eq!(second_result.unwrap().pays_fee, Pays::No); let last_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()).unwrap(); let last_synced_period = compute_period(last_finalized_state.slot); @@ -505,13 +546,12 @@ fn submit_update_with_sync_committee_invalid_signature_slot() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - // makes a invalid update with signature_slot should be more than attested_slot + // makes an invalid update with signature_slot should be more than attested_slot update.signature_slot = update.attested_header.slot; - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidUpdateSlot - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidUpdateSlot); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -525,10 +565,9 @@ fn submit_update_with_skipped_sync_committee_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update), - Error::::SkippedSyncCommitteePeriod - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update); + assert_err!(result, Error::::SkippedSyncCommitteePeriod); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -546,10 +585,9 @@ fn submit_irrelevant_update() { update.attested_header.slot = checkpoint.header.slot; update.signature_slot = checkpoint.header.slot + 1; - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::IrrelevantUpdate - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::IrrelevantUpdate); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -558,10 +596,9 @@ fn submit_update_with_missing_bootstrap() { let update = Box::new(load_next_finalized_header_update_fixture()); new_tester().execute_with(|| { - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::NotBootstrapped - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::NotBootstrapped); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -574,7 +611,9 @@ fn submit_update_with_invalid_sync_committee_update() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); // makes update with invalid next_sync_committee >::mutate(>::get(), |x| { @@ -586,10 +625,9 @@ fn submit_update_with_invalid_sync_committee_update() { let next_sync_committee = NextSyncCommitteeUpdate::default(); next_update.next_sync_committee_update = Some(next_sync_committee); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update), - Error::::InvalidSyncCommitteeUpdate - ); + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update); + assert_err!(second_result, Error::::InvalidSyncCommitteeUpdate); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -612,12 +650,15 @@ fn submit_finalized_header_update_with_too_large_gap() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), - Error::::InvalidFinalizedHeaderGap - ); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); + assert_err!(second_result, Error::::InvalidFinalizedHeaderGap); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -637,14 +678,41 @@ fn submit_finalized_header_update_with_gap_at_limit() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), + second_result, // The test should pass the InvalidFinalizedHeaderGap check, and will fail at the // next check, the merkle proof, because we changed the next_update slots. Error::::InvalidHeaderMerkleProof ); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); + }); +} + +#[test] +fn duplicate_sync_committee_updates_are_not_free() { + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + + new_tester().execute_with(|| { + assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + + // Check that if the same update is submitted, the update is not free. + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update); + assert_err!(second_result, Error::::IrrelevantUpdate); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/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/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..5cff8413af66b23fe55fc6c68f8b936768289ce9 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -9,9 +9,10 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; use scale_info::TypeInfo; -use sp_core::{Get, RuntimeDebug, H160}; +use snowbridge_core::TokenId; +use sp_core::{Get, RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::MultiAddress; +use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; use xcm_executor::traits::ConvertLocation; @@ -45,7 +46,7 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, - /// Send a token to AssetHub or another parachain + /// Send Ethereum token to AssetHub or another parachain SendToken { /// The address of the ERC20 token to be bridged over to AssetHub token: H160, @@ -56,6 +57,17 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, + /// Send Polkadot token back to the original parachain + SendNativeToken { + /// The Id of the token + token_id: TokenId, + /// The destination for the transfer + destination: Destination, + /// Amount to transfer + amount: u128, + /// XCM execution fee on AssetHub + fee: u128, + }, } /// Destination for bridged tokens @@ -89,10 +101,16 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { _phantom: PhantomData<( CreateAssetCall, @@ -100,6 +118,9 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, )>, } @@ -108,6 +129,11 @@ pub struct MessageToXcm< pub enum ConvertMessageError { /// The message version is not supported for conversion. UnsupportedVersion, + InvalidDestination, + InvalidToken, + /// The fee asset is not supported for conversion. + UnsupportedFeeAsset, + CannotReanchor, } /// convert the inbound message to xcm which will be forwarded to the destination chain @@ -115,51 +141,107 @@ pub trait ConvertMessage { type Balance: BalanceT + From; type AccountId; /// Converts a versioned message into an XCM message and an optional topicID - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; } pub type CallIndex = [u8; 2]; -impl - ConvertMessage +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > ConvertMessage for MessageToXcm< CreateAssetCall, CreateAssetDeposit, InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { type Balance = Balance; type AccountId = AccountId; - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { use Command::*; use VersionedMessage::*; match message { V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => - Ok(Self::convert_register_token(chain_id, token, fee)), + Ok(Self::convert_register_token(message_id, chain_id, token, fee)), V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => - Ok(Self::convert_send_token(chain_id, token, destination, amount, fee)), + Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), + V1(MessageV1 { + chain_id, + command: SendNativeToken { token_id, destination, amount, fee }, + }) => Self::convert_send_native_token( + message_id, + chain_id, + token_id, + destination, + amount, + fee, + ), } } } -impl - MessageToXcm -where +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > + MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { - fn convert_register_token(chain_id: u64, token: H160, fee: u128) -> (Xcm<()>, Balance) { + fn convert_register_token( + message_id: H256, + chain_id: u64, + token: H160, + fee: u128, + ) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; let xcm_fee: Asset = (Location::parent(), fee).into(); let deposit: Asset = (Location::parent(), CreateAssetDeposit::get()).into(); @@ -202,6 +284,8 @@ where // Clear the origin so that remaining assets in holding // are claimable by the physical origin (BridgeHub) ClearOrigin, + // Forward message id to Asset Hub + SetTopic(message_id.into()), ] .into(); @@ -209,6 +293,7 @@ where } fn convert_send_token( + message_id: H256, chain_id: u64, token: H160, destination: Destination, @@ -266,6 +351,8 @@ where BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, // Deposit asset to beneficiary. DepositAsset { assets: Definite(asset.into()), beneficiary }, + // Forward message id to destination parachain. + SetTopic(message_id.into()), ] .into(), }, @@ -281,6 +368,9 @@ where }, } + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + (instructions.into(), total_fees.into()) } @@ -291,6 +381,59 @@ where [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], ) } + + /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign + /// account of the Gateway contract and either deposits those assets into a recipient account or + /// forwards the assets to another parachain. + fn convert_send_native_token( + message_id: H256, + chain_id: u64, + token_id: TokenId, + destination: Destination, + amount: u128, + asset_hub_fee: u128, + ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { + let network = Ethereum { chain_id }; + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + + let beneficiary = match destination { + // Final destination is a 32-byte account on AssetHub + Destination::AccountId32 { id } => + Ok(Location::new(0, [AccountId32 { network: None, id }])), + _ => Err(ConvertMessageError::InvalidDestination), + }?; + + let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + + let asset_loc = + ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; + + let mut reanchored_asset_loc = asset_loc.clone(); + reanchored_asset_loc + .reanchor(&GlobalAssetHubLocation::get(), &EthereumUniversalLocation::get()) + .map_err(|_| ConvertMessageError::CannotReanchor)?; + + let asset: Asset = (reanchored_asset_loc, amount).into(); + + let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); + + let instructions = vec![ + ReceiveTeleportedAsset(total_fee_asset.clone().into()), + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), + UniversalOrigin(GlobalConsensus(network)), + WithdrawAsset(asset.clone().into()), + // Deposit both asset and fees to beneficiary so the fees will not get + // trapped. Another benefit is when fees left more than ED on AssetHub could be + // used to create the beneficiary account in case it does not exist. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + SetTopic(message_id.into()), + ]; + + // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also + // teleport fees) + Ok((instructions.into(), asset_hub_fee.into())) + } } pub struct GlobalConsensusEthereumConvertsFor(PhantomData); diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 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..d3b6c116dd7a3420c730f77614de61f355b64319 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -12,9 +12,10 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ outbound::{AgentExecuteCommand, Command, Message, SendMessage}, - ChannelId, ParaId, + AgentId, ChannelId, ParaId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -24,15 +25,31 @@ pub struct EthereumBlobExporter< EthereumNetwork, OutboundQueue, AgentHashedDescription, ->(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); - -impl ExportXcm - for EthereumBlobExporter -where + ConvertAssetId, +>( + PhantomData<( + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + )>, +); + +impl + ExportXcm + for EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + > where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, AgentHashedDescription: ConvertLocation, + ConvertAssetId: MaybeEquivalence, { type Ticket = (Vec, XcmHash); @@ -87,13 +104,8 @@ where SendError::MissingArgument })?; - let mut converter = XcmConverter::new(&message, &expected_network); - let (agent_execute_command, message_id) = converter.convert().map_err(|err|{ - log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); - SendError::Unroutable - })?; - let source_location = Location::new(1, local_sub.clone()); + let agent_id = match AgentHashedDescription::convert_location(&source_location) { Some(id) => id, None => { @@ -102,13 +114,16 @@ where }, }; + let mut converter = + XcmConverter::::new(&message, expected_network, agent_id); + let (command, message_id) = converter.convert().map_err(|err|{ + log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); + SendError::Unroutable + })?; + let channel_id: ChannelId = ParaId::from(para_id).into(); - let outbound_message = Message { - id: Some(message_id.into()), - channel_id, - command: Command::AgentExecute { agent_id, command: agent_execute_command }, - }; + let outbound_message = Message { id: Some(message_id.into()), channel_id, command }; // validate the message let (ticket, fee) = OutboundQueue::validate(&outbound_message).map_err(|err| { @@ -154,6 +169,9 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, + ReserveAssetDepositedExpected, + InvalidAsset, + UnexpectedInstruction, } macro_rules! match_expression { @@ -165,18 +183,33 @@ macro_rules! match_expression { }; } -struct XcmConverter<'a, Call> { +struct XcmConverter<'a, ConvertAssetId, Call> { iter: Peekable>>, - ethereum_network: &'a NetworkId, + ethereum_network: NetworkId, + agent_id: AgentId, + _marker: PhantomData, } -impl<'a, Call> XcmConverter<'a, Call> { - fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { - Self { iter: message.inner().iter().peekable(), ethereum_network } +impl<'a, ConvertAssetId, Call> XcmConverter<'a, ConvertAssetId, Call> +where + ConvertAssetId: MaybeEquivalence, +{ + fn new(message: &'a Xcm, ethereum_network: NetworkId, agent_id: AgentId) -> Self { + Self { + iter: message.inner().iter().peekable(), + ethereum_network, + agent_id, + _marker: Default::default(), + } } - fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { - // Get withdraw/deposit and make native tokens create message. - let result = self.native_tokens_unlock_message()?; + fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + let result = match self.peek() { + Ok(ReserveAssetDeposited { .. }) => self.send_native_tokens_message(), + // Get withdraw/deposit and make native tokens create message. + Ok(WithdrawAsset { .. }) => self.send_tokens_message(), + Err(e) => Err(e), + _ => return Err(XcmConverterError::UnexpectedInstruction), + }?; // All xcm instructions must be consumed before exit. if self.next().is_ok() { @@ -186,9 +219,7 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(result) } - fn native_tokens_unlock_message( - &mut self, - ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn send_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -262,7 +293,13 @@ impl<'a, Call> XcmConverter<'a, Call> { // Check if there is a SetTopic and skip over it if found. let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; - Ok((AgentExecuteCommand::TransferToken { token, recipient, amount }, *topic_id)) + Ok(( + Command::AgentExecute { + agent_id: self.agent_id, + command: AgentExecuteCommand::TransferToken { token, recipient, amount }, + }, + *topic_id, + )) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -275,9 +312,95 @@ impl<'a, Call> XcmConverter<'a, Call> { fn network_matches(&self, network: &Option) -> bool { if let Some(network) = network { - network == self.ethereum_network + *network == self.ethereum_network } else { true } } + + /// Convert the xcm for Polkadot-native token from AH into the Command + /// To match transfers of Polkadot-native tokens, we expect an input of the form: + /// # ReserveAssetDeposited + /// # ClearOrigin + /// # BuyExecution + /// # DepositAsset + /// # SetTopic + fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + use XcmConverterError::*; + + // Get the reserve assets. + let reserve_assets = + match_expression!(self.next()?, ReserveAssetDeposited(reserve_assets), reserve_assets) + .ok_or(ReserveAssetDepositedExpected)?; + + // Check if clear origin exists and skip over it. + if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { + let _ = self.next(); + } + + // Get the fee asset item from BuyExecution or continue parsing. + let fee_asset = match_expression!(self.peek(), Ok(BuyExecution { fees, .. }), fees); + if fee_asset.is_some() { + let _ = self.next(); + } + + let (deposit_assets, beneficiary) = match_expression!( + self.next()?, + DepositAsset { assets, beneficiary }, + (assets, beneficiary) + ) + .ok_or(DepositAssetExpected)?; + + // assert that the beneficiary is AccountKey20. + let recipient = match_expression!( + beneficiary.unpack(), + (0, [AccountKey20 { network, key }]) + if self.network_matches(network), + H160(*key) + ) + .ok_or(BeneficiaryResolutionFailed)?; + + // Make sure there are reserved assets. + if reserve_assets.len() == 0 { + return Err(NoReserveAssets) + } + + // Check the the deposit asset filter matches what was reserved. + if reserve_assets.inner().iter().any(|asset| !deposit_assets.matches(asset)) { + return Err(FilterDoesNotConsumeAllAssets) + } + + // We only support a single asset at a time. + ensure!(reserve_assets.len() == 1, TooManyAssets); + let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + + // If there was a fee specified verify it. + if let Some(fee_asset) = fee_asset { + // The fee asset must be the same as the reserve asset. + if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { + return Err(InvalidFeeAsset) + } + } + + let (asset_id, amount) = match reserve_asset { + Asset { id: AssetId(inner_location), fun: Fungible(amount) } => + Some((inner_location.clone(), *amount)), + _ => None, + } + .ok_or(AssetResolutionFailed)?; + + // transfer amount must be greater than 0. + ensure!(amount > 0, ZeroAssetTransfer); + + let token_id = TokenIdOf::convert_location(&asset_id).ok_or(InvalidAsset)?; + + let expected_asset_id = ConvertAssetId::convert(&token_id).ok_or(InvalidAsset)?; + + ensure!(asset_id == expected_asset_id, InvalidAsset); + + // Check if there is a SetTopic and skip over it if found. + let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + + Ok((Command::MintForeignToken { token_id, recipient, amount }, *topic_id)) + } } diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 111243bb45a7ee8e9e06438e1d4eaffb16573d7c..6e4fd594634039163806c5dda22598461f17d9c0 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -4,7 +4,8 @@ use snowbridge_core::{ outbound::{Fee, SendError, SendMessageFeeProvider}, AgentIdOf, }; -use xcm::v3::prelude::SendError as XcmSendError; +use sp_std::default::Default; +use xcm::prelude::SendError as XcmSendError; use super::*; @@ -57,6 +58,16 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::new(1, [GlobalConsensus(Westend)])) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + #[test] fn exporter_validate_with_unknown_network_yields_not_applicable() { let network = Ethereum { chain_id: 1337 }; @@ -65,14 +76,14 @@ fn exporter_validate_with_unknown_network_yields_not_applicable() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -84,14 +95,14 @@ fn exporter_validate_with_invalid_destination_yields_missing_argument() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -106,14 +117,14 @@ fn exporter_validate_with_x8_destination_yields_not_applicable() { ); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -125,14 +136,14 @@ fn exporter_validate_without_universal_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -144,14 +155,14 @@ fn exporter_validate_without_global_universal_location_yields_unroutable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -163,14 +174,14 @@ fn exporter_validate_without_global_bridge_location_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -183,14 +194,14 @@ fn exporter_validate_with_remote_universal_source_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -202,14 +213,14 @@ fn exporter_validate_without_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -222,14 +233,14 @@ fn exporter_validate_complex_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -242,14 +253,14 @@ fn exporter_validate_without_xcm_message_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -289,14 +300,14 @@ fn exporter_validate_with_max_target_fee_yields_unroutable() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -316,14 +327,14 @@ fn exporter_validate_with_unparsable_xcm_yields_unroutable() { let mut message: Option> = Some(vec![WithdrawAsset(fees), BuyExecution { fees: fee, weight_limit: Unlimited }].into()); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -362,14 +373,14 @@ fn exporter_validate_xcm_success_case_1() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert!(result.is_ok()); } @@ -381,6 +392,7 @@ fn exporter_deliver_with_submit_failure_yields_unroutable() { BridgedNetwork, MockErrOutboundQueue, AgentIdOf, + MockTokenIdConvert, >::deliver((hex!("deadbeef").to_vec(), XcmHash::default())); assert_eq!(result, Err(XcmSendError::Transport("other transport error"))) } @@ -410,11 +422,15 @@ fn xcm_converter_convert_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -443,11 +459,15 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -478,11 +498,15 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -513,11 +537,15 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -547,7 +575,8 @@ fn xcm_converter_convert_without_set_topic_yields_set_topic_expected() { ClearTopic, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); } @@ -564,7 +593,8 @@ fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { .into(); let message: Xcm<()> = vec![WithdrawAsset(assets)].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } @@ -595,7 +625,8 @@ fn xcm_converter_with_different_fee_asset_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -625,7 +656,8 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -636,7 +668,8 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { let message: Xcm<()> = vec![].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); @@ -668,7 +701,8 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec ClearError, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::EndOfXcmMessageExpected)); @@ -698,10 +732,11 @@ fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); } #[test] @@ -723,7 +758,8 @@ fn xcm_converter_convert_without_withdraw_asset_yields_deposit_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::DepositAssetExpected)); @@ -756,7 +792,8 @@ fn xcm_converter_convert_without_assets_yields_no_reserve_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::NoReserveAssets)); @@ -794,7 +831,8 @@ fn xcm_converter_convert_with_two_assets_yields_too_many_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::TooManyAssets)); @@ -825,7 +863,8 @@ fn xcm_converter_convert_without_consuming_filter_yields_filter_does_not_consume SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::FilterDoesNotConsumeAllAssets)); @@ -856,7 +895,8 @@ fn xcm_converter_convert_with_zero_amount_asset_yields_zero_asset_transfer() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::ZeroAssetTransfer)); @@ -886,7 +926,8 @@ fn xcm_converter_convert_non_ethereum_asset_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -919,7 +960,8 @@ fn xcm_converter_convert_non_ethereum_chain_asset_yields_asset_resolution_failed SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -952,7 +994,8 @@ fn xcm_converter_convert_non_ethereum_chain_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -989,7 +1032,8 @@ fn xcm_converter_convert_with_non_ethereum_beneficiary_yields_beneficiary_resolu SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1025,7 +1069,8 @@ fn xcm_converter_convert_with_non_ethereum_chain_beneficiary_yields_beneficiary_ SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1056,3 +1101,65 @@ fn test_describe_here() { hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into() ) } + +#[test] +fn xcm_converter_transfer_native_token_success() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + let asset_location = Location::new(1, [GlobalConsensus(Westend)]); + let token_id = TokenIdOf::convert_location(&asset_location).unwrap(); + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = + Command::MintForeignToken { recipient: beneficiary_address.into(), amount, token_id }; + let result = converter.convert(); + assert_eq!(result, Ok((expected_payload, [0; 32]))); +} + +#[test] +fn xcm_converter_transfer_native_token_with_invalid_location_will_fail() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + // Invalid asset location from a different consensus + let asset_location = Location { parents: 2, interior: [GlobalConsensus(Rococo)].into() }; + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let result = converter.convert(); + assert_eq!(result.err(), Some(XcmConverterError::InvalidAsset)); +} diff --git a/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 bd467a410d013c363913a8e4b2be8ca7b184e2dc..158dfd73b1ad8d6b67891b661bc215c290017cd0 100644 --- a/bridges/testing/README.md +++ b/bridges/testing/README.md @@ -1,31 +1,29 @@ # Bridges Tests for Local Rococo <> Westend Bridge This folder contains [zombienet](https://github.com/paritytech/zombienet/) based integration tests for both -onchain and offchain bridges code. Due to some -[technical difficulties](https://github.com/paritytech/parity-bridges-common/pull/2649#issue-1965339051), we -are using native zombienet provider, which means that you need to build some binaries locally. +onchain and offchain bridges code. -To start those tests, you need to: +Prerequisites for running the tests locally: - download latest [zombienet release](https://github.com/paritytech/zombienet/releases); - build Polkadot binary by running `cargo build -p polkadot --release --features fast-runtime` command in the -[`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; + [`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; - build Polkadot Parachain binary by running `cargo build -p polkadot-parachain-bin --release` command in the -[`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; + [`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; - ensure that you have [`node`](https://nodejs.org/en) installed. Additionally, we'll need globally installed -`polkadot/api-cli` package (use `npm install -g @polkadot/api-cli@beta` to install it); + `polkadot/api-cli` package (use `npm install -g @polkadot/api-cli@beta` to install it); - build Substrate relay by running `cargo build -p substrate-relay --release` command in the -[`parity-bridges-common`](https://github.com/paritytech/parity-bridges-common) repository clone. + [`parity-bridges-common`](https://github.com/paritytech/parity-bridges-common) repository clone; -- copy fresh `substrate-relay` binary, built in previous point, to the `~/local_bridge_testing/bin/substrate-relay`; +- copy the `substrate-relay` binary, built in the previous step, to `~/local_bridge_testing/bin/substrate-relay`; -- change the `POLKADOT_SDK_PATH` and `ZOMBIENET_BINARY_PATH` (and ensure that the nearby variables -have correct values) in the `./run-tests.sh`. +After that, any test can be run using the `run-test.sh` command. +Example: `./run-new-test.sh 0001-asset-transfer` -After that, you could run tests with the `./run-tests.sh` command. Hopefully, it'll show the +Hopefully, it'll show the "All tests have completed successfully" message in the end. Otherwise, it'll print paths to zombienet process logs, which, in turn, may be used to track locations of all spinned relay and parachain nodes. diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh index ef4a5597902fdc61caedd071f408f03a87a19ea0..54633449134b4ce85ff124d034cd6f58061409a1 100755 --- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh +++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh @@ -270,7 +270,7 @@ case "$1" in "//Alice" \ 1000 \ "ws://127.0.0.1:9910" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } }')" \ + "$(jq --null-input '{ "parents": 2, "interior": { "X1": [{ "GlobalConsensus": "Westend" }] } }')" \ "$GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT" \ 10000000000 \ true @@ -329,7 +329,7 @@ case "$1" in "//Alice" \ 1000 \ "ws://127.0.0.1:9010" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } }')" \ + "$(jq --null-input '{ "parents": 2, "interior": { "X1": [{ "GlobalConsensus": "Rococo" }] } }')" \ "$GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT" \ 10000000000 \ true diff --git a/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-active.js similarity index 94% rename from bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js rename to bridges/testing/framework/js-helpers/only-required-headers-synced-when-active.js index 8c3130e4fd960601d377dde5101520c95531cdf6..61738a21e38e726efff98ca19249dd33f91626e6 100644 --- a/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js +++ b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-active.js @@ -65,8 +65,12 @@ async function run(nodeName, networkInfo, args) { // wait until we have received + delivered messages OR until timeout await utils.pollUntil( exitAfterSeconds, - () => { return atLeastOneMessageReceived && atLeastOneMessageDelivered; }, - () => { unsubscribe(); }, + () => { + return atLeastOneMessageReceived && atLeastOneMessageDelivered; + }, + () => { + unsubscribe(); + }, () => { if (!atLeastOneMessageReceived) { throw new Error("No messages received from bridged chain"); @@ -78,4 +82,4 @@ async function run(nodeName, networkInfo, args) { ); } -module.exports = { run } +module.exports = {run} diff --git a/bridges/testing/framework/js-helpers/wrapped-assets-balance.js b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js index 27287118547f702b3e94eb635f9e3855d1cab535..7b343ed97a88f36f19ab7fe34e6d515d521f2b35 100644 --- a/bridges/testing/framework/js-helpers/wrapped-assets-balance.js +++ b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js @@ -8,7 +8,7 @@ async function run(nodeName, networkInfo, args) { const bridgedNetworkName = args[2]; while (true) { const foreignAssetAccount = await api.query.foreignAssets.account( - { parents: 2, interior: { X1: { GlobalConsensus: bridgedNetworkName } } }, + { parents: 2, interior: { X1: [{ GlobalConsensus: bridgedNetworkName }] } }, accountAddress ); if (foreignAssetAccount.isSome) { diff --git a/bridges/testing/run-new-test.sh b/bridges/testing/run-test.sh similarity index 100% rename from bridges/testing/run-new-test.sh rename to bridges/testing/run-test.sh diff --git a/bridges/testing/run-tests.sh b/bridges/testing/run-tests.sh deleted file mode 100755 index fd12b57f53349a0a449af7103c05341d3c94ceb9..0000000000000000000000000000000000000000 --- a/bridges/testing/run-tests.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash -set -x -shopt -s nullglob - -trap "trap - SIGINT SIGTERM EXIT && killall -q -9 substrate-relay && kill -- -$$" SIGINT SIGTERM EXIT - -# run tests in range [TESTS_BEGIN; TESTS_END) -TESTS_BEGIN=1 -TESTS_END=1000 -# whether to use paths for zombienet+bridges tests container or for local testing -ZOMBIENET_DOCKER_PATHS=0 -while [ $# -ne 0 ] -do - arg="$1" - case "$arg" in - --docker) - ZOMBIENET_DOCKER_PATHS=1 - ;; - --test) - shift - TESTS_BEGIN="$1" - TESTS_END="$1" - ;; - esac - shift -done - -# assuming that we'll be using native provide && all processes will be executing locally -# (we need absolute paths here, because they're used when scripts are called by zombienet from tmp folders) -export POLKADOT_SDK_PATH=`realpath $(dirname "$0")/../..` -export BRIDGE_TESTS_FOLDER=$POLKADOT_SDK_PATH/bridges/testing/tests - -# set path to binaries -if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then - export POLKADOT_BINARY=/usr/local/bin/polkadot - export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain - - export SUBSTRATE_RELAY_BINARY=/usr/local/bin/substrate-relay - export ZOMBIENET_BINARY_PATH=/usr/local/bin/zombie -else - export POLKADOT_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot - export POLKADOT_PARACHAIN_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot-parachain - - export SUBSTRATE_RELAY_BINARY=~/local_bridge_testing/bin/substrate-relay - export ZOMBIENET_BINARY_PATH=~/local_bridge_testing/bin/zombienet-linux -fi - -# check if `wait` supports -p flag -if [ `printf "$BASH_VERSION\n5.1" | sort -V | head -n 1` = "5.1" ]; then IS_BASH_5_1=1; else IS_BASH_5_1=0; fi - -# bridge configuration -export LANE_ID="00000002" - -# tests configuration -ALL_TESTS_FOLDER=`mktemp -d /tmp/bridges-zombienet-tests.XXXXX` - -function start_coproc() { - local command=$1 - local name=$2 - local logname=`basename $name` - local coproc_log=`mktemp -p $TEST_FOLDER $logname.XXXXX` - coproc COPROC { - # otherwise zombienet uses some hardcoded paths - unset RUN_IN_CONTAINER - unset ZOMBIENET_IMAGE - - $command >$coproc_log 2>&1 - } - TEST_COPROCS[$COPROC_PID, 0]=$name - TEST_COPROCS[$COPROC_PID, 1]=$coproc_log - echo "Spawned $name coprocess. StdOut + StdErr: $coproc_log" - - return $COPROC_PID -} - -# execute every test from tests folder -TEST_INDEX=$TESTS_BEGIN -while true -do - declare -A TEST_COPROCS - TEST_COPROCS_COUNT=0 - TEST_PREFIX=$(printf "%04d" $TEST_INDEX) - - # it'll be used by the `sync-exit.sh` script - export TEST_FOLDER=`mktemp -d -p $ALL_TESTS_FOLDER test-$TEST_PREFIX.XXXXX` - - # check if there are no more tests - zndsl_files=($BRIDGE_TESTS_FOLDER/$TEST_PREFIX-*.zndsl) - if [ ${#zndsl_files[@]} -eq 0 ]; then - break - fi - - # start tests - for zndsl_file in "${zndsl_files[@]}"; do - start_coproc "$ZOMBIENET_BINARY_PATH --provider native test $zndsl_file" "$zndsl_file" - echo -n "1">>$TEST_FOLDER/exit-sync - ((TEST_COPROCS_COUNT++)) - done - # wait until all tests are completed - for n in `seq 1 $TEST_COPROCS_COUNT`; do - if [ "$IS_BASH_5_1" -eq 1 ]; then - wait -n -p COPROC_PID - exit_code=$? - coproc_name=${TEST_COPROCS[$COPROC_PID, 0]} - coproc_log=${TEST_COPROCS[$COPROC_PID, 1]} - coproc_stdout=$(cat $coproc_log) - else - wait -n - exit_code=$? - coproc_name="" - coproc_stdout="" - fi - echo "Process $coproc_name has finished with exit code: $exit_code" - - # if exit code is not zero, exit - if [ $exit_code -ne 0 ]; then - echo "=====================================================================" - echo "=== Shutting down. Log of failed process below ===" - echo "=====================================================================" - echo "$coproc_stdout" - - exit 1 - fi - done - - # proceed to next index - ((TEST_INDEX++)) - if [ "$TEST_INDEX" -ge "$TESTS_END" ]; then - break - fi - - # kill relay here - it is started manually by tests - killall substrate-relay -done - -echo "=====================================================================" -echo "=== All tests have completed successfully ===" -echo "=====================================================================" diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl deleted file mode 100644 index 07b91481dc7cf995b913a9bf84edd3728982eaae..0000000000000000000000000000000000000000 --- a/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl +++ /dev/null @@ -1,26 +0,0 @@ -Description: While relayer is active, we only sync mandatory and required Rococo (and Rococo BH) headers to Westend BH. -Network: ../environments/rococo-westend/bridge_hub_westend_local_network.toml -Creds: config - -# step 1: initialize Westend AH -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-asset-hub-westend-local" within 60 seconds - -# step 2: initialize Westend bridge hub -bridge-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-westend-local" within 60 seconds - -# step 3: ensure that initialization has completed -asset-hub-westend-collator1: js-script ../js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds - -# step 4: send message from Westend to Rococo -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 60 seconds - -# step 5: start relayer -# (we are starting it after sending the message to be sure that relayer won't relay messages before our js script -# will be started at step 6) -# (it is started by sibling 0003-required-headers-synced-while-active-westend-to-rococo.zndsl) - -# step 6: ensure that relayer won't sync any extra headers while delivering messages and confirmations -bridge-hub-westend-collator1: js-script ../js-helpers/only-required-headers-synced-when-active.js with "500,rococo-at-westend" within 600 seconds - -# wait until other network test has completed OR exit with an error too -asset-hub-westend-collator1: run ../scripts/sync-exit.sh within 600 seconds diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl deleted file mode 100644 index a6b11fc24052aadf562bc34704aeda9ee115eccf..0000000000000000000000000000000000000000 --- a/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl +++ /dev/null @@ -1,26 +0,0 @@ -Description: While relayer is active, we only sync mandatory and required Westend (and Westend BH) headers to Rococo BH. -Network: ../environments/rococo-westend/bridge_hub_rococo_local_network.toml -Creds: config - -# step 1: initialize Rococo AH -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-asset-hub-rococo-local" within 60 seconds - -# step 2: initialize Rococo bridge hub -bridge-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-rococo-local" within 60 seconds - -# step 3: ensure that initialization has completed -asset-hub-rococo-collator1: js-script ../js-helpers/wait-hrmp-channel-opened.js with "1013" within 600 seconds - -# step 4: send message from Rococo to Westend -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 60 seconds - -# step 5: start relayer -# (we are starting it after sending the message to be sure that relayer won't relay messages before our js script -# will be started at step 6) -bridge-hub-rococo-collator1: run ../scripts/start-relayer.sh within 60 seconds - -# step 6: ensure that relayer won't sync any extra headers while delivering messages and confirmations -bridge-hub-rococo-collator1: js-script ../js-helpers/only-required-headers-synced-when-active.js with "500,westend-at-rococo" within 600 seconds - -# wait until other network test has completed OR exit with an error too -asset-hub-rococo-collator1: run ../scripts/sync-exit.sh within 600 seconds diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..897b79eeff23b19a3a32efd8cab68209c8bb254f --- /dev/null +++ b/bridges/testing/tests/0003-required-headers-synced-while-active/rococo-to-westend.zndsl @@ -0,0 +1,7 @@ +Description: While relayer is active, we only sync mandatory and required Rococo (and Rococo BH) headers to Westend BH. +Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml +Creds: config + +# ensure that relayer won't sync any extra headers while delivering messages and confirmations +bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-required-headers-synced-when-active.js with "500,rococo-at-westend" within 600 seconds + diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active/run.sh b/bridges/testing/tests/0003-required-headers-synced-while-active/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..8fad38f220520629f27d1c1bfca53d2dbe8187bc --- /dev/null +++ b/bridges/testing/tests/0003-required-headers-synced-while-active/run.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e + +# TODO: This test doesn't work. It was added at a time when we couldn't run it because we didn't have the scafolding. +# It needs to be fixed. For the moment we keep it in the repo as it is since the idea has value. +# But we don't run it in the CI. + +source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" +source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" + +export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` + +logs_dir=$TEST_DIR/logs + +$ENV_PATH/spawn.sh --init & +env_pid=$! + +ensure_process_file $env_pid $TEST_DIR/rococo.env 600 +rococo_dir=`cat $TEST_DIR/rococo.env` +echo + +ensure_process_file $env_pid $TEST_DIR/westend.env 300 +westend_dir=`cat $TEST_DIR/westend.env` +echo + +echo "Sending message from Rococo to Westend" +$ENV_PATH/helper.sh auto-log reserve-transfer-assets-from-asset-hub-rococo-local 5000000000000 +echo + +echo "Sending message from Westend to Rococo" +$ENV_PATH/helper.sh auto-log reserve-transfer-assets-from-asset-hub-westend-local 5000000000000 +echo + + +# Start the relayer with a 30s delay +# We want to be sure that the messages won't be relayed before starting the js script in `rococo-to-westend.zndsl` +start_relayer_log=$logs_dir/start_relayer.log +echo -e "The rococo-westend relayer will be started in 30s. Logs will be available at: $start_relayer_log\n" +(sleep 30 && $ENV_PATH/start_relayer.sh \ + $rococo_dir $westend_dir finality_relayer_pid parachains_relayer_pid messages_relayer_pid > $start_relayer_log)& + +run_zndsl ${BASH_SOURCE%/*}/rococo-to-westend.zndsl $westend_dir + diff --git a/cumulus/client/cli/src/lib.rs b/cumulus/client/cli/src/lib.rs index a7b2eb19de88a5c585ec3f6dfe5ad46ef0399b88..b08ad75c430d773bac3e16c746363b2d6cdae27e 100644 --- a/cumulus/client/cli/src/lib.rs +++ b/cumulus/client/cli/src/lib.rs @@ -21,13 +21,13 @@ use std::{ fs, io::{self, Write}, - net::SocketAddr, path::PathBuf, sync::Arc, }; use codec::Encode; use sc_chain_spec::ChainSpec; +use sc_cli::RpcEndpoint; use sc_client_api::HeaderBackend; use sc_service::{ config::{PrometheusConfig, RpcBatchRequestConfig, TelemetryEndpoints}, @@ -96,7 +96,7 @@ impl PurgeChainCmd { Some('y') | Some('Y') => {}, _ => { println!("Aborted"); - return Ok(()) + return Ok(()); }, } } @@ -423,7 +423,7 @@ impl sc_cli::CliConfiguration for NormalizedRunCmd { self.base.rpc_cors(is_dev) } - fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result> { + fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result>> { self.base.rpc_addr(default_listen_port) } @@ -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/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml index 01e07cb395a955dfe3016aef3c8bd3ac5e2be7c9..47e2d8572c3f8e589997ca9066e63c6896e721f9 100644 --- a/cumulus/client/consensus/aura/Cargo.toml +++ b/cumulus/client/consensus/aura/Cargo.toml @@ -54,3 +54,7 @@ polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-overseer = { workspace = true, default-features = true } + +[features] +# Allows collator to use full PoV size for block building +full-pov-size = [] diff --git a/cumulus/client/consensus/aura/src/collators/basic.rs b/cumulus/client/consensus/aura/src/collators/basic.rs index 4efd50a04ec6ec654ce7b32ac17eb07d12df3d6c..d843483b79fa0fd7d3ad577e191b4836c6a5dceb 100644 --- a/cumulus/client/consensus/aura/src/collators/basic.rs +++ b/cumulus/client/consensus/aura/src/collators/basic.rs @@ -138,6 +138,7 @@ where }; let mut last_processed_slot = 0; + let mut last_relay_chain_block = Default::default(); while let Some(request) = collation_requests.next().await { macro_rules! reject_with_error { @@ -215,11 +216,13 @@ where // // Most parachains currently run with 12 seconds slots and thus, they would try to // produce multiple blocks per slot which very likely would fail on chain. Thus, we have - // this "hack" to only produce on block per slot. + // this "hack" to only produce one block per slot per relay chain fork. // // With https://github.com/paritytech/polkadot-sdk/issues/3168 this implementation will be // obsolete and also the underlying issue will be fixed. - if last_processed_slot >= *claim.slot() { + if last_processed_slot >= *claim.slot() && + last_relay_chain_block < *relay_parent_header.number() + { continue } @@ -234,6 +237,16 @@ where .await ); + let allowed_pov_size = if cfg!(feature = "full-pov-size") { + validation_data.max_pov_size + } else { + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + validation_data.max_pov_size / 2 + } as usize; + let maybe_collation = try_request!( collator .collate( @@ -242,11 +255,7 @@ where None, (parachain_inherent_data, other_inherent_data), params.authoring_duration, - // Set the block limit to 50% of the maximum PoV size. - // - // TODO: If we got benchmarking that includes the proof size, - // we should be able to use the maximum pov size. - (validation_data.max_pov_size / 2) as usize, + allowed_pov_size, ) .await ); @@ -261,6 +270,7 @@ where } last_processed_slot = *claim.slot(); + last_relay_chain_block = *relay_parent_header.number(); } } } diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index 02d60538a7323b86948c4378c7d0d4cd5c634116..0be1e0a23ca5bbd70c00c86443a5ebf4ab90c833 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -412,6 +412,16 @@ where ) .await; + let allowed_pov_size = if cfg!(feature = "full-pov-size") { + validation_data.max_pov_size + } else { + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + validation_data.max_pov_size / 2 + } as usize; + match collator .collate( &parent_header, @@ -419,11 +429,7 @@ where None, (parachain_inherent_data, other_inherent_data), params.authoring_duration, - // Set the block limit to 50% of the maximum PoV size. - // - // TODO: If we got benchmarking that includes the proof size, - // we should be able to use the maximum pov size. - (validation_data.max_pov_size / 2) as usize, + allowed_pov_size, ) .await { diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 1fbc0689da862999367a0c4a9bda59ed3d6525af..b70cfe3841b78e439c2a936a37222da89ea9c5de 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -350,6 +350,16 @@ where ) .await; + let allowed_pov_size = if cfg!(feature = "full-pov-size") { + validation_data.max_pov_size + } else { + // Set the block limit to 50% of the maximum PoV size. + // + // TODO: If we got benchmarking that includes the proof size, + // we should be able to use the maximum pov size. + validation_data.max_pov_size / 2 + } as usize; + let Ok(Some(candidate)) = collator .build_block_and_import( &parent_header, @@ -357,11 +367,7 @@ where None, (parachain_inherent_data, other_inherent_data), authoring_duration, - // Set the block limit to 50% of the maximum PoV size. - // - // TODO: If we got benchmarking that includes the proof size, - // we should be able to use the maximum pov size. - (validation_data.max_pov_size / 2) as usize, + allowed_pov_size, ) .await else { diff --git a/cumulus/client/consensus/common/src/lib.rs b/cumulus/client/consensus/common/src/lib.rs index e12750dcc553f9983f3432c9bf2ad156318a4cc8..6766c2409c385f2cada12ea5da5a81c39fe20205 100644 --- a/cumulus/client/consensus/common/src/lib.rs +++ b/cumulus/client/consensus/common/src/lib.rs @@ -185,7 +185,7 @@ where } async fn import_block( - &mut self, + &self, mut params: sc_consensus::BlockImportParams, ) -> Result { // Blocks are stored within the backend by using POST hash. diff --git a/cumulus/client/consensus/common/src/parachain_consensus.rs b/cumulus/client/consensus/common/src/parachain_consensus.rs index 944917673b119732a587adf3596ae59b829e30b5..861354ed63c30783dc04cb20dd9195caf94096b9 100644 --- a/cumulus/client/consensus/common/src/parachain_consensus.rs +++ b/cumulus/client/consensus/common/src/parachain_consensus.rs @@ -433,11 +433,8 @@ async fn handle_new_best_parachain_head( } } -async fn import_block_as_new_best( - hash: Block::Hash, - header: Block::Header, - mut parachain: &P, -) where +async fn import_block_as_new_best(hash: Block::Hash, header: Block::Header, parachain: &P) +where Block: BlockT, P: UsageProvider + Send + Sync + BlockBackend, for<'a> &'a P: BlockImport, diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 284fa39ed1e704dd7594861c6f0c3264df75acfb..06f90330d4745525cb20f6c3950b7d011a71f486 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -321,7 +321,7 @@ fn build_block( } async fn import_block>( - importer: &mut I, + importer: &I, block: Block, origin: BlockOrigin, import_as_best: bool, @@ -568,7 +568,7 @@ fn follow_finalized_does_not_stop_on_unknown_block() { fn follow_new_best_sets_best_after_it_is_imported() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::default().build()); + let client = Arc::new(TestClientBuilder::default().build()); let block = build_and_import_block(client.clone(), false); diff --git a/cumulus/client/consensus/proposer/Cargo.toml b/cumulus/client/consensus/proposer/Cargo.toml index ce91d48bf589a88e5eb0f81507e73da3d28a6b81..bb760ae03f4df0dfae60fce06ed49e223763a8e6 100644 --- a/cumulus/client/consensus/proposer/Cargo.toml +++ b/cumulus/client/consensus/proposer/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" workspace = true [dependencies] -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } async-trait = { workspace = true } thiserror = { workspace = true } diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index 18d121c41d16823b0a8763132f51f9b949c41c6f..81c2d9f24f2813f580a4c96723ca5aac80813c60 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -323,7 +323,7 @@ impl RelayChainInterface for DummyRelayChainInterface { impl_version: 0, apis: Cow::Owned(apis), transaction_version: 5, - state_version: 1, + system_version: 1, }) } } @@ -612,7 +612,7 @@ fn relay_parent_not_imported_when_block_announce_is_processed() { block_on(async move { let (mut validator, api) = make_validator_and_api(); - let mut client = api.relay_client.clone(); + let client = api.relay_client.clone(); let block = client.init_polkadot_block_builder().build().expect("Build new block").block; let (signal, header) = make_gossip_message_and_header(api, block.hash(), 0).await; diff --git a/cumulus/client/pov-recovery/Cargo.toml b/cumulus/client/pov-recovery/Cargo.toml index a95b24bc2933aa2d8529e70ee2b37759625b1757..3127dd26fcaa67d137ddbc867f30a76ff3faeedb 100644 --- a/cumulus/client/pov-recovery/Cargo.toml +++ b/cumulus/client/pov-recovery/Cargo.toml @@ -2,7 +2,7 @@ name = "cumulus-client-pov-recovery" version = "0.7.0" authors.workspace = true -description = "Cumulus-specific networking protocol" +description = "Parachain PoV recovery" edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/cumulus/client/pov-recovery/src/tests.rs b/cumulus/client/pov-recovery/src/tests.rs index 6f274ed18b6bc7871607cae6c5183d6d861e5117..f300bdc5f2ba38fd4cbce653e9ff8e77257b7c7e 100644 --- a/cumulus/client/pov-recovery/src/tests.rs +++ b/cumulus/client/pov-recovery/src/tests.rs @@ -329,7 +329,7 @@ impl RelayChainInterface for Relaychain { impl_version: 0, apis: Cow::Owned(apis), transaction_version: 5, - state_version: 1, + system_version: 1, }) } diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index 38ba84748c1e38b8dd3c1fcdd2f8d6351299b18c..629fa728be372b915af2e51248706700ac6909c9 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -137,7 +137,11 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidate_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidate_pending_availability(hash, para_id)? + .map(|receipt| receipt.into())) } async fn session_index_for_child(&self, hash: PHash) -> RelayChainResult { @@ -260,7 +264,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { &self, relay_parent: PHash, ) -> RelayChainResult>> { - Ok(self.full_client.runtime_api().availability_cores(relay_parent)?) + Ok(self + .full_client + .runtime_api() + .availability_cores(relay_parent)? + .into_iter() + .map(|core_state| core_state.into()) + .collect::>()) } async fn candidates_pending_availability( @@ -268,7 +278,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidates_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidates_pending_availability(hash, para_id)? + .into_iter() + .map(|receipt| receipt.into()) + .collect::>()) } } @@ -423,7 +439,7 @@ mod tests { #[test] fn returns_directly_for_available_block() { - let (mut client, block, relay_chain_interface) = build_client_backend_and_block(); + let (client, block, relay_chain_interface) = build_client_backend_and_block(); let hash = block.hash(); block_on(client.import(BlockOrigin::Own, block)).expect("Imports the block"); @@ -439,7 +455,7 @@ mod tests { #[test] fn resolve_after_block_import_notification_was_received() { - let (mut client, block, relay_chain_interface) = build_client_backend_and_block(); + let (client, block, relay_chain_interface) = build_client_backend_and_block(); let hash = block.hash(); block_on(async move { @@ -468,7 +484,7 @@ mod tests { #[test] fn do_not_resolve_after_different_block_import_notification_was_received() { - let (mut client, block, relay_chain_interface) = build_client_backend_and_block(); + let (client, block, relay_chain_interface) = build_client_backend_and_block(); let hash = block.hash(); let ext = construct_transfer_extrinsic( diff --git a/cumulus/client/relay-chain-minimal-node/src/lib.rs b/cumulus/client/relay-chain-minimal-node/src/lib.rs index 732a242e72957214ce8161d5f785d20b8c2484b3..e65a78f16d79bf0f76c18af771a72c29308a6c4d 100644 --- a/cumulus/client/relay-chain-minimal-node/src/lib.rs +++ b/cumulus/client/relay-chain-minimal-node/src/lib.rs @@ -175,7 +175,7 @@ async fn new_minimal_relay_chain, ) -> Result { - let role = config.role.clone(); + let role = config.role; let mut net_config = sc_network::config::FullNetworkConfiguration::<_, _, Network>::new( &config.network, config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()), diff --git a/cumulus/client/relay-chain-minimal-node/src/network.rs b/cumulus/client/relay-chain-minimal-node/src/network.rs index 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..c2deddc5341de0d235e9949140762db39600226d 100644 --- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml +++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml @@ -9,6 +9,9 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [lints] workspace = true +[dev-dependencies] +portpicker = "0.1.1" + [dependencies] polkadot-overseer = { workspace = true, default-features = true } diff --git a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs index 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/service/src/lib.rs b/cumulus/client/service/src/lib.rs index 9b5f0bec53875c98dc563b86ecea1d4864a554da..7f656aabca7ac78a9bc4c35137ec56ed535fe362 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -28,10 +28,7 @@ use cumulus_relay_chain_interface::{RelayChainInterface, RelayChainResult}; use cumulus_relay_chain_minimal_node::{ build_minimal_relay_chain_node_light_client, build_minimal_relay_chain_node_with_rpc, }; -use futures::{ - channel::{mpsc, oneshot}, - FutureExt, StreamExt, -}; +use futures::{channel::mpsc, StreamExt}; use polkadot_primitives::{CollatorPair, OccupiedCoreAssumption}; use sc_client_api::{ Backend as BackendT, BlockBackend, BlockchainEvents, Finalizer, ProofProvider, UsageProvider, @@ -43,7 +40,7 @@ use sc_consensus::{ use sc_network::{config::SyncMode, service::traits::NetworkService, NetworkBackend}; use sc_network_sync::SyncingService; use sc_network_transactions::TransactionsHandlerController; -use sc_service::{Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, WarpSyncParams}; +use sc_service::{Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, WarpSyncConfig}; use sc_telemetry::{log, TelemetryWorkerHandle}; use sc_utils::mpsc::TracingUnboundedSender; use sp_api::ProvideRuntimeApi; @@ -467,12 +464,19 @@ where { let warp_sync_params = match parachain_config.network.sync_mode { SyncMode::Warp => { - let target_block = warp_sync_get::( - para_id, - relay_chain_interface.clone(), - spawn_handle.clone(), - ); - Some(WarpSyncParams::WaitForTarget(target_block)) + log::debug!(target: LOG_TARGET_SYNC, "waiting for announce block..."); + + let target_block = + wait_for_finalized_para_head::(para_id, relay_chain_interface.clone()) + .await + .inspect_err(|e| { + log::error!( + target: LOG_TARGET_SYNC, + "Unable to determine parachain target block {:?}", + e + ); + })?; + Some(WarpSyncConfig::WithTarget(target_block)) }, _ => None, }; @@ -500,67 +504,37 @@ where spawn_handle, import_queue, block_announce_validator_builder: Some(Box::new(move |_| block_announce_validator)), - warp_sync_params, + warp_sync_config: warp_sync_params, block_relay: None, metrics, }) } -/// Creates a new background task to wait for the relay chain to sync up and retrieve the parachain -/// header -fn warp_sync_get( - para_id: ParaId, - relay_chain_interface: RCInterface, - spawner: SpawnTaskHandle, -) -> oneshot::Receiver<::Header> -where - B: BlockT + 'static, - RCInterface: RelayChainInterface + 'static, -{ - let (sender, receiver) = oneshot::channel::(); - spawner.spawn( - "cumulus-parachain-wait-for-target-block", - None, - async move { - log::debug!( - target: LOG_TARGET_SYNC, - "waiting for announce block in a background task...", - ); - - let _ = wait_for_finalized_para_head::(sender, para_id, relay_chain_interface) - .await - .map_err(|e| { - log::error!( - target: LOG_TARGET_SYNC, - "Unable to determine parachain target block {:?}", - e - ) - }); - } - .boxed(), - ); - - receiver -} - /// Waits for the relay chain to have finished syncing and then gets the parachain header that /// corresponds to the last finalized relay chain block. async fn wait_for_finalized_para_head( - sender: oneshot::Sender<::Header>, para_id: ParaId, relay_chain_interface: RCInterface, -) -> Result<(), Box> +) -> sc_service::error::Result<::Header> where B: BlockT + 'static, RCInterface: RelayChainInterface + Send + 'static, { - let mut imported_blocks = relay_chain_interface.import_notification_stream().await?.fuse(); - while imported_blocks.next().await.is_some() { - let is_syncing = relay_chain_interface.is_major_syncing().await.map_err(|e| { - Box::::from(format!( - "Unable to determine sync status. {e}" + let mut imported_blocks = relay_chain_interface + .import_notification_stream() + .await + .map_err(|error| { + sc_service::Error::Other(format!( + "Relay chain import notification stream error when waiting for parachain head: \ + {error}" )) - })?; + })? + .fuse(); + while imported_blocks.next().await.is_some() { + let is_syncing = relay_chain_interface + .is_major_syncing() + .await + .map_err(|e| format!("Unable to determine sync status: {e}"))?; if !is_syncing { let relay_chain_best_hash = relay_chain_interface @@ -586,8 +560,7 @@ where finalized_header.number(), finalized_header.hash() ); - let _ = sender.send(finalized_header); - return Ok(()) + return Ok(finalized_header) } } diff --git a/cumulus/pallets/aura-ext/src/lib.rs b/cumulus/pallets/aura-ext/src/lib.rs index 4c9e61458a87c456766880351da7ecbcce30a92c..dc854eb820184cbc79c5c150b1ce008c0d1955ce 100644 --- a/cumulus/pallets/aura-ext/src/lib.rs +++ b/cumulus/pallets/aura-ext/src/lib.rs @@ -16,7 +16,7 @@ //! Cumulus extension pallet for AuRa //! -//! This pallets extends the Substrate AuRa pallet to make it compatible with parachains. It +//! This pallet extends the Substrate AuRa pallet to make it compatible with parachains. It //! provides the [`Pallet`], the [`Config`] and the [`GenesisConfig`]. //! //! It is also required that the parachain runtime uses the provided [`BlockExecutor`] to properly diff --git a/cumulus/pallets/collator-selection/src/lib.rs b/cumulus/pallets/collator-selection/src/lib.rs index 17dc1a552c2de40455933fe8a72d05caa4317a32..9d7e62af3c68f496de34e4f0c02ffdc5a21cf978 100644 --- a/cumulus/pallets/collator-selection/src/lib.rs +++ b/cumulus/pallets/collator-selection/src/lib.rs @@ -972,7 +972,7 @@ pub mod pallet { let result = Self::assemble_collators(); frame_system::Pallet::::register_extra_weight_unchecked( - T::WeightInfo::new_session(candidates_len_before, removed), + T::WeightInfo::new_session(removed, candidates_len_before), DispatchClass::Mandatory, ); Some(result) diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 9e0a68d09a14a685da6a66facdd0ac2822d4c6d4..882dcb68fbbebecf3d798b8d1dc5662de5a43919 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -53,9 +53,6 @@ use polkadot_runtime_parachains::FeeTracker; use scale_info::TypeInfo; use sp_runtime::{ traits::{Block as BlockT, BlockNumberProvider, Hash}, - transaction_validity::{ - InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction, - }, BoundedSlice, FixedU128, RuntimeDebug, Saturating, }; use xcm::{latest::XcmHash, VersionedLocation, VersionedXcm}; @@ -193,7 +190,7 @@ pub mod ump_constants { pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - use frame_system::{pallet_prelude::*, WeightInfo as SystemWeightInfo}; + use frame_system::pallet_prelude::*; #[pallet::pallet] #[pallet::storage_version(migration::STORAGE_VERSION)] @@ -653,52 +650,8 @@ pub mod pallet { Ok(()) } - /// Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied - /// later. - /// - /// The `check_version` parameter sets a boolean flag for whether or not the runtime's spec - /// version and name should be verified on upgrade. Since the authorization only has a hash, - /// it cannot actually perform the verification. - /// - /// This call requires Root origin. - #[pallet::call_index(2)] - #[pallet::weight(::SystemWeightInfo::authorize_upgrade())] - #[allow(deprecated)] - #[deprecated( - note = "To be removed after June 2024. Migrate to `frame_system::authorize_upgrade`." - )] - pub fn authorize_upgrade( - origin: OriginFor, - code_hash: T::Hash, - check_version: bool, - ) -> DispatchResult { - ensure_root(origin)?; - frame_system::Pallet::::do_authorize_upgrade(code_hash, check_version); - Ok(()) - } - - /// Provide the preimage (runtime binary) `code` for an upgrade that has been authorized. - /// - /// If the authorization required a version check, this call will ensure the spec name - /// remains unchanged and that the spec version has increased. - /// - /// Note that this function will not apply the new `code`, but only attempt to schedule the - /// upgrade with the Relay Chain. - /// - /// All origins are allowed. - #[pallet::call_index(3)] - #[pallet::weight(::SystemWeightInfo::apply_authorized_upgrade())] - #[allow(deprecated)] - #[deprecated( - note = "To be removed after June 2024. Migrate to `frame_system::apply_authorized_upgrade`." - )] - pub fn enact_authorized_upgrade( - _: OriginFor, - code: Vec, - ) -> DispatchResultWithPostInfo { - let post = frame_system::Pallet::::do_apply_authorize_upgrade(code)?; - Ok(post) - } + // WARNING: call indices 2 and 3 were used in a former version of this pallet. Using them + // again will require to bump the transaction version of runtimes using this pallet. } #[pallet::event] @@ -951,30 +904,6 @@ pub mod pallet { sp_io::storage::set(b":c", &[]); } } - - #[pallet::validate_unsigned] - impl sp_runtime::traits::ValidateUnsigned for Pallet { - type Call = Call; - - fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { - if let Call::enact_authorized_upgrade { ref code } = call { - if let Ok(hash) = frame_system::Pallet::::validate_authorized_upgrade(&code[..]) - { - return Ok(ValidTransaction { - priority: 100, - requires: Vec::new(), - provides: vec![hash.as_ref().to_vec()], - longevity: TransactionLongevity::max_value(), - propagate: true, - }) - } - } - if let Call::set_validation_data { .. } = call { - return Ok(Default::default()) - } - Err(InvalidTransaction::Call.into()) - } - } } impl Pallet { @@ -1611,6 +1540,10 @@ impl UpwardMessageSender for Pallet { } impl InspectMessageQueues for Pallet { + fn clear_messages() { + PendingUpwardMessages::::kill(); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index 7bea72224b8ba36c6155597f4b957a2ed3a8d822..247de3a29b69af795d8bbf175bb9ca5127118d57 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -49,9 +49,9 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - ParachainSystem: parachain_system::{Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned}, - MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event}, + System: frame_system, + ParachainSystem: parachain_system, + MessageQueue: pallet_message_queue, } ); @@ -64,7 +64,7 @@ parameter_types! { impl_version: 1, apis: sp_version::create_apis_vec!([]), transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const ParachainId: ParaId = ParaId::new(200); pub const ReservedXcmpWeight: Weight = Weight::zero(); diff --git a/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs index 51c6e83c113191250c3c2ebe60e2dfad1263d682..548231966e420f2c8a00642042657257e20f085d 100755 --- a/cumulus/pallets/parachain-system/src/tests.rs +++ b/cumulus/pallets/parachain-system/src/tests.rs @@ -1127,10 +1127,8 @@ fn upgrade_version_checks_should_work() { let new_code = vec![1, 2, 3, 4]; let new_code_hash = H256(sp_crypto_hashing::blake2_256(&new_code)); - #[allow(deprecated)] - let _authorize = ParachainSystem::authorize_upgrade(RawOrigin::Root.into(), new_code_hash, true); - #[allow(deprecated)] - let res = ParachainSystem::enact_authorized_upgrade(RawOrigin::None.into(), new_code); + let _authorize = System::authorize_upgrade(RawOrigin::Root.into(), new_code_hash); + let res = System::apply_authorized_upgrade(RawOrigin::None.into(), new_code); assert_eq!(expected.map_err(DispatchErrorWithPostInfo::from), res); }); diff --git a/cumulus/pallets/parachain-system/src/validate_block/tests.rs b/cumulus/pallets/parachain-system/src/validate_block/tests.rs index 1527492f57848b30a52984ba1156eb5b877268a8..a44d17507810099004113ce3ca30739c8ee3ddce 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/tests.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/tests.rs @@ -312,7 +312,7 @@ fn validation_params_and_memory_optimized_validation_params_encode_and_decode() fn validate_block_works_with_child_tries() { sp_tracing::try_init_simple(); - let (mut client, parent_head) = create_test_client(); + let (client, parent_head) = create_test_client(); let TestBlockData { block, .. } = build_block_with_witness( &client, vec![generate_extrinsic(&client, Charlie, TestPalletCall::read_and_write_child_tries {})], diff --git a/cumulus/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..732ee94f3e10efea146060554b2ed7468e52f6fa 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -1008,6 +1008,11 @@ impl SendXcm for Pallet { } impl InspectMessageQueues for Pallet { + fn clear_messages() { + // Best effort. + let _ = OutboundXcmpMessages::::clear(u32::MAX, None); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 7fb96de7a4eaae7e7f78fae66179a26a3a6c5866..348939de1f14613d3c1bb54b1d63d9c7bbf4cec6 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParachainSystem: cumulus_pallet_parachain_system::{ - Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned, + Pallet, Call, Config, Storage, Inherent, Event, }, XcmpQueue: xcmp_queue::{Pallet, Call, Storage, Event}, } diff --git a/cumulus/parachains/chain-specs/coretime-polkadot.json b/cumulus/parachains/chain-specs/coretime-polkadot.json new file mode 100644 index 0000000000000000000000000000000000000000..806231db764671eeeddc50219962108856acd492 --- /dev/null +++ b/cumulus/parachains/chain-specs/coretime-polkadot.json @@ -0,0 +1,94 @@ +{ + "name": "Polkadot Coretime", + "id": "coretime-polkadot", + "chainType": "Live", + "bootNodes": [ + "/dns/polkadot-coretime-connect-a-0.polkadot.io/tcp/30334/p2p/12D3KooWKjnixAHbKMsPTJwGx8SrBeGEJLHA8KmKcEDYMp3YmWgR", + "/dns/polkadot-coretime-connect-a-1.polkadot.io/tcp/30334/p2p/12D3KooWQ7B7p4DFv1jWqaKfhrZBcMmi5g8bWFnmskguLaGEmT6n", + "/dns/polkadot-coretime-connect-a-0.polkadot.io/tcp/443/wss/p2p/12D3KooWKjnixAHbKMsPTJwGx8SrBeGEJLHA8KmKcEDYMp3YmWgR", + "/dns/polkadot-coretime-connect-a-1.polkadot.io/tcp/443/wss/p2p/12D3KooWQ7B7p4DFv1jWqaKfhrZBcMmi5g8bWFnmskguLaGEmT6n", + "/dns4/coretime-polkadot.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWFJ2yBTKFKYwgKUjfY3F7XfaxHV8hY6fbJu5oMkpP7wZ9", + "/dns4/coretime-polkadot.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWCy5pToLafcQzPHn5kadxAftmF6Eh8ZJGPXhSeXSUDfjv", + "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9", + "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/30361/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "ss58Format": 0, + "tokenDecimals": 10, + "tokenSymbol": "DOT" + }, + "relay_chain": "polkadot", + "para_id": 1005, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xed030000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c706064c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf6380b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x00a0acb9030000000000000000000000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000008200e17579c4", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9381ca18820b278a00faeab03d52440be00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da944098499b5de4f5677804569aeadeb5e4c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94c4f030742ff8899655335ad1e54ca6a6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94d2dbb242ff048066ba7c14ef24678cf20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da956f8d5eba063b801102d640867bbb26f689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da96684268ab336f4623df9eab07c482056049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9784e05d1b3afe91a143e23fe5983f63080b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da994eb9f87cb79eefab94a8a6ffcb94bfe6d6f646c70792f62726f6b650000000000000000000000000000000000000000": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0xe2373d0044636f726574696d652d706f6c6b61646f74", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058b40805be8e467714531068689474e824c10a8cc53774726667883ec05d802d901643e234ed8170f89bac708c7155821d498f97282e09dca01e2231f2d04dc75ed1ff328c8cb9dcb66ec66356fc03a1ff37d21a2184104208d95b4a1924164e147013536e28ea9568e6424433bf39eaf5749ad799e3bcdebcc62baa8b99b5bb1045972edcdc7cca0dbd79253a5d88e8f499df68e0ac0d450d80e0c5cccccce93399579a75cf70040f84703a9d4e9c877dc8db66a56206344470c4ebf6d36be937afe0675e636cb07b17ba3e84ddf3bc4fb941bd77b35289620b6264b15d8868f36695b98e5d08fcd0750cc3b06bb3b292e88214cc28954adb4bb362b39aaeebbaae67b312f1043020c8b26d9bf66dd66bd68e9b758823c8308533689aa6bdc6bce6953bf6aa8136b36623406307660c4066d66b8a2aca0004ed42441ac9346bb340081ff881f681ddacdc0408706084a669da6bf6ee35e632afdc6d5eb52360e9436091598b385201129cc82e44943de63b940e5e68fb50e920087eca0d0a7e00b366a08b177c8004ee4244dcb3c7cc0ace5a647be942f143db4ba552e9a4598950e20c56a2c8b24fb9a1d92b51cc858862ce3d9bb534eb005e8784e2b70b19f950fcf66dfb941b74fb37eb65842d5ce0058efb941bcabd12811722021f73eeb574d22bf8ef35e6da2b37eb362bf74a348593a1f1536ed0f821a17823171a8a3772a01f993576d1842a4d88898979252a5d88a874f031b3c6f9b3c3ac54be50420e5e0082e02b51bc10517ce9e0ac46e6008accaa811970600c4628954aaf443ac49766059a3b746a3f805933249c80841234c64fb9a1f195687521221dbe7a7ca5df5e578f79d5e1e06b9c5500b300b3ca26a6808213b412d10be970a2d5f4a1537b3a1df03325403425a33a9c68ca0cd5e1536ea80eb3874eedb9979edd74efa8db78ce737cc77b5ec3879cc873780000000000e490430e38e0800311229f52a2446eb8e186214386d860c3a794a80daf3caf397676765e6978d5c183078fd71faf3b7af4e8f11ae435e5c3c7a7d8501faf425e7b7878785e55af4068a0e1536c280daf3e2a954a88102141827c4a8906f9f1e3534af4c76bcfab4e8e1c395e81bceee8d0f1293654c76b0daf3c76ecf8141bbae3d5861a6af89412ade1d5c7eb75743ec586eabcf648a53ec586a65e6b000204484f4f4f2af529259a7add79b51186e12b8fd71b737e8a0d9daf3d5e7370e0f8141b8ae3c78f4fb1a13f68a081061e1e1e1f3e7cf4e8f12925da83070f1e3b3b3b3a3a9f52a23aaf375e4f373737af39af332814eaf5af34383838af395e6bd8b0f12936d4c6ab8ed7f0c68d1baf3b5e674ecea7d8d09cd7d48e1d3b74e8d09123c7a794688eff534af4afe1ab773a9d5ee7ebcdcccca7d8d099571caf281a9a4fb1a134af3a38707c4a89e278b5f17a79dea7d850ef15a7468d4fb1a1355e71cc39c330ac51e3534ab4c6ebcd6b272323f38a7ab531993ec5869a5e715e6b30ec536c28f67f8a0d7d4e4ece8d1b376cd8b08183f329258a8342a16e6e6e3cef534ad47bb5798d0141f0b5e6b54422915eafd74fd33ec5866aafa7d7aceb3ec58676af33af323636363e685e4d35359f62436b82d4a0a1a1999999399d3ea5444fd7f529257abdcaa4b6989818cdd4037aa552097c259dbeefabf1662592c251ec44533e8a7d4a8962af9d0eb7dbb6490d27cb3ec58666efd4be62433ab5279212e975a229247a7d8a0dbde6d090941a1a2f45a3f143451a158a6730a2f1453534ce1c3ab5423a2869559c555e49a756eefbe361b3a0d7acf172a28e3489534807aae488fec4fd80ba6584034006197ce101767c8c8f315a41638c310e75cb03dc219ce009fa56a55a5525d8aabece91952b91369fb6f67ebf47888e2e185594d8efd71d63e4d8dcddc789ccd7284f1412be3cc7b99ddaca34320fa15bda7133771cf6d1d7de2e11ad5595697c10301a8953ebdae350526f890c31182a94512931a52e7a1c46fa49c13c2f548a475a22436c448574a0fcfe7e3c4823fe8e804694ef19517e77e956a79eee00cf5f91218c8c8c8ca0fcae3dd92afe7e5bd55aa7e27f9a43af53f1a534c07aa2af1a8def4ead929d5aa6f138ade2d7b44a86527228514ba37d8df64421e9cbf7e52c62ca73bf23fc9eb5cf8fcd4c846ea9d6aa7a138302b0cfaffb2e863580fdf8fd7e3cf49bc4a9756a7fbac53c80eea23b861457e809a75b1ea07c4fabaa36a455157cabae076995a4d76b5ab5472230835ec76955d38b568fd61e7a7ddba757d5a3b5865e075bd5bf7e6a55fcf5eb5eabf8d757e79a4df6882e8e163357e9f8ab537bc2a51d7b0c243bc5f2ff7890cfe6d675ef598b22e5733fb5ca7b7c4dab344fc3b059b173c734edf160abb0c7775ac8b4c3c2edd476ea3b7f469aca4e7db37ee7ae854bbfb0e35e8e24be162ef5c2a55898aaa1fc5d9e781c90f2acd9afc77791b90b9772a1a6037f0b8b2eca7ca2fd6ebbf6f633829d6798859569f6f82d66e2578fc677f2db68f23aa5d3a978eddba9fd3ba549baffe99f98854b37dc4ef56558990a71983061c2d0daa27b2cac4591ee796a9deaf307b4b3bfdf35c14ef57fda3bb56a697ca438ad3a8df7b144228ddf56f1ab0e8d5f9dd8f1b5836e752ab89daa1aed578deeb77db677fafb6e7780f1ddee80ddfe00f7a84a15ba471ca8426b89f66da07bb4012eb43f201a407e7f5b15df97adcaded75ab5b4dff50794cd05e36f7cc8c204cbb394bf02b4687cc75e0c976621d38ebff82e86452c04243b257f04a4f1fcedb0347e3fa0ed54f5285fb3b6e8a5c52cac9256f9a248b1ef0774fda7bb6e3f23d8ac9262d79e85f5347b0c6bad350611a50e41b36fa7b2ff442d1462d1f82cac3854be630f0b97c670e97e3c481affd33f7d854291c6cb703bc5df0f284ed929fe4faf4ecf26205d1cad8ce311dd2a00ac3894afd32a3e1f877f7eaa557bb48123caf7c1382ce56ff4d9ded9f337fa6cef548deeab46f91b7db6772a48f715a4fcd5e1d9a4872e8e167393ae01209fe9b690fd7edd2a00ac1eed7badda31a4b842fb60abf628044dd0fea955fb7e4dabf87d1cc681697f757a36395d1c2de60074cb03dc232392a055a37bb0553b861457e85e6bd516e10c69d0bdd72aa6fb6d9f5dd513ddafcece26a78ba3c5ac43b73ec00a527eaa55d5a37c7e4dab6a8af2f93e5ac5940fb66a8f36b085f24fad6acadff6e155c5a17caf557bfeeaf06cd24317478b3901dda6c0fd662632b0108e500617f381180fc424110302d317d3144c523069c1e405d3174c603099610a83490ca6334c6598ac600263c2820906a618805e00c1805d00b9006e01d4029805100be015c032402b805500a9007e01a7004601f40242017c02e8049009e0124025805d4030c02e4022804500bd008f401a802f00a500a3003950c2822904252494c62889514a4249097104b189c8445c222a21c120bf20bd20af20cb9056c854f0edc0fb80678477a5234357061212242c4860484f208d411283940512164a2328355162a2a444294b498a52074a564a1c286da064440a433a43898a120cb2116062c40f805f605cba33b82cdc141c0c382a382da62b60570057909d202b41f480d701ef08cf8a57858b02e704ce0bc90892d117e63bc367862f8daf0c1f1adf19df183e33be2f7c5df8b4f095f155e1a3c2f7e59bc227858f8c8f095f123e247c637c627c617c5c3e30be2ebe217c41f8b4f88e3e2cbe1f7c3e284df11df15df1e5e0bbc167056605d007de06b21490c440fa02c90b243390d22095814406121aa5284a2e2895a04482af0ae94ae9090f09508bd881d295120a4a554a272819513241c909ae0a8c0bac0adc0c38293a2e7038e06e509904611a830f0438b2831103140009d000563a20a0071e0ea0eae9b620c4062031213075c1c405d3164c593055c14405d3134c64988c60fac2e485a90b93104c5b987a60fa816907a61b987060b2c26403530d4c4f98a83081c0944412300c60124024805c4023804060238a1802b3b01168a574855219252b94aa50a242490a2528949e506a428909a52e19a00286211d7d3af870f075c00be39dc13383978657068f0c1e1ade19de183c317861f0ccf0c0e07dc1f38207c6eb82c7056f0b9e16bc2c7858f0aee09501c513418706f883ce0c100b308b6e091d153a31b8343833381b7035e068c0c9808b017704b701d315a61c987860aac2e403930e38a32dcc7686cd0c5b1adb184c5a9882602282a908262eb6306c666c60d8beb0796103b371c17464c2c2b4840d0b26304c4d30753129c1940413124c6398c43085613ac27685ad8c8d0adb976d0a5b14362f1b14b62c6c676c4dd898b029614bc286846d09db185b97ed099b13363236317046701ce0aa806880677056402c26266c5ad8d0d8ac6072c256854d0aa52fa52980660087008e014c03dc007785f30087049704f7010e045c08382c9c08b62d6c64e0942879e19600c5e098e046c091a0b404ae049c13601940326c626c616c5c4a51d8bed8c0d88cb079b11561eb6223c2c6c536844d08db165b10b62e6c653071310d6103c2a6c576b465b161b1fd60f30138c6c6836d071b0e361d6c576c39d86c104fb055b16dd968b0cd60abc1a6657bc116c5e682ad051b0b3628b6156c2ad864b0c560a36283c136c5966593627b6243c19682cd041b093627b612704d804c804b804a6c37d846b035b131b12db129b161d944b0856003c196c4f6810d89edcad681ed88cdca56058462e3c0b681cd88cd480ba39d413383968656068d0c1a1ada19da1834316861d0ccd0c0a07d41f3820646ebc276028d0bda164018949c5022c3e4014d0b5a16342c68656856d0aaa051c1f401b00a78c47684cd0a6d0a9a1440316851d0bc6850d09ea0394123436b82c6046d095a174d095a1234246863686268616847d0b86846d0c0d0bed0bcd08aa075a11141e3421b8226042d08da161a10342db42cb4230d0bed079a0fb41e683cd076a0e940bb42cb818603ed069a159a0db41a6855685b341a6833d0b46832d062a051a1c1409b42cba249a1bd408b427381d6028d051a14da0a34156829d09ed050a09d403381e68456028d04da08b4263426b4253425342c9a08b4106820d092d03ea079404342bba275403b42b3a255d138a06d40334233cac26467c8cc90a591952123438646764636864c0c591832333230645fc8bc9081c9ba907121db42a6852c0b1916b22b64656456c8aa905121fb924d2193421685cc4b0685ec0999133232b226644cc896104b9075c99490252143423646164676848c4b66840c8cec8bcc8bac0859171911322e32216441c8b6c88090699165911d6558643fc87c90f520db41a683ec8a2c07190eb21b64566455645b321a6433c8b46432c8a8c8609065c95e904591b52063014845e60476042c0ccc081818d81798175811b02e30226c1e8826882158828d114110932062880afc4001100c139040027a6021e8982e7765d83a06112da94092284b82824892a4e308c493a0244b8a9600912409b7fc44080b2429fa21688809104f98244962c3c3e38141f2be58a2200c2c799204e575b14541444143519214055dc0c623c206152d292222624214f424c8c88dc7c53e81c22449d15090942750a0f8f0039402e4f086f0431228167872c48913280228fa21c889132802e8e1096131f0c31033de16fb4488a124440c0191844914fa448804103134001a0f084b1414648124440c054961e2240888d0d36231f0c31037bc2c7678582c51511011d1922451920439a9c092a228413ffcb004033f3880c7fb418fe703274e96fce0c4c91201f42088a20b3c0972c2830d0ab240122845498a82b80093a0293b9e0e96c809932545414024090ab2c00e0f070bc5024f98242162284808212296f470ef061bf4c313284f922c49c2240a0dfae1099403143d1102ca0f3f501b9e154040f92108881caf064b44445093244444440421790285091151900242af8a9dc2240a1d0a6a3204a5013c841e0d36a86809104c9c2c9192244888202110e003104117b8c0121f7a783358a2263f0405f1430c96a8e849d05050104f98dca3e209130ba0bc29f60726444f980c150d119484082751a22c4932140484132642ac9afc00c5c90ac7cbb244371e0a7649121caf048b81244b8a9600b1a428c800266f041960e264099322264f98cc784a6c931fa038a94092a022217e081a2228c6c3b2435004e07921d82750a208910408274c84a84092274b7e48c2240a2d7a1284810b0429008820208470f2c313264f16b0644808bac303c1120531296232042588222196c87849ec13284c8a960c0901449222284e8892308942970c09416776b82c64508c91c509e8b6ba3d3232a2dc8a361826024c041c8533194d269349472772e7f04aac9725b618cbd5b48e3b0cc330896dcbddc532d9d92663b2acf3e425fb9ad998b16ba6b3968cad5c6c33de666e8cbb71601b316c3199652d7baf2b36ef4a6ed6b869b028773dde6ed9516a51ca28b9939c3126637794d815802d46665994b21bcb64dc96b11bebed4d4a2fcb9a59669eecade5caedbd5a7a5df3769671b3646c25d69c71b327bd6cf3322fdb0df3b2de4dee26b965b7e4665e996198a6692c97a564c9383100c830acbd2ddb4cca2e4ace64b66d9994524acebef96158672da5a669dd1d19a3699949afcbe416935dd7c59c75632cb7cdba97e52ec771dc0d77d31c731c37c735c735f372dccd35d7dd1c87711c87450cc3b0b8ad35866198d61ac69dd5d4608be55053d3ac6158776b58478e8d615ad43a4c6b0de3d8bba4dda8f576b6bcddbc196fafc65d7fe0f72d7f4c62de355d5fd738607d35efb5d7455a6c1bc3b0edd2f676b6d862bdbd18d68c3586ed2ecf485ec6969b799b33ce564ac66878b398e5ee6e5ead35c91863dddd758c75cb53e3586ed981b26327b9376652c6ccbddb2dbb9b4863cdccdb3277866a790373044b33bbccccdd729bbb872cb36c1f8c5b4a660c5be665d912c3b65b8569cd9259322f7393b26e6ec9429825f7aeec65ac7136eb1966ce5a0bb2dd9d79ddbd4d6ad2c7ddbb4c026d905a2f5fabf52e6fb3a6691a336bdcdcecedeed7bccd1d57f62eb8bbcc58770d40b85bdbd9bc2137694d2b9b9b7bbbbb9731eed6b8a75b62dbccdc65bdcddbadc9dde5eeeeeee6edd696f43573dfd8b0bb4b18c61896358661d933eeb0ffc062e4c5b088e25d6c8b281a78987b31295993925993f2da24fbc8616e6e0c6b8c2396c55eacaf5d13194c69449a6d2286a06020080318b8402e00c549920b3c6172810b40716253fa698820209e3049f284c992207eb08005a04c1b9c3861d2a4e7c68e1e291ea91a3e6aecd8e191ea2901e18449103f24297222c4120b0401b133b4a488684789c8e6e6a6c90f4b2ae0c40994211e3b96040dddd8043549523444d0931b4117b091212a2262324410ce55f4c312a2274b868a921431c10006a0a46a6c0d4c2c103414659e86960439f901555a204f82a2445982a3a368c950d093a00be4a009ca0013a22416800265284a92a00b2c1982524464430d434436160811166032142425c90d408408a90188201c1c104144444143513000e58724434152a0fc103494640911103f042501d24425a486283f0411258152148588a11f7c726c0d4550a00c2501226828088821284954426a08623254c46388a020704e1628122249d1132643516c16080698143d116228c90f444c303083a86ca884d450435093254e2a7099213500493204942945492ef0844910444b82b8b141414316c091f224286a0b24c913284c888682a424c140922128454b7e80e2c4c99224454304dd90f22428a63c347a5bad8e6b492a545aad8b4a0b53b254a850a14285a92c152a58cc946cabd5a2125b5953914a5a54b845a5d52d956c8b0a152aad16a6645b54a82c95562b462a9b9216b75a2d564285a950a1c2545aad64a9b0921653e196a664a950a142a57545254b854a2b69f12a592a545a2dd9ca94b4980a634a5adce2d6a5a4c5db924a5adc6a25db6ab5a2926db1121d6217de5dc600d1920ddf100320f6eb1800e5e3aba4d965ab9666ff96c8d22cbbb744b2af4ea60525b8501cba474a58417b5a2d2fe52f2a345e5fa89c42314c18d9c4c97cb26060a971f2b1fd785830b0506c66e9a8d07a85d79305030bbd5e3340b10b7b02802a47f49a598aa0a2cd1d82c6f3bb9661fc7e10c8520495fdb2dabe598c6c80e0054d403194030a1a7061541465bfd02c463f94a08591140054391aa266805e1bd82fab6d1aed97153f3e8b11cd13aae0c0158c8c563b8ba0a24d23a992b346a1f1a6283c418a335431325aed6700197a3546458785f6f73b68dd0d97062d91a11e9a45beb55578fcca79cd2a47f0acae5f915925d1aa13eddfc43001562774df43eb89d6a5420f55ae9093e8543f0a583909daaf4b05da67c98371270c4fe4600539d8e2c5c868753dbe82b4573134923108810b36d0021facaa1cc1b38abf22b392558ee815f62b0158c55fe19500ac583dc49d7ee442fbd7595a7cc07e4dd64fdce96393a5c587eb71b2aed953f7e828be2e971543078890650b4ca0220a61c49000dc556b045ef840052ac8a24b0dc0b0da957ceda1fdfd7cb891d12a26d1aa1db42fc3186edca91a5dc642170715d9e2965aa294e162a6fe755ecc945d57765d97c4e9d4f5fe98ee11161afc34d04693d7d725901087a1f2d8c4e9d4256594dd46d86b4be4fabebb2247ed117bc41eb3c76b587cc41e63ec3019c82b5c0c3b6347c9336d8a9231c07e858c4dd9a9d5ae90a90cf7a882343e4a1ae34f1187c66b5e04de83ee11087e4097d4d100f23113bf084799e97e401d3776330f1df6aee30f887b77b94be4fbf5ee0b5bb476d9d724fbbe0b6b768e2649f708cb13542ecfd51bf6bd30fb1632ed6fa8dd0bb56f46f8fb9fd666dd6bef36acddf75df4b4b0bff336d1dea47b9f0bbbb8bd3380f7ae3bef0a11b5e8f73aa50c6608438996cb144d78807eb3765d77795ad8db6587795ad8dfcf08126fd621e8f66e67fdf515e1cefd276eb8943fa06e36e18a70dfc97dbf231add6613edfdaa21e96bfc6de152196eab6379846ebf3a759708f6fdd56161904e5d58a4a03d9dbabe5fc5a1d7e37e41781e4efb70bf9ec312f9f268bfeea35560a7b4edd7b9707b47bb4efb6828af7d4ebda3fd3a962ab406a1d7514b84e9d56d214ea7b447ba47585cc0613093bcf678ba472200436bf8d38eca1e6953543685380ccdbe4758a0a0eb859722d053c8dbc4e9949685d7f2ecf79358c83466a155a35d341267effa88ef3886952976f6a9c2bd8a96f8edfbaedbc23deaeeda8bde16ae17a3eb2b80fc7888349e0b77d5ddb54de309d01201d578e8593007f8a1dae387a44b347bc717a952c1c25e7f3bfc78f1dbe1876aefcfc8132a448566dfef885773bae8f54780d60aa8f62e7af22bd282820a29a1dabb6612a952818262eba3cb3ceccb348fd3b67057dde671e1aeb67057fd1de963b372f34652a5427779ea1e31018662e742700b6f64646464b43a221f67fc8486a8b201238ce810366bec8f07f9489bc45fdf6f87a5d914a28616d5506ceef2d4788e315c7a854df8324421a944d0fe522addc2fa3a43b3c200af5efd41595575283f3b020116aea1062075e128892f1b6e0bd1c12e00dc2325a2a06c44e3bbd2bedff9b0b7a103c0266c04bdde74bf08630565caef0f091b51ec1b4a914740ba8fdf2552e55147382aa75089ee5709b467caf786a15d7b1b3600e433d5b2e74fe84220bf7571c3a05302761d0de0fe24d341009460b798c9eb94ecd4e93bf4c0456263c8cb6bda469fed9d0ad27a8abd5b1ee0f59a9f56550d54d22aec396077c01314bb045a152916b70bc586506cbda0d8e57f3a0465500cc3300cfbea60b3c982692d7447b2cb77320c33f9eca7a5b6fda765c8543bf6edb5b5615a97855298ca500ad3ec1dcb307bd7fde5914d320364efe4857064b75df4649885cf5a9b5552edd8b330b69c553b3ffb4fcc8458b4b3ce5ad3a6d655764a7b7f45987a97a1d4e67e40deb3b06697025227d2c8085ab50b65691811864ad1a87619d6eff157d884fbf5ae935393f2425a950d184187a46074bb948c12b5e83665bc56d49474d214e23428f785454db97ba150a4dc441de1237a5d7e40d76328ff13b95fa194a6f15d37af8fad08d3eb5c28b3599966dfc2ca5b58316fdbb66f53fb4fc4b0482b518bf2e5bd59449ecf854b67cd6613398558b446cae72320edff741796f61451f27cf90e51f2fdec9d0cf91d7f3bfc64e7ae3dfe8af4b3cb23fd2c13e2304345de7e47a21c5142b56bfb26f2d93b2dac92cac76d0e092d1823235a1443b5c758e4518d87285c3824a403ede5912fc228f7da547e0b87b817614892d0ed3fad854239743b170ae9407979b65088c3d0ed5a5899ca6deefa90fd15b92ecf1f10366b8b6297a1a685fc2c5cbabfc2daf23fbd1fd050f4d86b4257bade5de152fe7e15d92d565de948f7ee1d7bdcd73be77d3bc785a74eedb7100cf7fcae0ba530e5500ad3aeebde4912cf9f6e827d035bd53dfb9ebf12d36e3fa0c54c60a7b8f750efdca2cc6165da04fbf625c2340bbf3c7bb0553d74df8560a756a31912ecdb9425eed7e5bdf3f700ed9c374fdba9c5ceefbafdb6595b74bb1772e8859503dab85925e5de9dc3d895a9f74a80161674d787372bf7c8854bb970296fdb429266ffe99feeb0b05ee76b218aa710d36ce26c6164447bf0d02cc4e9d41e0b2bd3ab95f175854ecad0016013f91db4d225527be8d5ba8e70f4baae57392b152a5f3703ec14e835b368df50fb1532e08712b208b3dacbb0b2a86ce264bfd0ecd82b157a7d1787dd1f75bd908215b48869369fec172a14c384a1d8cc5204950503cb4a9b46fbcaa2d7acf275a3f13f51863b68c79e16eef783c0828165b53b28156e5ddd61a45a15822ebd8415d1678b3085078cc4d88285175a582d1653d8820bbe700215472bf00ba0d7297e46f78b1294419982668015a4fc2a2f30360086b749d7add78a37dbddd29552ca90e97ef111e89aa0fb0dbf743c067f04408100927c61f99f96539b3af1025d11a69a00c04e953aa6e07e04685129678b7900b5a606275e468fdec8973162dcedb7c7e0bebf02b468ed37d92944857e40a0141dbfe3b008534d88e9326d31e340e20bdd2324b6a0b544e57bd0058303576849b62abe6390cfa0fc0ad0a22dcaef3006055dfc80aef6b655d86352707dcf20ff0a43698429d62a5411a6d8b1a951fe15325d1d399b8067e804c0df37e93387312960fee92dd26d11708f3e7005bde81e79a00b5d53b7d4a35d8941ecfd9ef1f2c2faf13fbdad4215e9c747ec7cd9aa7abd49bc3c865d6b556373418ffef44fb7981907dd52b02b6dabf6080933e85eb6aa1fbff75a250bd09297ef4a3f607c5f880a95424ce5dc4e710598b8c2bfde6d00c03dba72855e6f19d1aeb43a958de8f68d3e33ef6bd1e7f43e187d64deaf893e31ef3ffa98de4f451ff0fd9ee8537a7f48f421bddfed67e4faf718ce6be17599d04acdadc8dce6a5d0ca8e5be9f1d249a115d4adcc845670260bc7716e65e63a3f8556e2ad9c7ee331a1951e9365e33d6e25e63c0e86566a704c968ee3e078cd5938af99ac9b9db3fe1cb7021e09d641d51c7556ea289db350a7e1a6d04a8f1ebf11dec664ed4c560daff1798f9cf738abe63d262bdcb9cd64dd787896cd7bdc4a9cacf81d13891e1b3e1e274b67b2748e03c77be84c568fe388c771968de398ac9b9be3384be73ddfb1b3f31b938563b2789ee361f81b67e1f88dc9da11722067ed1cc864f9d031593a9e9343c3754c968fe700392be740268b861e61781aceea1144c77bfe23c744a2e7e6e63c39729c75e33926eb664890e823c654dca9e9a18303651386378e0327b573a363e306c7a62627e2c49a540f1d14ea46b8f31e386e727470d8f4b07163c3c6070e2a95f358b373836373a34688eac60e0e540cdf23654388901e38efb92aea5c9523448810214370e8e4f4dce73e3754aab35257a96a50a9bfe7435043767c7cceb2711f213e373670543aaa3864c859361f32e4c68e1021393df7b9a9b90f0e214284a8ceb25109d1c9410d49c50ff90d67d9f80d3e417cceba711f06e28343a5bad979cf85842772567822ac1a7216ea43264b85f31f67fd370001c2731f67d99ca7e73e349ce73c7ef4f80df7394be73e1c44c864d960c3834c96902067d97890c9b221c8814c960d40ee73a387c764d5c0e3ac9af398ac1d3bcee32c9bf75cc5c39323070f1ee7e1392b751e9eb350579dc859f144b8e786b3707e03d7f01f379cc859ff8fb370dcc786c9e2992c9efbf0c133593df7e1e3369c65e33e72dcc7593ae739101e3cfe83061a0ee43c7e9cc75938cee3ac1bdf719fb376eec34254931524c8854c968f90b3722e64b2820879cf6405f98f1f579d15bee7ac1e2732594386bc86c9f231593eae8367b278f0b88fc9e2b90e1f677d3ac0c764f1f0711e67d5f09cd9c3ee60bf01488ed330593c268bc769f86fcc1d720079cf59396e63feec0ef69cb36e9e3359436cdcc7861bdf31593676fc860dbf7156fc8dc9b291739ec9baf1dbf8cfe2796a2a893388ddc1ae9a2c21a90799ac9ec9eab90df1354c16909e68454e96161fe2e7e57bceda99353d93553341f0a7ffa0a1e63c93e5a3741e353c5e73568f99e231593af3a5cb7ce72c1c73889dc90a6794d48ec9c2711da1e938c239595ab2c78767a1a6b63bd89a3ef34f560ee9376c5841dde62c9c396477b0a326eb66f6ec0e7616e93137bdb3097795856c446bc25d6161d5be8f1f12fef5fe8ab4f62ba29dfb4fdb84355e13d23c0bbd6361f74bfb4ffce99fae3184fd3a4dc86aadb460ffc2ca46947bd785312800bd77dfdeb1e7856c44f733c086a1de6c929dbbb066137584cf9dcf4da196114521e167cfe6d2fd16c63001b2115d9acd841b77faa7508b3b7d99108c3bfd98b026eef44de1e34e1f0c5371a75f0a7be24e9f140e893bfdd5c9661336a28b830a4f2beb1e1974d9e9a56fdce1711e7ff4b1e2dd0a127c1bdfbe951adfb8d3e33d48b782047fe38e8ffb784df4b1529aac997ba5cbdccab7d1c74a8dc9024faab9951be7bed7a28f153959d29bac182f274b3ef3cef2262b4e24f8ac789c936e4566b24ea7cf4c96cc4b353e73d6cc649dc29fce9a79f7d269be07a38f956db2b6779f3b4d56ceb35fe79dfd7456ff345918763959dbe559a773b772bd66b262620e4e16a74d96f61adc5935ce4d96e79dc5dde62629df4d16867d9b2c6fb250df2ebdcbb3bacbc9c24c6761d7f17996fc9cac9898cb4c96ccb3ec3893158373994ce6f3acec73b270340fe72c2d9c48a44c9365ba8ccc6d4c323239ffb61d07b56ddca6d59039cd903c99b5b6cd789193c14a35b0aee38954ea5823939edc32190eeb664e33b14689846d9ae671329321757246eb4ea4ac636fa51a988c87c99cbca8b343725daac13d459a9b8e8ee9c4799a0e1d67f575e8d06666729e43c6e7e876ec386bfb8e1d35b2acf49cebe8b6ebc072e438abc673e8e4882519f99cef3891bee3a9b3669ed2a1e32cee3aa68ea7ced2301d1daec6739e0393798ed6d1d1d97156f61d93a5a343f29e9a1375567794f69cebc039ea33a9eb38eb741d3972e0f89cac1cffbc8eb3b6ff2c9913de64ead373ae8342fd3838b739ea479d453a2a5ee73b6795bec33d374f9d557a8ac33ff59db366feb3e275e4b8b9c9f98d8edf9c55e3377c16f79b4f1b9b1f67b2509385fa6f83739bb3badbf059d8e7739c259f8371e89c955d67b270e83c67b2701c07e739cef29e7396f69dc9dab1e3e164d94c96cd4da6df4c564dcd6d26ebe626d36dce92b9cd64d5d8bce6ac6efeec0e3f7cea1d78d464d54c56cd51efbae79cb53d67b2c03fe72c6e46d91d3ef8d459db1c6277f8a9c9da3191601ddce7646ddb63262be6dc3c7796361dc04dd6c6fd66b2b677dabbb36acc1e76877f7396ccdc6177f839264b47e638262b67b2723ec3c9ba8de74cd662a10a1a5e8449011896b8b2aa71840e3461043274e104568eb0b252e33f716767e6de981b77f636e6e3cefe34b5dde1e3a06afc66b26c665e33592d1ed8200b5cba484105183c58d568a160034c50428b25a0d16505ab1a67d598acd20469a61677f63373e38e8f5953744f9a3567793388dde19f264b6622c12b2bde6326ab74d344a26765857470b23cefa5c96abd208b19b2bce00c33b6e08515699b00c6062e500205676881c68a741669b2b2d9f3cd9ab8b3f7e6c69d1eb30ea17b3987f454b23bfc6eb2b889446a65457e9bac1c72d0032ed0c00c31c0c460a54d24beb2929385186e8005138e2043165656fad959d96461f3bbc3675d138c3b7b3937eef098b587eee34ced0e7fdf8a5476340d569618a37100f7a8034dd03da2d7bb52d7edd7b3897c9fc32acfef7e5a55bdd7530fadaadeac2c1b04c10b5fa0a1a19939cd6b8de73064c8a7d8d0210ff2aac3bdef07d02ad4c9fbd0c9c8f73eb4aad2ccaa813154610863989999b9f9cc2bcd0310804f29d1000409f2293634c86df8eafb1d5a753a9d4e9f72839e0ef4bd035a556766cd400bb25842166e6e6e6e7070705e7178fd61830d9f62436d38fdbe00adaaa7596fcc6a63d69c594f02f85e02adaa3fbfb98761181156ca10860c4354e1892a57686868664e632367d621a46001982666666666724eb36641188110ba70f33a7300bcd21ce7e6e6e6f586571e20403ec586027925f24a430d357c8a0dade1b5c6cd67cda2f881152e6059e8000a60b8c800431346b09133ab148314c6e8c2e775e638bcd21ce5e3f32925eaf33ae4d5474fcfa7d8d09ed71a910b3026b0417c811668dca077c0022e78c15e7042972b425e677ec32bcd55af35f80b26364002e6001547605a505c91052b4558f12589287ebccedc86579a0779ad914411b898800755b8c005ac25664002301938ba821745e0799d3990579ad3f05a6304618ec0821b4c70b1042b368608c1079698012ab3c11423e0418fd799a75e69eee3b5c60d36050caa3006ce8d592f1378390113376ccc2aaf504513d2b0819ab5c168418b2b765e67aee395e63c5e6ba06e666d1f9031022b3738b3320ba208c30a38376655c1400847b86163d622b4a8a00543c0f13a739dd71a3650b3b27260540230a06e66d50019a8106608352e4454e30667560d182561042e682e4444f31a3fe1dc983503590c21892dd4a85183e6355e4f0f5f677ee395e6f3b5468c8d5989c8028d16844143434303a266cd725005259880ba1011aa7433eb45052dc420e3e642443747fdb4e1cc2ac50803092a50a807b36e7c0e6d12af181380fc0e65b819cee36beb23dfaa1ed0277bbcb63e1848e54bb489d4b48d535eceae7b4ddcd9cf78a74f8614533281a0a9c42499efe4cdd474345c8dad46b3e96e3cd40eb331d485236dc41b43e89e3be71eb30004691f7b5f0b6bc299934c0c0de7794df7bcd7f7d5805180fc282429b6e4c5f8f3c2e5d947c918ed77248f4179d207a44df990ee65e903925749d27e077acc02f0b49abc8e718194f1b805209f590032ed9f32d239d4d8ebb88d448aa0ec1f5ca02520ae54d00db50908e07edfa23b3b2edc503498782419cbb55efa6dda02d61ed31630f338346d01f79d966197b69178a28ed42c4c983060a0cd8567ad00955b96678f81d1f1bbcaef1863eceed27ed1554923a9a7d7a94d498a558c44bbc676483c24a993215462a49ea82318ed37ed591b40b999c1d0550659431de1788e4b6cb4eb4e91fa3ffd9a668e523ecaa945d49148e399c6591d407bc62f1d073a1f6c418b9aee97a6ab4d2b33874da4449a8588f88deee783343292743f1fd8c868157dd8a35564cadd7c44b7cd1de5f515d9281460f7bbddb849b102945fb5731f907702570056f97d4072c81335453d6ed2768a9f002b48bfafc84655003e01568dc178f003aae914cfdaaadb85f6390560c7531c7e02dc7726d9bc34a4daa225dab326a13c23674be8b8b9e71d671619a453fbfdca7d3ddd479f3502172d608841088ee86203ab960cb2084307300823085dc458ed77afed5767990af2dbaaf5a972c490d5f72bdacac6156df5a556df90d5b76df4d91a4a3acf2c365876007440944b250eb42fbf114c7c05b1a73a25df31a805c9a153f2da1cf2ab06a1527e87e873a3321690afa05ef70c9c5736ca795d2e355eb74bcdeb7ab1f9cdbb9c7057376ae074d1475ed69a6cf6f84851799d3fa01f930f3f30a3534b771840a7e45113fcf560ca4cf2594e68fa8d10ec42a6a4504b02765525be5ff31af7a9c2010eb3d272995eaf0054b9326465fa11da07802b35ab7e35e57025b5ea5719ba27ba974bc4333d8677e46fdc50f96e3f900b901fbd9c30fe46d8ef8f3b49889b282e5b6a0a57bc23df7dddf7eb81748f14fec41df92e092aeff110407e9523be5ab6a1925848dd2ea83c7f4548d49b3fcb23cf4238faaeeb9dd0f56e1a59aadd0ac84771b38718fb442d1be18ffc15f650a5cacd6b5ec5e635defd2c111235bd43f05bd843a7e4390860a4df6ddc01df6d4cd4751bdf950348475d138500d36d9c01a6db984568f1a10fbe8878d31900dec66dcc22fae077c5540b13c05f0230540fa6276057a6332092910526c2acc0778d0a777513eeca26dc55cd529cb0872a564aa7f94fcffc446b84bb628a13968e0a697e13cedc263cbd2694798d30e608d8551599c7fca7ab980e1e043b05863bc41d7913172a1fd385cacb78a1f2a732a8fc4c284f235fa209773513eeea14ee4a26dc554cb82b53b82b30dc55693fa01e7ee2ce07c09a4ec95ff3d429296f780b904f0abf7023030164f9d59132adad413174bdd3d78670911a5293e242d767879f9ff5e9e9599f7f7dc0b8c33e1bb92c17364207762af521945f7be82e1794b726fa3c157d7a7627faf04a5e5876500bb3d0db141f0bb54df16e6a8f7a0e523985a2d0a6422cda5452497b66d14e9d8a07a3cfceed9d187dbeeddf88c7c0aeb417464ed017e10c69d0d6a1fdcb8b5d941d5f6a916793f86b97085f765758f9174f2fc6eedd66e5cb63b3f2bbff44166251ec5d58af677c226c3691e7cbee0ab3103402b7576fbbbe23d7b759afebfc26f1dafb03e250865ea7fae0e9c28e615dfc8e5cc7666d127f3589c7c2ebfcc94be95de1528fbff8fd64a8e9343685aee346ad1a7b9f017b9e45f0f73d8bd0e2037f5f441ffb2e86edeefbf143e23e48a7624ff4c1e6b6f67c1f96274b8b9c3e60efc9e26367f5f7ad558c31f6742ad61c687c87ed35b360e1ae7aa2cf7b273ec615f64ec518638ce7af872a7dceb2dd8aad65e956a77650809c179de2a253bc2700b32fe6f109c05eed0392df2ecf66f24c2f995139ab112aaf581e1180d93bcd63f00848fbae2bb26b03c3261f7e00a6534b6b878503137d382f5e3a2c4a683c83cbdb6b8df60a5eafdabebf84d829f61cda0493df66ad99d7dc63215746c879f980f627c6b0e8a2fb9f6613801d16caefb0b46a27c7a5537cce0bca6727c0ca7165744de5120959d27ddd735f3ac595d1297e0da0a4cc95d12aa6bc4b642f2bd60bf7852ba3550b24e7822b2f8b2e8e0bea88fcbe45a7e818a31d739e0eeda2f43af46877791d82b4c332afc3e5423bcdeb10b59d0242370bda6d9cd7610ced3aaf438d769ed76110da7d5c90b492d761d30ef43a3cd1cee4755843bb18afc31edac9781d5626cc5021b43b1d7199f13a441da992ded09d4e4e5cd0781dee16b4ab41337392f9bc6e71482b04dac52d9400b1631d1663f35ea428b70cba6fd16e9500fbfb152151210eb357a01b05da655236c56615a22dbd2deaaeaf0889c6d9b4c31d830a2b68c75e1a3486cd9b65596324d4918b625f5a99627388c6b963c0600b7a6df4421de92f94cf437768d77c7901134b00d6ab0cca472161322edac933817d479ad605640c4517e535d498b761f568ad4e964b0c64408bbacb341280f53adab8f008c0ec4318edba21d40b8c7a43177512832668516fd12d8d76a4a312155aa045ed05e5578e565e02ac35949700b377266fc39ddc0498bd45bb186fc3218e3a592e743b25e36d78ba1375648f1d9bb54b17cadf39246975b25c9c2c177a5d415bb43b791b321360f66e4f323126b044fabc991939139bb7a6a644b15993d0788e76dae66d181e81769db7610884769fb7a18976246fc38b76256fc39076a0b7e1d0458bae6b0bcaef4c197649199b7939a61de76db859d0ae43e980f29986b4e32524ed62f3c68d98e7edbbd3aa04d803ca8c05acd751dc8c745db11db76919767d32b6c74b2261cf2836ab111a3730941f86340beb75c5185378d962147d960a36c8a205540053061818adf8dd95e8d3802f64687125064720d183159f03c39d714d214febb5652907a655fb6ef3580460bfe33c066b803e7079b64f28005da0457ba6fd21f1e8a28e906846775623928b9d95e9a6e4d265309de2caa541292643ce8c4ef13930d187f3e2a5537c2f94bb48010d318a3006316cf1c0727922066788810355cc2007f2ccbfa0883e205f532cbdb0d0959f9148e5e3fb9d0c9b76ec5d9beaf777e4a2f1f19abb3c1d69ccc2451f0e4ceff059a3fc7160f895034399268c2e625839c216621066c53736c0c10978f0840778c0c48a7f6d893e4a9c188113a2f04215c69062c5bfae883e53ac408225b850410758d2587108409e4d3a28b6ced1d25ddfae55f2ab5119ca4e5dcfc2eb1d83bbda70a9167d50323e3b03fada2c427b3f9b456829227bfcae2a48f7deaf0f117db65fddb3702fead41525fa74930352d2a90bb5dabc7767c0766e16c17d7b378bd0e203f7ed4574f7beab2bcaa641af779bc733cbf79ff68a3a7545893e4a7ae7faf5558dae36259dbaaed7f542af83d76baeef75edeae6c61d6ef6f0d3fd6da7ae3308c0783d7bbc15eda81ee244618f6fad260a9b18d65a5d7d45320eb20374df1f10aa87f8a53e4432b2d084d1aabf1f0a01d9e37dc81e274b8b3c2b3ebb9c3e68efc9ea6bf228ecda779585286cf6b03dfbae3a06a374ead2422dee5cfc0170884e5dc76651a7ae5fe72a2067ad6e0d092f71c7b8c89857974ef59300ab768d41fb3fd18757fd1da28f0682758fc03d0273c6425fb909aabdee19f4958d288adfa21753ee0048f7678944da97672c911b34228047a0bd4b18da5f5768a72a3741fb5a7806ed676c4477883b7db0067c4d8ad6d30a2e18daaf295ac1d5864baf56cd533db45775480276f5d3fc0150eeaba45e645f5d74bb037c0fa8d3a9f6b946755e63e4c737897faa5539dedb6bc9e635877b25ddbcdee85e3fd4ab0defd5c379c5a1b176365e51a457eec6eb4de975cb79b5a1f1da12913fbdd680afd95f6b985eb1f09526e6f5a2f375e6facceb89cabc461caf3234be9708f61aaf269ad79825923d1e5c22358fb3e6b88e7739421da18e07c0d3a5f5f48892a8e3fd94fc8074cc05e373844be3db07eca7663de5a9133218cf488055d2530d4e3f6abbc3338255a4517c6b1a08d61a1abf712e781c337cce0d1b38a81b9b9a1a3433273041d0a20994c17899988bc69bc08cc697481c8dff3a1aef751f8de748346ae12e4f7c16f2f2c463612f4ffc15cae58997a1b63c71365865a56587f0451834076b3fe8fabb3ed8ab56419f8d3bd7e3f9d8ac5acdac3571474e1fedb3abde14cf9a03ed07a17db93ebb3b2a6c0a4a3c610b347ebfb84759e856a75e5ca4c9072c9ce8d452f0b47edfbfb8883efc2cd8b35cbf8ea20f36af2d9d42c9ef77c521763e03aeef2c6216a1c587d5b2c9ebd47574f97a79417b77e4b150c69925fb4a86d7169dea5fe1aeaea3e8736de99d7e36b777b6d07a1dd1feaeae6bcbb2c9072caa746ae969bdb6d0e5020a33a071061728018b12562d23295e70851a58e183207c59b5ac88c00a3a18c2162ebed062f5630632988110c2a001129c58f51510255f8496f822f8d88bd0e2c3f57d11d8f928f922f6d78bb8be47c98942001fbb0f7cec71fab0bfde2b5ef1d743152b7cec56f6d7b903b42fbb2c111c687f485fd78e402e68efce461f6c6edc915798face47804bb9de0ad8ef83f7010ba34e2d4d75aa1fa74e15b0cf1c00b9a00327c01082097cf00530abbeb8d0c54125b662635858406bd52f22de4a9c1dd061d5ef49a539e7480916340b31dd5fe978656d55271c8d97ef8f87ebd7cc825d7e469666b9f21536a9702b2df1f143b251396b0f749767a3cdb42b03f5ac2dda1b497501b4b4bc44965bb1bb894e1a808da8fc78002936a38c51ce1899896ebf2d54de83824a1ce4371218d1edda470086ca7f24c042e5b577a520745f73a05bfaf7fd698f739d5679dfa75ab57ddf33a4555b28af2b5e19c7854b334dbbb6b4a4fda75148ba63f25d47fa7e06e83a8ed43d7b77795f7a79b348f6eb17f72c647a61bf6665da79def6f120efcd26d8ab3c518b6ed73e1e885a947b93edf2ddacdbb97961e16e2d8a85dfbdb0854272fdfbf5efdcac2dca9542ed5c93eff27a29ac52a54a95d28fd805c2bc3a6217488d749b75f5138558b414ee4a8b40315080dfe387447e7bfc8e7cd7266a7b77ecfd1df9266a9b48bc67f79e5dbef3ba269e01daf3985eb36ed7b8a3ae2fd5be85a86b16c1ae5d9b4bb96f94bbb62659dd34edf1d714e230d7230ff2daacd9b773ccf40a9952e116d75d13ddf200f967a2cbe93266022100aea49f47921253fa987a4b647777dfef8f07c97b0f6848eb2cfbae04eea0b27779b0cb7398bd1fbf22fc3db744b259cf5f11d64214926cf239ca57b77d0e65a71aeb637beee3a19bdcf2748685bb3cdd46d0bd26d8a91d54ce5367d1c5c75009c897348055a30d264106ed7bad0a999ee8eaf450b74248132441b147cd738012127ca12c7b70801355a22423a65a85c27ebd87eb574a4910adaa35cb45015ac57b54822de84539cac546eb92f1855eb4ae167cae559534824d2fc61eb1eff28b30ba4c755a25c46186f64518c5ce1f9224b48ff5977beefae059e3377e408c7d05387f45623c671aa0d85945144145a342b16fd75601fa4db459b122d823927e9c457018df71732389676c22e19a5665c78400e5d90c74c790e20a78da23121c512cbc28d8aac57ebdebaf006630c319ce40f70bb33ef658c861e87e46eba38d8ca0fc8e60b6a34e88697bbfde2111d36d36d926e01e9dc0095a9b5ee7708897e77a914685360cedbe5f11ef1cd7f164cad40b2bf6eb5bb8ef160779ef0453d0f8ed1c56bebcdeafebf6f8fe9020f1bebd931e7f46b2ee5be85d7e48b433cf5d1efe4f7b61edc9cbc3dfde85b529ef372945dcc2b8a5d0ed94fe16f45202bd8cfa039202360de81eb9c00aed15d03d82a20a3445f7080a1250be14306a41f7e88931286f41f7080558a80fba4727b8822e9126c2d1e51ff2a525b266a0f1bb3e18efc4ef972fb19f2c41589ea59fa43f5980b03f96ca597b562a544ad30b0c31e56f270eec3c6f897811660b2a54a25234ca8fc71887a54b6bd38ad1f82e7ad777a44b030bad46d2c07204a355082584f085172aa48432181550212a245a8ba4f0849a15d05a34c50a3a29d05af401295cb4122de1063d76c08556a22bb8503e7f473423a323f13bb26128fffa8e9c0ac93093b3327f48b0f7ac539041bf22f2d77f62d716cd7e8528249712a83c1636a9442d2a9f5d68c3840913d6566764d04b0a2b6898859712688739943f23973273cda24672da6fec57d8a427ea88c4260a897c76ecd7f92b229fcda26e8fb274a1454daf59fb52985ec7c2905e6148bba8eb2f186db45b000949a3b85ee4151719d897651cb80c32e8bed967577b8e71904674df655efc8a2c1999a6691ad69a16f94a91f8dd06bc507e7696efae70d2e68ec326d9190bef59c8f7c2f819c99064e76c8500f7480930947b7606807b94850bca51ef5d173dfe8c68dfce85757b932cacdcb37759883a228d8ce8774923ddef59d8f4fb4f67deb5cb537a3f0b492f8535935f91eca4f9fe8a6407ff13c11085243be9d949334421e168768e66f3667924f6ecfd0e8c3ebb92cfc253a7e44b27854b97a77acf3e1e38eafd6689c85f1f0fa8e591f7e60d8964848db298fed360f4314dd93bf2b234ab462bd8243bf79a7d0bd0c282e338ee3fdd498fbf22fc2c7e47b2734d48cb235f5a1e6944e5f2c822501a1941bf775bf7cdd27779b6d2495b5823ed4eea485bf72f1c2295bcff74c93ba9fbbc6f9d37777d74738b4bb7709f55c626d8a9f849fb5f674974100025dd2ebcd003088017dd2eb0e0b28b030cbd48e38c8e7b06fdc5599efe7ecb9375a8752afb7e211b592ac461b8436f79b23678cd04c8b7e1ebe127ee28a105f17a044dd4e8f084ecd091daf1543c767af8f0783ea0e5f14103f7784380f4d4e06dab96006ff07a891e01d84d803878bd440e1e5fd71580dd8d2b375e54c6b031a65778ed27c3489b12f1647843d3ed54878327c3cadb4c805adc690078f4030a80b700d84e074ff6122baf97e077dd36bd62bce2526fa3cf156d65ba00bc5d8e3691595d1922d33d31a1cc4d61ccafa456a6772bcf145ea9599962c22b5f99a200bc6d55bda83c472a4d8fa609c61d090454a4c876465a09902f3b231f50b7aaa28e30462fda1929b51031babbbb5952be3c27a1db567169c17ed797ec90bf5f4f2f457a7a227f8b60ef0e0143e2cebe93f13ad13ae40332a100ac1a2d827d46ba52115e1e3ef750c58a152deef0b5b8b3d967245c1e7eb83cdae61929e245a97d4652d54477fb8c34a9b668f61901c112028c64549efb1250028a81023cad1be5d74dbef3daf33ccfe3d548f233323b6e99e348d78d60d7e4b8a87531e684ae6390b9232e605bb6a0b11730745b3524c461e8f5a2ea615f2e362e269b9fb8d3e7ec0bc57e855c2836b177517007c0ca5eb25ca959d59cbf3000aebc0a9bc3959e554d006adec79c58223ad03e77617d86883b6d85cdc2aab2f79530b4c71006c861800c76ffe9293a05c451fca7b1f307744269bf796b85d2260a02a7d3dc8799df4c9616ed33d7a60fa7d39c66b248442081163354118591d1eaf4d6cae635be5dab11da68356cb69985e63472b217d8642e9a006aa7993045fbac0570573c051a60418a131819ad6ae24ebfa6565aa47094bb0d7f0620bdca3bf98e42827dfb36bfb40343a69c05b072144d640e1e0be0b90ae076eda04c184b60e908768511b40b77f9e6de9dc7e85477612edc9956e6c2bdb668696639459fd3642f4c31ee421b3b33c51807f012fc7ea56f21c63f402b3ab585bf74aa04ca3016c0989b2e2ff34eb62783cd98b0142eedb88c96973926fb32217fb902286f643f24580ec250d97d0b3924dfabdce59199594ea7ff347f699fd399b29732b07bf9f60e63ef1432175d006cf2165c0698430fb6a043ac0056ee01ed730f5ab54dfec247ad9233cbe9cce5c45cb8742ac634d90ae642fb5c05b0b62a6f3180e41fe0b789f10ff03f9d7959e802dd5465300c1581fc05da5ffee104dc20a47105ad816e10d2a842bbebc364507ad3aa51f02038ab138f96bebd2b854c05b0f4d27ffafa906c185aba0c398a5a44b753b262dfce5f11ec204fd1a97ee8c4a3a5c932f8029242294ccba852e588af6afc889e15cd678e7d48be893a72ec0a23ba4d2d601f909c58d8b4cb3cf6d2a92980dacc72ba17b297900bb90c4de3273ad59741969a3396e85333c14dc35e23dcb5a14a8a9d26dc0552318a7d26dc1582fd0ab34d61cfc26e53d887883ea5ddc1de39b406c5b6fe115f71192da4f70cf6c204b0b29721d9672ffc85cb686c0e5d14c3a6932b68515320ae24fa9ca6d73bfc3e51ee891989548a46af7759286f062ac312fdc2892050195eb46b8f0bb48bcd72e345e2282c4f3f93f40af7480b2a50196e943f20f6b23c7d9602c8edf39e2e189898a69142b717181adfedb903208ee46289a868d4a18c5dd7f43aa5752adedb6d8b02185324452f2e62183f01d62b8aab4ba7e239f6025690c61d68768602786d893e9ad6f5da7271e9543c3f01ac5714dcd4c22149ebc585c667a193219e13c02b0a7a45d1aa292e192c918b0c709b9a36a5eccbc3302ffb0a804d6d79f81718ba385a1a6333e856a7f6118d59560aab68d1ce812846469da6bde2d414ddda1339566d671fdef2f58972795d5829e42f5b488fa46b738b9785f21d06d87506f4fda7b332b4d02aee114c77d2ac6c4629beebbc2e5cfa5d1e180ea1ed79dbaa4b7a5fb8d48bb4fbbc2a5ab0ebddf60171374b3cbb92cc5b2b3093097b86947ac0cbbcf498d71e1ac19005ded458b47e22a61453d2669652696eefc4f09129e4426687b8137f455bd1296e02c8c597c25d31f37eddd43e231ed596479b594aef48dbaacde3baed9afb8e64b452a117cd4259a54aefc467a11666291d0c3910a503bd2c0c3fa0befc8c645d85c637d14e740acb10cc04b0b17413ed44abd603c35d31b89d9f006b8a6eef3a9494c3002b1ff5fbcbe6823d872edadb7492e5ca57e065408b9a9700563e3ae22d5ad54d742a3e9eb9681f70b613bd1323cd6663e954a4dc05acbda50a169476f98bc6b715adbaa255d763bfb5025f41baf5cc627a6b25f36ddbdeb34418689b0b0ea1a6180f864a007775d3650ebe6a7cd4a9e8058defc0705706a4d9cc02fea7b7e8546f017b4bdc2213ee0a0c3f305a92b0031e6031325ac5842c2d3e80374d168be0082118adc0f9b813bfb1b140117d7480b1c413a8d84017aa28c22abea7883ead2b27d8828c315c21892844b08a6f2cd1a7f3c011d030820eccf81284557c3b117d648ea0c51738c8c1152f3a58450c0450ac76a5a55fdf583a159f04b036960fe87a636955636915ea08a38ec8eb1795b47f5a9b1d5e94bf04801cc801c35fbfaf74953e475930609cb4ea9a710a1b28687c355176627db867c5de5da1931088209000ca782f07b1d0782fdb7f9ac13e7be9a69798b197382558458bf623bed2c279692c8d25eec4cf2e0bc3ec95bdd098cd6db2974ec52c0bf6ec622f5b687c6f69d5953d62b13bab5a6519c2a6965670952507bbb3b467cdaebdcb422ea3535f3a159756f6824486f6b90cec03d2e6827d7969d597f6e19e75086d2cd1878fe24efcf6aa55b0b1d0f8dd4c3e609144a7962ebd0cdd2cca2004ca47d16757dbac9a362b98cd5a53c407dde5c92af6ce527a5f33cb95af4a0fa27d4a8f07a255f251be14cadee93ef21a0707f1f25774b25d286bb428d223dd1176e004dd2eb14bd7ef4adc71bb6c978e728a2962949794f2c2aeebbab0cc0927300cc3b0acc46280555e41a356068dd189e84473d15c9c71064fc1537c402c0658645bee282f2cd3da48ff3243b75ce8f5eeddf655127db6f904d81f10a63d110618bf22910e5db60aa37b04b09e5e45cbc3053c296995f6eb40b42af67b5a257ffdd72f2d642b3a753d867c45c83de8d4f520edc3577ae73a0e8e8e8e0f1f4182d07a7ae55073a04aa24f91195ca553d79f002b1f15d9281f75ea7a0a4099a53a75e974ea323202785d7c446b7096c8d2ebd94d4c0a4027db856acf320986d43ad558a67d05c83e02b46896cdfe46f90bf0fad2bec75c343094baebd18bd3fb77a20158a54a46795d525e5ce3a4e0a6454c8b3bad6920586be4ac97a7f5751daba157cc302c4a958c1dd6a6dcb1f907df6bcda355d2965dd0ebb2555e17c31a68b73b207f0468d1386b53d411d4a95371468ec6e9c4a318ed22f746499273f9075fd2ad0bddea7801d6ddc20ccae72f652d11a6fca201ec50805635dde81e7400e380d11dc309a860b4aaeb85aed12a0dbae7228055a3bcdf42f7dbb9206fa13fad310e4bf91ce3b037ea8263947c052b41c9b7e8e932de3fee9d967da34fcc4ddf56954efab68ae633608dd79834e1766a669ec2ed94cc8c09b753a659190adaaf0937ee80b33296d2dc4e91e6372be8cdaa75b3d670b382dbd46636b179cdca5762649a965abad5a998118df724d8718859d4a9c84980ad9ae8c3abf8ee925f30345ee8519688a4f1d7144be4061abf68b0118c048def2e4f8692f607e4a453f1d704a2531109d35873e22a98518bb185f2bd4ef1a3e814cb80a7e8147f871ea24f4ddce16b20585373e626aef3bb2be4c7104bbcbc9cfca535d1a787b8c30719aca9a172156b987987b8c357cfd3043be5f116f9a55b9dca47b177bfa0fdbad13daf0fd0eef449b46390d90aee014fbea20b5b41fb0e50d2a54b972e50548e227b6527683f9301ed33145814b4cf2146af39056d255dba74e972510774613ec25a4aa1db1d20d32d94f842f7880763d022d03d02c215f4d42a795d0646347e0c1a675153216a688d626464448b6a68896e35d157120ea6fd01528feebe93024aa9a191db88a47156ee382bf6f80b9b4491325d244d99c65f7d9dafd984a9a44d98cacb4ed12cdb3cc516e5ac63986e01add575d40110c865194b0407caef592224cabc5950fe0d2c753ac53e66aa873f24fa706736aa7b86f7ba657caf0ba63b17db99e3f4b42fac72754fa77848904ef554894d0470cf85a81eba8ee3b8b31570bfef3f1dbfb08d283f66c7b66b0ad8d5f59fe6217187b570e3c6c7f819df8b3bdc1377f8d42906afad253b2343b73aa8888a138580aee38ecd22b073d8b91791bdfbae503d6447256057d9510cc09030e5f30714cf3d0695e7eece19f97ad0b4b823b9bf1eaaa0e25157ccbe9d0118f60d7b11bb02a2533204e533b95a65ab3909a253f2555b3058ab5b3ea149d9b2a38c12884f424698f68f70b4af0fa827d8298961b119d3bcd075dbdcac814eb6684a7e77e557bec96a9dc2a4bc24b6cda579b7638c314a23ae48238d2964eca8826ed332ec92dd732f2d24c76e13f06eac751631795d526ea8231cd1104646464654cb628c1293cbb3f20732fea033eff107209f774b8392aa745c4515ddbd85eebdd07d16b1d03dd647747f498682ee631555d41a74bc03adbba1a0fb0c0cdd6347747fc9b80390df3be8eeee65461dc962d0e303b0d6d0aec1b90eaa87ed4b391966e9ae459f6c563962c86a7b37af682beedac66959b2c7b04641c5b3f66845447622b4db405552c6fee3513b7bd01e670276a5cd8d3b54aed6d5b20835357fce05768c60496a598c327694524629a5945276dd1d27eb40ca6b0aba456e74a1fb9fde6020a594584dcd9f7345b024a59645197be680b178bea49432a3712e632159aba2e3362dc32e191be452bf7bb98e9445a125c718399a2064292e2fcb962766d98c52caaba3c4f68a987ca472d629f4c2a494bb9ed4564a0c0bddd6031ec41863dcdd0eb14b6eea749219c2dddb4397ad30d22d4785384c9187836e23006e172b7082f2104ae005c5423b078f2ee3a0bd32f39bbffc83999959fb3565a7b450d3348c997972b70cab138ff2f7044ea8bc2655da2bd6d81bc3e666929b434d36cfaa512c9b55a37b4d8ef237da447372a2ccfdead108832eee8d60dd492f46da5d9ef42eec2b125b88c3502c749ad4c115af00f9db8fd1687958723442b743478eeeeec631c3e7dcb08183bab1a9a941e313dfadc59deeee934c8c092c913eafe3362dc32e199b773745e29629a3eb9a9a3fe7aa31bea5e6c3bba3659137b597b1670eb8d431e66031dafdcebae85676062e1c5cacc5c8e242f706710bddb7e418e3c61d743600f9d5bbae5953f4baa6739c13d637819cf5ba9ca4e44f1928a59432c6958f534a19e5253962e15eefa43c662485b194f29252ca9304a594524a29a5c41a40352c98c1b8f3836988717968dddd8d31c618d90795e7e5e971f1c076b218638cd2a36207b77d40fb8d3f56441db4c618235701f273ac0eadbbbb1e0e5a638c11cbaef30cab31418c314649f25eb34ff580f1d7ab94f132be898c570c698d31c692c736883146d0e318802c00a5c98b33c714734326c6c649e603da2fcea9eeeeced418639451d49035357577f7c68606b5df18638ca8babb8bc3653d2a34191b5eea3d436a62726e989e03dee3e5893528cd50c707489a5e4d0d628c31e2f0a200efe9c41863ccb1bb3ab61d5ed4525e94d7b3dd2ab56e671fbf977f932e72470febe1edb7fb9865f73c228eced5c393d2c9102aa5f4e1c57bcce36d517c8a5404290d3cfd83068eb1eb8931fef062ecf941038f8f1e3c76523b74e4d0c131c3e7dcb0d183f10d45e8e41594af9541f9597482f2b1e682f2af33285ff214941ff9cd5580ddbb5beae6f746da6734e838065ac7e035dc5dea591d40af6c01bb58c115f0d43ebb6aa395f66ebf78455b69ef2cdaeb1e7bb7a1b61b66435079bdc6631b632c6a2a677619aa9232760c130687f8babb3bc474c3845965f1454db364df8f013aacb20886ee63af7777777757f24a5e9eddaec678b5184ba525b26524d212d1b4ef5b22d9e6794b04e33ada44ae8ee39688f4b66d89c44fa34da44959b644b862d8d61863cc30adeeee6e7577376e948f31c618bbdddddddddddd5dcf051dc7cb7731c6b7a99b99c832b83c7bee2e5dc75a800d0583c1b83414608e188b16ac05b8ef36d334d214ba8d5b687c17312ae74ffb905080f23b823e8325730bba8e3bc5f1f3427e02e41418b12661186317097504a3fca63c6b03688ba0eb4a0cca2143b03d3a4ea738ceeb3e20f69a243bef034be0694f25197e42e63473024f383520cd0724754ee009a706ac7902d47a7b0a8d67d0a371caeee606f50181db25053538fb01d960158035752367088de727c0585394b753e15764a39f73e6dcb081839af3c6a6a6c6a49939cd3965668c69ce39e70f92cc966eb9a3bc4ad811987d4698728ba7d4345273e9f8d7f9ea5587417a85317f25999999f97bc45fe83a0f4863ade307f40179401a26b1c87b43aa2dca24e8765552f6ac3586ab9232760c1306876eda67da6488e9860943fbfbda6f1d68cf4ea384a24d831b9b58236bd060dc4c76d2643692d79c3c1a1263eac0d247921fd8938a3bab79606792dca6653206f4309a8b47eaa6e6c46954e2b099cbc33e6c17cb058d33905cfcf40c9a511cada4e51fd5a3aba570503736353835b01c40a7628c1d37e3b0e21f5a403e2f4f63f2e2d131fe3419390c39b5dc02884ef1b5598498944de5ac4234ce9d2849544a4fc666179ca44c494f7a5a4aa7c747a7f698c73d390c69d6bc2836b627658aea746acfdf2ecfbc649451c618638c35151be3378d6f1a675d003d5623f368366fbf5d2743e87e67b61327d3d5c4783aa9ef271cd307926400722906356f92d7a9cf84b3c3102a869c6a3ae5c100e48335344a1c9a8edb7098ca79d2366db31a5814bb93ba3639c5234a6b0dc7d3a1fbd614345e869c05e44b27db257a9daa279602e44b8d5f00f2f735684a54e6641ef30dcdd356155ff7b2534d7669368f37aef338f3b08b548acddbc9784ae815a44fa8e4e956c863a19c441d63caccd08c268004731540403824180e48e371a249de0714801198a2484c9c0aa3b1200b72180521630c21c01003084286c018e10600791dc89d2509890a5cff6e97debd4071d54cd3e3cd9430493078d579552cc9a67f2718ef8071f81e77092537b35aab78c60d0a77b0014e52538ce35525e533b00c8754df5f03e9852e377e773bd9905ac41f28848402f14cf84335bf94e4608212b263c09eff2047502b2b894193fbbb47c596116c8ddb7f67b45398a242407eccd60b973c5dc294648e9ca080671e1adf5b0760b0b13f336e06e681819ad10c41ea831a9ecc3394a504310ee2b105185987f52b8879f98b82450385073df563387cc1ade3ef79d3e3532b8a74b616af185ced04b1628b4d7104d2db5dc4e7ba7d36ab9d9064072ee4fa1538ba191e27f0ab6f3778f48c477d532424444ea7579f4a73e562d5e24bf6d1509d1ced29dd4a1b4a688a01a4fb3b349b96e8bf674584ee93e4fc08f395e26b84816e917db2749fabdc13f35fa44ed13537a6ef564fee6063e921afd15de1bf32a1f17c3a1e30790d366425aeffc2f8e091929531ad42679c9471df5a00a8b97631cb46fa2382986dd4e9f3ef758e11f9fde940e32f60c68c632950dfb9281a3cd2cd288aeece0d5cecf0685357bbbf2d8d9269caf5ad057a455d7c3b86e329d10f26125a570a7fad0c0c4a9d776dd545ff1d83f51ecf9d51c0505970ef7b583a4b43c3177971f501669c4f5fae0b01a4e95b2131394f0731e725d371578c1e09ddc51958037e0ceb40e93ed11c43d98471092ea4eb0fd16acd502d3d08d42814c908c3a2f40491e7946d4a7b70075caa1e9c73a5e115404165edb883a45a22a6114d2a14af8a6edaae2096d57f7874d0ed88db8614d8735036e1a0eaa0a9f3fd334f643883f08ec353121005c92cfbc42d24774a4d281cd79a220f10b557932cb53f55d782b6e1a04d6cd34cd944a4d54ca1741500cc09ab09ae27e4aabda847571b5afd2e79b6ef3be082e06e4aab99da1f6698c4cfbfb3b074cf5dd1e556364259c157df24098747af98dc69bce17e32e01f562c5c5bcf477404756ac7b95c26da6f1ef3c008bdf5dd80c36eb1709018272ac06fc390b6f2466f29578d3c85ae0686922d3e68b448429b45a40a3004ccd7f54d1baed2fa5e035c99602dd73e67b9ce84b69b483dcd3b017444585390b9294e54904a6c99000b519f376ec1d4685684c265c8c5211effa9c6ec34b048a2ba10de4cd581c5cb7c7b49b822414f5d41f7a3f89ea5985bedfb9de4d76d8dd3a94c2b5be04e0981c0d6b1a71809d7650f6719127eac39b8b708da6dc63a26da5221a8a4a10955cb2d73734bd192485989a2c3d22b665a49461c3298dab3662c4fdb73cb4fbcdd80cf81404324bbd1793d826d5804783bb65ef891426c6b24a90990ab249c213dc48418b02f85bb130a748f3787031a970bc0c7a36b02a544c3485d834d6faf5461d1029aeff1365f1e193c1b6c4813f741a2f0dcd182c01e3c9d19da6b1e314cf37dc75a9629e813360d40570101772aef5954302f9358048d850c0e2952420e0742506c41a15ec1e5aa6a8d26588a739ca71dacd851b00c3f397dc086057716a27e461cc91bc4c30b5788251fa0858324b9f820f1f717ff8b6a6666fdb00dc2e0acd1926ee8f8fcc425985b52dd5a93682640885aebbf93293e07bb07f453f7a927b7fb5fdd60b4d09aa24f030ce91fa3c3831be59c71638ee70f6e7869931b0f21b9de597380e6bdfacfa84e13b39485a00842a1c7370f5cd064bfef54278dd993fee22dceee9298697f9059b4930ca3f4b650b9421596c5361dc5985c5c12ccb34070fd426bb04f929254988e0022830a2300e9a8c4c46074b46f5f43e7a27203e7d5b781c37230b749bf97b04c7d6e4c736575a64ac80dcf7a921161b5d2cb42c798e7c8d23d86bc8273016b48947a1d225d4508aa8ab84668fd07bc8353be35bd184f9ea1dc3918eb5947a67a52fc4ec789da931d42efbb1e3b54dc54f946e8d33e608989529533413045510261d2e890dbc1e22f89e01d04bf54229ce8f37f7e0b51c1fc114d13b510c351c80cda5a43c16a1304f24b1684702e60bf9186d884504131d0131741f2dad11bce06adffbd5c57fc3e26845a7c9bdd66a5a7ed48b31e8b57cd6b96427572d45fded05e44c866ce5575ddcc212936e8a675a94ecbf3572c8c6d53bfde23bbad71a5a6e781e541646b0551921b8be3fd6f1f7a2d03e8ef15afdc51cd310d6d514e9dff144e7cd4a6f80e9fe21fb99f5b92603feae1b5c802f615fe2fa8a30eab191138740ac02488e90f295ceb82eecf5b1b6bfcd15b1224a344f12c4cae34c493ff4241175d173c7864363789dc09da143544a61e9d595adf92451f99d2df886024dc03172dd9853a7ace9644ceeeba82f42a62508327975680f179a50bc168e8400d779a8792ea15f48c4fa52587f9914c1c64d3385f23dd5dfe60d19c838508321412f4cc0ab67c9fe297ba7e311eb7a081782e5ff77cb99c4c8ab403479ca136ac42276ac27de0fbd23f392e5e3f24185fb6cda3f05212cdb0a65246f108583051bf405f215c7d39426596eca0c6ab7d29572d08fe1a01f9af23e9536895ddff46e6b7baf73fe370aa317d8ff9058d9c1c4d1706bb782ca35c9e1c142745d85e368c4b50ef753c11955e6282012e0e10225308e4bdb846f2196906a6a2bce412ae3b8c85f219ccf14ce077aa9d1dab51cfddb17e44cc39861ee61c0edd45f9d4927b0e2edd13a22f44e005c59166096e34dc0f32d1af67db0c0c35711f5b88ba8a4c4b1bca259c4ec6a7facfe396d76d3d6383eccd9f06a3b8de6c1cfbf60bc7b301af1ee8fedcd9c01471155522f3389583c62768eaf8ff2cf8e08bf1bf886d7578f81afd1e2868a71bac6903b52b984f7aafc46db05407692973488d2a09e667a0174ac312def493cfffdd446fa11a5e3ce98a3a90302273e2128051ce1345ab2e3e42bb975c59eaf8dd4164090a24320a59d05d4b46ff7aef0a88c2e5b87c417fff48d2176f18abc4c08f32f091bdbd5c50e1a801bc4807bf9092457992acbc833f215fa744d116b69164e67d5688993cb7919ac12df8954887c31c1853d3ebbc418ace4108fc16e81d7490cd3078f3d567079134251bbc32c912107bc4b08fd15256bc04ca90ac92b147c15a84fb22f2588ff4319b695e5f233a331bfa63db43144619f4a8be87139de9fb94d747aebde1c7e1a96cca21275abda4756934c36e1601071c8e41b235b1080aa4c38e85eecffb930d4459be65ebfe760f6a77a8beaded5eff74e30b09d025cd97d07533acb25b6909f4518fca43341a32f0efb05e2cdefe178a75a2d01e4e27a2e64b3d7f9aa44adc4dd2f31c1c14b7744d2e3525cc2582ba4b44bdb496b64338486aab537b99749298f467423e79d5c04042528f01ac8ea7eaa502a2c0ee83d32742fd812788afa5ae6eb6753286beb8befafe328b349c107147e4b21d6d6950aad82775611a1a6d99f9b0c09fa235f9b0cccf833d1a706734d0934d964513c12b85f882aeb661e72a879fa6929fad2bdf908657e618e1fb900f9ce0d9c2585c0589ad1aa88d83387f94a0c753327b73c8196bd9fc6940e2773ed439ea4776c3e7c4f4602fe7e7eb7bac0a1203b204448e78e579d063061c1b27e02b7a61ff4b8853942d4f8936f1cdefbafcaeaa177b5d90f8adf1c3a3962c4a0c686b298a873c09c398658621e889baa6066f8b27a80a3a9ae6a0707aa34f2fb82b34fdd5717d34fb8925d1af1944c7cf90d2b7db53c66457b055473bf86a9b44e7bd7a1908aeb7b06fbd85fdad577acea0ca2eadf0673279f956bcc8dabc4ad67eb5c72acca3628444ae88174e9fb0343711a83995b7745aaea61b26d6b8f13b934a19180c12e8dcf25502ca1dac0c89335a59b4a19c4089ff329f88b9a46864f33d7b1056f21c8bb055b3bcea13214324d91c8ed854f33c3fd00561e91a7be3f5f61402957a425460892211dc74ea7114518b81bbd25e887b292b175c6f0d4532c24c005a1e0d6306805f90ad5dd263dc39bb64ad100d97d1eae8c09ef41dcf883c81c1033449cd8df3937d9c7d5ab21f67e4fd5885140b67add9d5972f0ba7b5f654ccaaacc2351293bd2ea044260615e3c75a03444e1bc024f9ae29d25a19564919cd91821474e52c79bb0eea29540b44ea9b127998b23b3ee024378173494b9744137831077539b9d2788af1401f74f1f01e3bd0a0127aba22f0b0827615970bdd49440d290e33dac44e5aac5e11727b7a4990e96e20d47822b41ba68daddca82e629a3bac20bdf518353330f846b2627e3251e8b1d00f0246d43f5750c63efe8b4614108285c249d37c5701d7e00f3d393880f4bffcd854e25dc8a9a0f380f018f240e286e1a196e0b301898182a94df40c3b3ccd9d26621d561ff1440af57a0c6f3e1c86ffb883d70f67e8ea164ba4dadb0dd56634738cb89c8f9f1fdaacb34cab97a14ce9407aab422e1510c3907d3d527d3811c23162a25f108ab0b3ce6f101bf6678a6569dd7b5859e1634641a5351990cd641496bdd85dd201aa8c05a6e3e52f4f70edd892141bedd9df0e45d9b02c491094d1862a878c818af03080973d347c389b321399f1bfdaca36c9899aed2c7844e45806f9d486d0be34f41156a0886c3d192229b66a7d0db8381108286ea91bca4038438e6e967c3e3a71fa0c1668a1f108ee9f233c0c8ff2227ff535de05f20d8a8a73aed649df16fce3bacda616e4ef9e850f8c798763bb5ce555fa50af7fd902dfa6dcd8c8671a5159bd1e40a2dba6b19fe351cfa7461f388b28c688a22898b453796b0409630035ff9c35b4685b10de730744e4ead1e79ff4630813b22a1c96039cdbf0d203589ed3829f2ff407ccb8997bf0b9efdc7406dd962267589467e6190613367d7355a604d01032bb0373ef0b75ceb67fccea1e51057b5980614bcb90b9442bd13eb1f22a0bc3efa60441c46de3a95463bdd12096cc413c027bc9dbc313fda3e5d4c16dda9e3ecec86f6534c9d5d152de6beee95193c5e7678290c4f7f72c5d8154186b693f0148a34a851054dd284d48e173c7b8c91106f1c76529b8a337b849826cfdf31598518a546af68fcd9809c1b99269616d10f12e8106a3f1c8c184be42e8cc82d4ea674e33294ac1f23e82418606f4faa4420949b839e0b1f11351bac82564cbf9fb24c5dfe9b0e5e7d53aeb2a06c154baf852e958b2545b1913ad590c3d9942b7de86acff8f652ff56ab86f59f525a707e97b41351447c54a7150aaddb958d3344ac2be97626aeb29d4f0c0c1d704601356c5d918eccc78208371cd93f0de7d51db060403efca47bc1f0b2fefa3f940e3886108ceb46c284028d8179a221cdfa7ce465256511a47dcf796ffd35e39a4c1eca114c405f455342f8b9ffd40b326e94d1cfa1d7e8896c23426134c9f79d478f9ecc241eeca4a886af66f7bdab898b8bf4bbcb66fdcc07a69425ebb60fbf3c3ab734f3b090464787ac1f607a4e0f6fd2d2c230e0b13d849ce04ddb6db8b4a06544271f8f42d5329e5033ec8c785a7f8199e23568bc0fb0d28d7f686a6cadfca326e68354eda74d9bb8843d0cc0c92f1d4023c0feb08dc56a74384c8ad6ec846397509c9ef7f3dc990d2a9e279bad4401e191fbf2c3c69f8f17c190164ece18cbca632f465997065a7643beeae1fc29b9b3404ee889beaf231800bc2d3f34ab89ae752912699e765582a53b03b4f697d2a79b9604ab3f011647a485c50c079bd1c943b3e9f7b0797e802f9334251accfb8fd9e0d703b6f4541b56b6e608c38b59c05dec585b990aa8f33ab3d49697fca8d908355fc4e5fdd6ef9911a079619a3882fd0bf1ca7e31bacf7023f945b3cd12794216ebe7f53fc95ff36627dcf7f4f4eabf5e389c6eda8165754d93f4aa1f1892dc2cf2baa3b0dccc7b3c5901b04f0100ab4dc3fa4ccc93c16324b9f9b8532e738a8cdae0a7c8b64e654be959de5715fcaea775ab894d1320a335de884eadd2c38ba0d0683dee422a4d9629996d2ededdee1e614232e9db0f5566a69548226140ade76a12c958f7f764467cf8b14a2711c31fd2fd9088e6a81adc96fe00844a5de21644cfcd2c317460509ccad2795712f473795a563c5d123c7e71173f075f37df0b75c4670fe46ccd6a8936c37141b2591ca0f136f651fba444ee333c496f094d1aebd8c05e3b73b93b57076d13737dfaad19365cbf9c7a0fccbf9ec74d2d53cee7ce0510643b2400b395c6ff0d9c96437321811b7b825bf25d926cc471fa225a802a08a969c19562dde88db321c92ee568df49ddae9b1e396aa9e6765f06a29504e5e9ba2fb0361bc245e9ba35d25c64f606bcb4237a19593bb63d034bdd68b23734f8c5332584917243ff3271fd4a69ce55c01232600d380059d24bb415fde00b0eafa7b5d0c9a8904e1d4ffc60d4e6ae393b514caf1b1a40c75a184455b113b078a40857f82f217b47828dcdf8273fe32abc397f4632e34af0eabd1a1d24af449c6955b605446f7b22fb19075c31b24ae52096fa2d623719aa65270504790e30086ef2baacc0fbbd5e4e57ad9ab156ad03b66503eb906ada4ea718f42139aa34c00f2afc5472f81f9317dc9cc919651750d751515971314f517f060eb84d67b1045f55fa1dd6fb91abd35b0016b0b76fc7014616fb4a1cf897a3fb4f67c5cb5f171ec6ca55226d0439c8f245fcf0ebe8d449e38f57a02de6d853d3fffbf22d969a4eb6af9a76552ea72d5a0cb47b4a35cbbf5a510ac0653098bf08b99729ad75200d59004ed6d287d12e1e3f2110734df6bb88e2e2ffaddb666a06a0e2bdd74e2c0de796562ecd3b96d89c5aa7a36b00732845c05019b3037285bbafa39a7dc25961b3734058896bbd9c21aff474ba076be2a01cc0981dfa224c5ca86feb11f90929a841ff12fa2de895c5cb4420143af693225053e62540bbb0a9fd3a2bdc29ee511461329e3dad933afac0a29dafe91f3b2dedfedd3abfad124cd36521992ebce8bcaed0f1a5e0cf2103998f92af73b2bd20496646f91de18a03d78873e090e005dae45db326d51099d2c6b6ad4ac78f6d5a391c00fd34320a3908461ed9b86a1cc0cf8dec400ea5f4f65b4bf3f91032c345782ca3b432bfd5e9c3708c4008148acd7de1281f98fd65880556e54a404ca1883795182999f6dff0a1d621e727d39514255a9edf5a6a06be0e1e6288d0496db677943db035b8e0cd4ef4c22da808c6f978903250338670e735874c79373ced9c661f25ed11b58a3863821ebadf7c8c0618856d2d26f95aacc81f37a7e4e9335f0d65891f9f989bd3f1e3daddf58017b35a5a0a544f295c1c6ed25e337a492608370d28ad6c089df94f306c1cd129c0d8813f9c8717214667edf73e8aec0a715afe279f492e64322bae3c066e6eb21ba19541d33d382a82934064096fff511889bfe969cebfeb9f4a897c32b4867533a18c76b253a38850aec251432576ff6dde0ee9549ae9041b23b5c2d00b00dceaa151e8410e46a45004b403cb4f526038032589e7846ee5b50b42a4897354bf9d968588ea8ac63f5bab12096588dbe9e1765fca76d534ee1f59f4a575db62bdd28405e9a744126a1ed28647e176628aadc770abaa6255f1f3f60ae1d659ab31a65d86d76dde97e17e9e67a0b16a0d92652c4d02407a82b626f7c9f50e73b1c5fba9565b6f72ce0e6d24a5d1628f59958e07ef48d90cd81dc1dd967501151a97e4ac25d69f5049a5669cf3f5f355b6f63a559e350321bfd0cfe33b4b529e76c40a1208d678be3b0b6b99a5f72f5c9c6e9664a8b52032e0376be136647b2785a0b7ee8009fe4509d910582d95c806b9a229961747d3498fdce57dcb7a18b9916797091384810208571a9b17049a4050adea81cc324f10853c68770be025bf91441c32a1fbc6572a19817bb722102818cf02ef2b88062ee46e8191c7f66a0715e08d5a582c3f234c992334233e298672534e7baa1364db69e817c5c15c76787aa21c0b3ca5e18ae14041e8ce9e2f2c9b8dd4bba858a96b175f55b6707663ab5fb771d05819f20340a13131c2c466b7b2981d04338d6dd4b66d562bf46da1778c42a70a2ad01627a300408db584fb35e14af3eb1c91d4326ee291964e5ecaba0d98591f30f15f93266a3a5bf6d5bfdf7f4637d0f79f3f093f86220dd11bbc9750868dbb2bde695902303aa69f8af827af1ebddda42c105505272d109fb1247d0f830534dafa429f534a27b760dab4aac5f42863bd23575ae302409ffdbe9f70acf57602f7316b66261f3d877ece80e13c999d61c5187ac216f46e73a5322a0be706125abea7fe2e087868eda3f804d9591c1f03afc6c311fd507dbb47246c35edd49fd72b80f0cfff383c8d01a53906721afbb5cc4c2afdec2182301ed818aa3520bbadbb344b3325712340875319cda665ecfcab8a310fbc1f25dfd5aa2cbad8e2fba01b2cbb661ef7e02d71f24a5b059bc2b37c81a6d8cccb712d3081ee64284cf56454514d8cf03e4b162796b9d9d6559960911fbc18ebb02e0be393f6e32d05eb60c23d4e2028cdc32b4af08da922578fbfad6c8717d00de6a7a963b1c777e7725e81b6bebf9fba6646885bbe143ec15a6ffbcc516d03fdeb5f40b07996b5adac4b781a3a03b987931e21a529953526211870a7019378dd4fc0f42ab9db2d0fd270ab72db3dd4455193677625f2eb7b6bd123267ad2233f5c39adfbad0d88538fdc94bbc660ffd184d3d3ec08efee158bfa53e4238992301c5bd5224471f7ce28e51430885986d112b6ad4f91a1265d042dab6de76444b7d26515e55f2d8a5a2abe24cb6a810450ddec558957de7f89ebe5553883662a8425fc180f8f922f7bd5992a7574c2755982749626ca7e13a3dd26120ead3bcb2f8df17d707d29408a022e4a8b89cd3d53cb7ab7786c5968b59f12b252c7fe2fcf120cbaa2aa9fc441cd2007e1e18459e664b202ec9fb863b701a10ca1b03ee0779c19903d323e15c02cd52fd12ac28c1e62f197992265e0c4f6d7645d35c055a39b70b9a43e6d6acf85799d584d99c02a6fb43c9055c4d81a59a106373562dd435af8200a09d7a4884ae64a0a98903d95cc7aa7a077eabf82f45ec0ea40a477ecabde0aa66c8a68c3fc71df5ea54a8cffbeadb6ebc1b7e3a30fdc21788066e121c25bc2cb77daf43a5312cdd2ce0e8bd7f80dba97e83df0ceed5309a4e8f4a8b6a506f3b979a8fd0db78a8c77e4baa769c0f58d6c44f550cb6501f3678aea9cc97d115110c3a0d358ceb48330f1716d47e6db1ddd7dc7cd9575dacfd709729b7b58f65ff8820da9958d4570a155a3566ddec186b3c05a477f3a831d7a3b46b8be13ee6eecbb6e662edcbbbbcd4ad6c6cff18172bfb358b71f9a8a324700491f63d760f7ac064a2b1e10807d038eecf488007d2f5a8b0de6bb6b549632ba7f464d16c886d6bf37f87f0f9d20029e560c45a831588dc767d141291648cd40045ecae6b22e98a4966891531fc862e4c2062bcffe26a8ef4cc18bb069563da40e02026e3d960140ab0386e2342d86f27835ad399bf34b54e02012fc46f570a5ab2c5db6a8a53b7e07ecb230b71237227d065340d778c6795130644ca5be124a93c5d2d51f0db09729ba67a497e08fbb7a56aac1998f27ffc37f22495d24bec96b985851038f9f33a167c0a5d16f68cba3c2c2272a7f8fd15510048cb683f1ed910abc379dafa50c8662486ab405de058039c68463d55af338ea1bace733d8271648435751528ddd05e6f40f8cd532568e1842cbc32491b71daf796bc896bb2d2b8212fd8d4366afa2d4e800a2744c02b9e8158b1091c154924ca96935d02e301649793f0fc532fcbe99fcae1ac38424c215902e1767f04a714620e669847cd8e785f03b9dbbd31be501cc03c4c3ccc01b6700702424fa70c982c6c2d8990251c62381312c50543166b49b4c909d02831ba8aa52979d16974dcc00cd29438622fd146efbcaae2e6031cec15a09739f3cfbb1d9f7a4c5291a821e9894455da09ee884d7783e2055c210a70091c8136ca895ef2c856f148c0c6e08b1a7b63b1a5917626d246781e32482cc6da833d06726f0200036c870470a0c0a857e49675edcc808d4eef7162a93d7ce2c47136c84c731841c844ce42d1909d2ed6e1c89049fb1ecf3ee80d62874e05e1bfc4e856b428ebc36d11eab21cee5c79464390537d10141545c82e888dbd127cc903ffccab16f1f615af0b21ac56541b8b7397f26159b842977a52cb1a7d07043b0c71794ba02f6f249644a395c058f8a67c6ed19378506878daefdf486dd78f1dc1efa550eed31b046d8a7e260f9599d46589af0ba12b33efa1ec1f0ac17eaff0255fc3b1d5b702aecedacd9f1dc809289572b73079098d84376a4f4e6a39cc9a56c546350329827dcfcfdf232988d7aafc35e3ab0df7e591d87ed8b02706e69968f8a6b41a0b2091d8f8a3db1f517ff35d45765fbc46804e9297092f47f36069947d59e8ff556ef0980dbd1cfe361a834932e444b32d0e0a4e5349e9816f85bba527ccb400a6b2f03bea379095392d38994656810e5b318f0be587e2c9a17f46b21437d2ba839829bf6c666ca10cdd8fdbfac9fe4673a3ecfb21fa22d7e4cc0fa371e8b3a2eb05d527d1636907f3f6a5aa51746cdfb8680f4fcbd2e854fa77e7a21a0ef5a11b02383f318471caca8539285b1f877f6bed1c5b6ef7ea682cd9308c14265e4b53a82bd4c19fd1eefca0612832e14b0b466177e7a9824dffd676a15d799b317ebf9f8d593b80200d28ddc828a30c30aa7a7165287d67a9de311b79f06a456a84c663da78fb3e3690b0e15a34461480422bc8ccacfbc00633007e7321151c1195acbd7956af0b0cb7a68aa80ecce7550d7087c0899ab5ee507d08edb35d4c5cc7d3dd1f55b601544de78018277743123ee1dd440c2076d8982b6ca07cd47ffd23fd15440b889b8547cfdf3017a26ea61d7b7ec583e00679cc578ef2ba426632bd229eba307b43a5d825b08f0cb444c893141087024088a154ca2b73dbc1444d6f962e879629e340d0746e15723fbe5f1932360bc080dad440687f81630d54a88417b8652442a05c685b6d16f3f3d9394ad86824f4ead8bc2e8d2dd9e45f247cdcd303fb3281d483cfacdaca01fa6aa7f177669a75930c74f068c385ef30d4f1b8d60ed28ba6cbd8c588015b43e208d585a5e65f19a96100cc6abe263e1eb31879e7a049cbc43942a07d2a312c8ca8d414be886c807934f31e32f184348477a0e0950cd171b9a186d50b4ac399d3ec37eaf2b123d4585f8782b0a379aa105d88a171c911bcaaca77555c149fb2dbf8d5c2d233dfb2b8270626662fa28b14a9092f3259c7609ebf55cf89d8114a487df7bdee816416e8d551017bae1f57da28a08ca677b056b291ad96913b7c8387849d9a0015c4875e93d4851eb904b038b60f563e44e96f1483acde7f4098d085288b4d3493b499edda59f3017ac930d3055133353cce8a7aa8a7c6132b4d60fcf18e39b5817be7960a6a52c4568650909a21639ffa248e0c27efcb1af87e28b82994af3bc8f91a05623cbb9ebc2e4dfe89af3200ebce2d2da5fd78aa0f0e4bbead85d80ad340c52931b317007fc3430381a4913608e15d8ef24f71ce0b28fb2b9139f3979fac26c2d82fad785f62e505a5c6c626565ec0868763c7c20942c16295121ebbb7f7702b0378fe5a5e6f1f8c045fec95b20a848845b5e4a05221660e587bc73a445037d9885d23c670b1c88fb28c563bf90225fe57247350c111d66527d47bae6d999b804d2c02f7b870879124e8cf3604d42d50e0a60a87026edb566a28c074382167ef8784fa69287e30ae96eafc0edb8c2ebd63503cccb1e6e29c12b56c220cf4ed762b3263721bb164b803b2964232a2f52b2f21b0aed6270b571bcb99f1b910d27c7b26496039fd94c8f444e7908a1594d63331857a4f5e729499bc1789e97c909c23caa98070941bcd52470ac2fa9f78b224311b70f4b7fa9ceb2d56749612ac5d472749dd8dc04537e019a5423e8952c85f35c784beaeab7a0dd9090b08ab5be435e49efeaa7bca8147f209131145a211eb214b01158681efa6aa4a169baf569d9430a3f408cc6b8f37e8249b3b2b51012f7fe9a073426ebab5c92a88536b9b3bb03209d5567b9ff4fbb8a76f2798e8cfc8f74f4463995a51dda75717f8db63f0a996f18046ff660436870ed779519802ee5011fdfac3feca749cc913aab4cb1ada6cefed2b7e6777ca7a886d865565dccfc41a4808b3f9f1a8d5de4b3e7bfb4736085ce9dd187da1b4eafd10adb225c14f43cb476b19d2d3339a073279e48c33fc5bc8cb8faa15b4072628d10e45e8c68fddbed540e024bbd6e5cc1b03f89e5072190ae3434145fb80c4aec8fdd5e87de21bb90d8fb8bec35d86610b3e6504194c7e4a05c3a4ff195966e9c0588d562e2dfe3c1101dad6e00e5c18f2a136a663cb32a2d715021f7c72218028c3dd797595dc6461e43a857ff4340c2981ebc0e47d0dbba03de46822d26f14fc4adebf4ee1be92c33ddcc3adb80adca29fc7251913b4a7f50825f6d1856c7e50d897d0df0b5ae7a0b6b2d014904203d23d058fc03f012afecc95cdc9013b326aa17ee0e897301685a50e7ae751408a948b8d875a237cc711cb89c23482949a8173cc882c628a81e19727c6f9b1d2e9bc77782b3cf1c91e1115dc3d4603cc3ee6ee5f4b5088dae1a09d7f7390e5a9e8d1e71d5ccec47d2f5005b311da91245f2883b446373eda118150c3579765d7c3124c4d49449f022f487c592c517685550c63028a00e92dedab1e98129b67a9656ca4180cf1e0a42a86fc051aecac1c19c6c763732064832ae1ac0df25946b3b1494a083597534855e953c546c41f0e4e7d74da677bcc87ae715479611fa089311bd7d3f9f719bdf7189aa9360be91d9189bf1eedfd15ae6f2de4eb596cd5a71ee7008a10087f35ff5133eaed32c29135e75e2ca2cd773843bf69bb12670afe36e903ae76b117f6ae04aa97aa3e5810c9a7fe0fa55f519cc18e8a30f9e2aeae9995b13d55891f91abff5e68c2dbcc9162b4a1143c214af587a88ec983f75b001ef8f5c3d3d3f38202329c713174fbc5b90f3a99ef6707b87f8c6d540167e905c176939309b916fb0d584c1d99fd06b5098f83434fd1d0f2a1876c7651debd3bee5b77c12f5888f2ff481947b80ba0ef3bcc8be5e02f67b8b552901a5a13de4900488d58dae6b483e6318d51e5c4e55b545fc3b20886d105e12397fcffe65c27d3e0b685b080e2ffa5743aba5c56c2e2d88c3a6be0804766867eafc3724a38b65cf3e1f6cae395c7a9a7f7f3c383e90eb8bbc18e1ae45aab100987b053090ac03e35b658e3933ff94aa7165462c46520faf5fdea372cdd8a111e31662a3bf42121d27dcd38caf086f4b79a7b008175fd51a939ff4e77642c562e07a6515120bfa83fc44a43ca98ef2468d949421df21fb25342fed2cea45b1e5b992272ec506cdce8b4d43c30f2d09b5330350bcd7988fce8d1246d607411ee5b00cf58f2304a363739a9fdc832d6f837d42ef9a632594cc0d47a6bfca2bc4c98a9a9f1ed814b7d637cdc287433ba8f04afffa3e59af3db9cb8219219da3cfcb02efc2b3aeb8f19167cb6fda90d60d9de1b270c7796404f23715ef164185e4a89da3f2d86a580732008e2aed741d7b52231c088597678ace3f6511ef6238ba48e6010076ad8da2fdbe20d130f903fcda5e1c9484934cd11009f89686615ec24520b8242196ac6044e516c7f59164f2b6d3823fae5db8609d3a3fc92b8c5b8d8d4462f3998f1ed621436564567a2a2f9f065ffadd7b8304df993051cb631bedcaff116cd418eb2e322bebea100abb72a86248986cb3321c22356518dc4fd9cd01e7031c18a09c167d5dfd1ad530302760d15a05378d2805f36412ca0746cafd4f9458c706dd67cdd980c819ea8f1193d3577fa71692493e71ba6172e60066d7c5d283637a3d1289c63c8406969156179a1801d29e103f582d5bc64a2778b952080d00a81a536117e8286115e81a19b0d0a14160e7bda929522c879aa39479de1564555c93d3f8958c5a59566f88c46980e42d1233f5d962fa2210f7c62092ecf70f175d8bc94021e97a59a85fd34717fe4ebd6d6cd2a634e62e0583ba4e7d237c07ee257750f63a9f525e3e60defcc9f745da059506b04fe42569b349b239921f9903cfdd45f17154a4b58fb00045cb3483c69070291184b245ede9a1b026a5a5191cef620398921241343375badf112c87ad5ae0994f474353cbdbf6c9f1b3c71edee95b42d905b62c79a46369b2347f6b3bb19c3c435368a1d7dc01d4aa7243fd027558dde8068e9695ec1de7621ea7d57982622db2b5c5dfabe49aae72cc633f72bd19e8c867cfc5cb96d08021ce0dcdb19c11054801bf296893d80372e7e829072462def57682179a217ee9bf10c8efee0ec141c393faf6178816a798af1f2841af3d60533af31ba7c19a278f16d40c28ceb60e9b206c33df44fddf50dda114f804559728ab0debbaab2e6a6a75b30dee2a9f455b1242add26edf310caf8cd11a16cd1d4d98db1e7594ca5827351dfdd65d101b40b36995c6ddc3f3c0d9e5020c8c3f184847115b79af821f200cd7fd142a83dcc0478b8674891239e003480091a16e3f7cb006d35503626dacfc691e64a15f368601affab3d7544ac8f88d491c89589b3ee30d5ece8004605f439e83ad8e0100cb9452477229c60a512de919071f6e2fbf268133239b50367a6b0efa8701812ca6aa25df542edf8c1587cd40c6c3947f43e59b52b85ce4a850376a12dc32d747bdc8f60f1fee9a1a2239cd3ad5dba247a1ff010b0ed56a970e438e8308d29ade283512ba0dbaa516f0dc26b494087a8f323dfdb27ea3ff09991250a8cc6c89fb383746dc3891a4f8f6bab964ce593cb96c9b47b971c867b14c2b669dadb71ad9c6f095ba4a42950446e14afac00dc45dbc2ef199ed8e44f8b0df72986b4974138bbd4a52c21a00d9e5d1f6a613e6606526342f92484003cc056c0fcaa746d47b7cd0220d6afb0e24cca68fe076528b064cc0465e108cfe18c29e96293e883042275be39108666c6f2e4b209eeccbd8053e095c8fcfdeab518043ea3a10eb87422f024cd08da258dbbd7ba5c474e0df5994e9805c2c351a65d2095d8ff0abb416e9e007fef9318e924129bba580793d4ff04596e061d2d7eeeaf7e78dbf312e2a777366b25726a9696eea9ea65d67abb5568537b100ab542e90a247d3c30d7bdb56ed7ac84d9f2983698965034bae3627b902030d4dfe51368e98f53ae57adea90df63cbbc727efe9d27783cfac332f9f2a4e5f3f71ea88606bb2564f0b5b03735f366998c0fb65b13bd638822d27ef54cc909215fdc40e23e6f2084eeb0bd0aae8145889ca2f4bfaa07ddb5000a304f1915fbca6b1c27a2e6089a48621a1ccf5f21a09db7f30e625da52b88a1b5e694bd17ed4a75ea2c083a41904bda2982cb812d678394227e14f6ecca142022e5e1d70e0890071a304ab860db43dd28b77c983e58020ffb2238955055ee30d9809f8bd058249a232f4e3c3d7da44b098d70431e4171f5419d9aecbbc3b69a2a9104840729233d939e71c2164d0a2fdcdbc29b79336a97aba41308c3f5380741b01423b5b266e93d37e9566052ae15b08369e630106a0826c7e4135f1bf25901136553bcacd23ab5522305163c3e042ee534e5c2fd95902419820db5593476cdb2c7b530d891e3f0cca2a6941b930b47f0715e795c50be7f019da5d185b153336276dd05bbd0b4bf391585d9eac5b04d54129810d8962d16517ad862688b990a6a7c0f54eb15bbb0f02be44bea0bb5dd634706e7a9e54c229d33126592c0fe8f4820fb9a02d3cd81cf3badcbe91e5c9029e31415b5d5a8c3cc8c188150f28aebf27f43b49f42c2cee0c0c243e19bf207641c34ec1b16162b4b2793f39e2f6f2aaf14c38818905e914716955fc95064930bab293eb24d2962e7871953a2a015e6482da92fe54f71eb07136fe617f9ce44a378840ca50ba45bb5462449e8a682f1e57da15775cacf36f73218eba3884551971ea1ff9c2af707d388f4195431ec6c5203ca08ecaef0a5098034d925dcf47467d42cbb821465adda7be0cb76d9f83e0c3a87326a53cf91b30fdaebde11f9647356c444d0baccd495c315f2be44e77aed1848fb98b0804c791fd8ed17968cc41f423317ca34ab858b304ef6f2cc9a8306e59e185708d05a8e822225be8d4ba83191cee382fd935a00079e2cee3a648c8033bff83308143256baf32c0fd17b60e60300487b00dc18dee3e70a0c7cc8f959c441a4a8a2418ed88edacdad9bdab6c033546b8371dabbb0a696e83e5b18a9406803b90246271561cce043d74be022505e3f350637ca4f4230046b425d916081bcb563bcd05f84a85904876b288adea78902cac325c75fc900a10c5c9d13068f0c4249c68c23758ab4ba563c4d06f39959490ddf2ff939b99ac30d586c4b5391a62f8e1420bcb16bff8843d748df8877230bfc4216ac257e0e1600d7538e564c0b66042d0fa6dc05a0543ef5d396e70fe6fd5de19afb4ae641009bbb8295188662d277c951fde8087b6ec027c2f952a5167c3546e2528e103ba4ec94697620200dfc8941ac8456ee136f4c60d993ab7c294735f8de09c09858bd901f3f45015c7226ce8b4c7b05b8fdb7545bfe106c0bbdbb63afd5b46da4b9b6328fe4fa72a9a04144c2f1962cde85a323f7225dcbfc5a29d269db35767f20062a735bb6377287799ab062f08c7954bde7ff2ec6cf96bd52fed6a5ac984619f3c50f01c38ae45dad4499fb68e1031b8aa7f4b82f27456753e415f1165f2aaf54bbc349d6720a57bdcaf46f83d8904bfdd4a24466c6f0196b1a255cc67200ffd1893b54421d3194aaae12fcba8fd5fa8ce5b84f86a9c9c2b7d56f9f07b3356324db95dca42ddebbfd96c72a7f8813f7b158f859c65948557056b89f19d48ab762d278145f8c5a0e68ee996a224811fab5bc815def380d9b4a7106ec3306e71bc616cfc5b8f6fdc3e9361448eb0b300a652890b94fc1fc81b9d80514ef91548e65a9ba0c252067b711175971cef44ea42598b479822522e768ca7ba3ede02f32cb02dff888aaca28283394508ef94645e1f7bf6c76980d9c1c454f758afd145f2e7c945a4771565ceaefe70274170ffdd5f5f38a2ddb1f341b1d34fb0403dfc9528ed5d53edafe15f0984c7fcc25a007606b44d6017a295bf83d3d0ce219ff430cc944ba901fe1cdfbc6226e001f7a7ff5aa25b15dd6ec7aabe96d46a606943c714458d80c4f14e32b5dd69c0a4490b114b578ae3cb63b90e61de9a642fbd71f3f9ab2fff44fcaeb78d3aa3b36e2cc2d5081b6eeeb98c4f549797b369648b2b021b3447cf89b63235ef28f80d352b4366b3f54825ba15deaac858f572647546e58436e8c64cb4ac2066b7594f8d6ae700825a19fb30c0d5f2ab8278a0de516f6c65f21a38365de12fdeb2c742bc4e67857203e4b0ae745fd4c66f80da9b5c03608c35db04048d620debfe2a583c6d5e6a946b0875636701a8c0c0ee8e7abab2d6ba78c0b6d8c835f8a7c3893249c272c85e87847e3653d587f2225de4339fd21144f8ddd45a3a8890c476a81b13b72f395ca5b24e9689e52fc53c6250b00170c6dd5b00b35b82a201f89628782093d9d6455a268381365e04a765816f6358d4d372a342059f6b901e86de072a2ea91fd127a35123499cac026f15e4612dc7f7114d5859b4142876c3b8107e8be9f79e19c46ac2c8cbb21c01b2e95ca85fd852986a1827d2f6160d7b6ea4534e6d84c79b52a6c4c69ebad19ce1365c607d045f490a8a29cb0e94d02eaa70bff4340bca9860d389db9dc05fdcbc972eda3d6031c5f6bf0993e8222548b114e05ca76517abf424b9e09110af726bca6bf83696726ae854351f783aabb657949ff7e93c0676a272bdb496da1c70f4efd8c97e12607ef6b1d423489f11e8fdd6e92f56bdfd347bb40f7b121ff2dfbd7f1fbea4e1a0a2e6ea82ca4c4959bafbe251399146f818cc7e08008a2a9b38dfe95f586fb28837876bb5573cb21875d433f261d02494789b18849d5b331551dbb57e4a3cff5818fb7ba7cab44e1a4f42347e6d699b0535057140e1c565e0abf385ad263d61b49e3fd8613fa24c424d27d8a06c925401673b45c3258ddb207da6e954a93bf485c0e6f4b63145609cd54b88c223ccfc1785861f2929a3cda462fc067e0eacad4f95647b9311bdba2d0d8d73747907679c274a8e6c3573bc94ee61639fdb874de2599abf0af3055705b13863cc633e7b21e7d6340518269d0a51c9ebc7477e9cc2aa303b152fce1617c2c5509d4ac023d2fdaa05a29fa415abb244140a87247d46f82fcb563fd44a3247ac5ec893dff908a2c630be725f03c99d24d19a9239c91700309abb3d982f1edaca6b683b140aeab7110f0d5b668f6da6224755fdb84db348fc47e5a75e0e360ed4c94fe4effd1fbf05b3083cd927bb932a80e8eb7c153f07c8a5753a8011725a93a4f514a854bf555a2090e6bb008aae0b4755df20dbead8b6046be886dbef05f38e95b8b5e3f9220f2acc1bcefb515b34e8bdd0c8e48fc9835a323ede4a59099701be98fef4c44f2a8c51f57a30db6cae0e2f878c51e089e1404aa5e9c13de110982354352de33b4c153a095680083773c1186fa2c0f642a71cf56b8fc6394fe93a9a8939a865d3d309c2ac0210dc42987c6a6c7a5eaab03029a281f956b0ced7738f383a04a05780101804a2e8ac9336d9cc8c020098cb823af74aa3f7deb2f401084a0c50922633d629e900c321071e19b79a9801d4680c6aaaec5c4563a2c6e235b9e3b942367385e19517564be11e408bea8948c0ae1158b5543587fa4d090ebadeafdd1b1e99aa50b99d0dc5f6e9ed9ab3247abb8e81b33a3238e8a97f053558314ba8887359a81856313948ef471e5d0ff3a2e7a5dd30f489213d95c263513fcb174e08ca5c0eeaaa18d7c90c992a183e76044f51d988bd7a9c7bd48321f4a8439a3164412752078e740f63d02ecd38f290db995978ccda1031835758a1c9d9713dcb5dffa8a8ff9c2bada3a7480b76570f50d7016a1fb335b482f82e1d0ab09cdfb84f009c77bcebfc8757d5498e3d6e8e41b84e2f1c0d666cecfb6b43a8a245156b52f0c3c0e0b023febab92c5f669ce5b4e9d8abaf182a0e58514876700b403d14ad2eca456e73185e536a8b2ad49f150a0be24da251c4813ee4f518f49c00bb35882c58ab64cf15d3a99d478430db02dc5cbb6496f7697f5f628415f1d8cb4c43a8b9788e4085254b28b82aee5090a5708fba060734c899f336e96fc382318f2b4a44f1828ec03fd31b7471103c87d13ebf2f5f9c6cfcd6aaa92bd83c6c1d17180a62850907207d482c5bf883310cbc039b173e45bed0742931896ba90585f685cc3dda8604477f0c615f893b1a51ab801400e42e0012291b9c5475d1d05f2028a5171c3b916f87cd98758531e8c3152be6d99bc15469e8ffa79826363017ecbd2fed0c3954fdddcd1614c692e2226503487abd706da76d85043d7ae62d87fb1b22285c90c9d2658aa23aabd62c2a19632b21aafab8f87de9dd78d030401d61d0e0717c017e4c9034b0c88249b44019ba521fc38498eae10325da06ab0bf2b0a54a58244ab0a0f5accf8bdd7cb7252cf51039e02b9673d45c8ea9cb09f68951aa534806996acd0330cd1b7780b95a05bc99758bcdf13ba94560f25f7f880e2dc76e882efec2b93c1dff16b59340799214e0b1f34b58eb06e2166a07a26175d7bad7ce8bc633a922656a400cb1ae556ec13ba5c90c01f7a3995825233db3e117c7b5556c76095e46a620d36ebb63f303cddc46ce454236a6b81b96854804c0994f27e4a0ef9566e8409f9993c7db2737f4cb589a70d8b1c2f2f26870e65cae6169765ed9bf607c7257c005ba671a00d328411960bf1955ba1c2d1dd776650ddfe9aba72f77438f279fd7c116a6ce17dd68273306ad3325c0b88c3ceda3a07763d9c5d133ddd9a6e48628b02c4488e978d15914ea1704bcab88d6b523db15c4a182ea78bc09715cd9893980c0e813ac5f820aa307ad0d6e51138c88f092c5a958aa04964a421593b2c3621190c817b11ceb088f65d02cdb1fe07881b826719154c46379751d171a34d9fe741b56b83b59a9fa58e0f26207ba68cce6ac54e8f4da84a0cb914baf6eeb1edf0cfe0ee601b1fa077f7c626943d7e501e20a6dfd6ab3bd8ab89142819096efea790d23b141991ba7bf1c067701c2cfa0ba20c75a32d01af89ca4509b781970b359b7731a91aba2374a10bb012336217d5418752785130768d028d07426f0518b7e65801fcc7da955eeaf3329050e4271ac27425d1530e9f281afb2ab6555531de9cd6a8a342a2a802205115db1812ed31d75a35ee8b112a7a184de69f4a1ab779ce2d558a8886fe8dbf08480e3a1b60cd5b8a8b17127c75d6272d9f5a141d7a52b241fb64c0917bb02f7c0d81acb607b7c054ae32ed77b1498fed3ec2d565a142e0e12dc1338de0a38cc8ec10d9e29fb1617a315029b55e271dc8d9406e5c8719e51b54d45cb2f66db06ae5196ad46676b71abec620d40bad2ecb0883e3f08bd1e5fc43a17a522c699784582ce9f33d674659880183b0cb7fee56930e62c49173c87407f1856d9ad94420521857a8445d8e41f0931f81dcc30b934a8848d1afc47b387ffb9ac8d2201d7d88ae0d7b4e0e709426ea685603d10851ff95cf562824dc380a5cbc558e5e466f0450a510de1631db533a127a0b12c2e031e2f2f52c6202ea120f971c768333294f10fdc11b0ac8350971d2b40576aec716c216bc91530beb5eab61a58c98821cd8395e4b6fe5bcefce826c85e0da278297ef566c2b2007009f72d96264ef664b482ce73bf05ac1351e643b7c334a74bac0f40df02b18230f993b4684216ef8e8952b25ce90ad6080e612a8bec8c9a9462e305d67309a84013f2a1bb80e38471c0a7f9005dbf31181ad8836dd71cfea8e504be8ab9b75c49f1752fc6a2178e8c8e9770dbc2342e796be5644c662076af425358dad93432f217ac2b1a76847e36e5b26c0f084ca8347799bd8c1f69db1ff6bf8377c49e2b98513ff11f2bd04d8e00557355cd6045e933eea799dcb804aebebaedf7f0d5502e4dd7259ba170585cfdd82cf571c70e0cbab187c41f699e00094188f47ef75b79d6af8aadbe10fb9b1a544e6058d83cfc3fb50c863093d1ac62850919afac705805786e2636ae51d0b14629a3fa5acf80a1321b539753f1bf8beaf84f2111dc50596a8b719939465c18b01a76abdf9d2926695d73cf45ec7c577d3dc58d3e8de453161bb1ae6c10108ba28d443869e2a2e61017ded30ff00a455015658e37d66f4601f826b388559e016d362e2e9ad927b269491972552349bcc217805e2e30e6d1fef6b84eedc18dce1560220c48c5cbfab8f4da917e15403b3e9dff94cebd6a4051cc5283f1f37fd8d8987ce4c71b5113f2e8081be470b4567cdacd6072207964646fa0b35722d259765c89f37005b8f7e9e7af447eeb796b5e59a7c8f4a63f9136ded3d266c8063625104bebd1d6742caeaa550f3a3b77b2d8a4ce24080046e5f2b2e93d35deb194b370b6ed14141da38bd3f1cdb2569ceb063dea5d9958b5f20a44e78c6848ac1a2c34c77766218a44ca410f0ae31381c322563d63602065f43c63df1721ba2b6e4da113b0b54ee5af8ea61ab251154af68b6967d444000cbe20c919f1ad9afedb3e9e1fdd0793f0ae11be578a57c73d279ec3aad1f109395d55534edb07f1be7129c8613ddfb02d974816da6f8e1b66f92e5e08f9b483d415010b31c84525b27ee00beba4f12bb633bbeffc21f089bdb9210a44e3399227ffb34583182997657026f6612f89c85aacb27fafd92b4335bd66afed6a31b038743cc912b932c6487f7d9e038116cbfdb259d4a0d2437b748a7e1e606c65ceb872c3a5584767d17db45e91fe9b07260ba230c2c40619b22a92f91995231eb019941424cd53399d96839451e134b9773f3d8bcdcc3f06e83458a5a0f01f270d42b4d00fa0e69b30b5bb52666e660d70543f6388a79a9a4606d10d3467615385574aa8b07b9b0486c54ebf3593792d49f1408b12c652c2e981db68d9aec07d8f710027f0a53cafb419c96453f5a2188cb7307ff68af6e56fe9c2038c94168c816ce4af15eda7380122486d060895119f3a21de520a4472091c12c5c54f59ad15472161a85800c6ff639b1cc098cf082e980d2631f30ddf77536524a720d3766ed3f4318ee086d0ef42cd66a32fc7c99b4c667e915f110f7bbff05ae9f000c3d0a18b93b738381f85f9d56b0ae49d76fc97e6381b38d6a15661b816d5935237d262d688b4528b5b204a89a4eab826ecac202187e266b7bf0b0d71398855996f8c3ba3d2ce2d21cd99af2c6f6d12c668394d5cf826b31f7a622ed617280b2539c4c35f3fd9c0ca11cd2c083cdee47a5aae141d19bc1be5ff98e17a34bef1c4efbe6d3c2205f0844ae86c9c02bf493a3c78036b90d7463f65685986e1070daccee9b853fc876937b68cbd055ac62d8fd70f7a64a36cb130733f4c9e54beb4519a00f7eed35c30072fdb5c1b28087f608ecdcf6c275c4ad09179cd8a2906e23551ca027165930c00c0950121038432dcc04d4231517aa8095d487bc740ba4d97af534a1a93ebc3fd8a30237c18768375c4e10980dd1454292a190e612772f7a7a632a66a139bba921f67aaf75b62a364a3eea4297a24ebcebbd8b65726b994234db8c74313ef4db43c0ec3b8b46d3b7e76a355475751f51b2603567fb6e9cd16537b85b99b88c643d53569be8ecf5b46271d288f8edf231efa4bd790e0be0168063e399d1014f297621ab7aeb14d679ddc92bc0dded2bcec286fd3d5aa9d3636ee0b25c849d02f2a63623a27e2e7467397a3f05bd01a55107a4185c4cca004c228c0f905e7e2f939c98192f7ebe1343b3e9d9f2dd6933c26a505ba78bd2e41d389d22abe7c99e0c3abf901563a1fa32de144d0b5491d584bf71c094a8321a6212c28232e9d4e89b06282c0b64fffc4459d996ce35f38236ec449dee19e26c94ef52c6b4e4482919ea694162a9c1c2e850e048fbf201b4a4df764890cc6e815309bcee496c1daec89dbd62f082c8a202dbc7fd0a7046a7554725161c7b78a40036d4be46f40eea149c52dd5b440845d653664e5a5c04b5459c5a2a9111641fb87f2a3140ec58c0e7e1e20460cc4eb4e2d9efe603bcf41e7a07ceebb9d809e59532d450afecc514da3fd6f1cdf16919cacd52491808c8b79439090e3eb8eb03e68848ff58929689862ea5b84be053b5b8f8c462550c18020d1c7250d681f7e703d93dbe5589fce6e26f38873fece57d3c05286220d22718d4f28abcd69b797d11976944dd44c1d95d19424ca7190183e14b7148882418c50d7eda529de5a3ff33a3c26ecf8708c29fe785fd4b622627489f02833ecc42e4e0101cf84781f6306d5c4f51f45e9cd529f357ff7a4617510b1fea8a85088ae019b8dbdc3c50f88454674e013afaebf0fd3c489758255e32646256cb13e6b0156e83293c62dd01ee76a280b9604e4eaddec52b0448c42eed8c54a010d4a1c44b267674780567cb31bcf636e2860d294a0bdea7b0cba7c5d29f88f77c5b9e94ea136ba4d94ba254d609eb659e78feb071e5d3ead02c4febe85c34e01368c8832857e2faff087e599064be6a1110209bbffbb7f2daaf76a0ef6e5315787291f8667eb5d58376e7c062bca17b9836e334fe70c171d25908ecebf2af2da6fc332cbcc5f1c452814c917ab162a0c2ba9f6168048000a10ef7ef31573c3a8b3da00ab78f4dbf592e72f306ee39b79e51ceacb61873dae2b115592792f41a4b6e7e4c365f1d26b9eb54b187270528eaf07ae41a5dbfb287c78f62ad8a0d21d06b53f9c18e35cdc5e13a316536b0a95a5d61c9ebe97ed6f18982468195c762a5894a468bfa5051fef1c5d0c404f448ee2dec17366164ec16fafbec94ed4828345d8c69ca0e14d3bdcd74112dc3c9620dfb38f726bbd7e9e3298176d8949f14bee1816028e230059a5156ba884cc2385d8d4addd56515551dd95bcea47c517d0b57ddea723321c4649a5f8961071a19b4899958726d25617e4f9c8414f3538a26f0aea0761400e451e02b2a786c6ebffac84bd0e0e67fe2f1c9af3232248bbb4c0dc4cadfea33ecbaee15287e1170d90858b4bed8c3a49154929d7186b56f6a38e409382cd031873fc928945461f7395266f64f628ef3bed0a4f6791cfcadc4716de80ed10faf860d32738153a6304e810b149206be3f4a52359571a417d4290067f6b3db79faa3d60be8a92bc36c532c586131dc4743fa9495e5642e63367f847646e53999db3b931fb1bb73e6f4253884cb9a1d3fa23b51d91ce26e66e68faf0e3f71da129cca65cf880f114ebc248771b833c207c42741611be4cf690e6e76fe98ea444b730a873f3b7e5cc4703efb78d4369c7716e368b8cd7b7447dacc44ceab4281cc75794943b68024fdffa39da848fa3243f08eec301429e1af92d812d1f9bfc9a230ead069b08747085a165f23e74de61cc6d8b2c358d49cab6a6d948642e9f7209df3e8afefa6090134bdded7d123b3a9aba227645a8e5d23f76680e34ba17e1aa86b20788df1b4f20db233cd51e0f43b532b71efbbedba4fa7026682cad493e670ba9e0219c062edfa1317a383333d3d66afceb42505a7b187c2c1aebad3da07bd469c5da3c2e91d8222c7ce138a09e3f7ab16efeb27e314b3256cfba1129ff6303ab7b0066ebe740d65d11fa18c8a0a9b9d07b0293341bd1dea74928b0f7dc5b12d063af5d69a0907d8ed3b11e280fe5242ced362cd2ae16fe53d919f18be3fc54565a874247551cdadceeaa7128eead45125a41a4c6bdd57cee39f8655b5079ed7548ef57d7184f8b24d45c458c1e32e62c8192e12ff92663f75c1090cb9ce0672009755ebe68a1bb7442e069f880bcfe8dc49efedb7d093a42ee5baffb82be8b179955969ed16a866acf6027d4d5fee26ffb420408bd1764ceeddbef71c8f2703df5dc7254136d9a1210a7b88a2b25c3f6f9f808f6f52fb07e6711ed33a1a41279a3999fa829acce9daa87d504eb9368daca2b0c665068116a3fc00f9ed0e60eb976c3f13c563adffa63a11dd9711f47fb4ec457c90a5473de3321cc5fdcda40fd0cfb81c47e51e66e2e3a93356c6c939b799f871c20995e7244e63267f9074424b3908c73c131f449cf0520ec671ce840f904fa81407719e33e903c91352c2c1395049cc621f8dd2ffa362637b86d8459d4e667166199973354c9353fd7dcedea608347db2649fabfc20ac99b06d3e10edf2ea49878522e96c3592ce13fd60085a41dbe5d02a2acfa9323111e1679f3aa24359c0889f90acac385ce18b5691b14834c87890ae038e79d36949a9c60d2cd3190136bdb2d0306d6af2d4f0171104e9188b0c7513e01e7e8b9363f32999fe51da560b0977884de2bd6493f129fd427ee9a1f232dda09d9d0a0dc47d8d2eab141b35952dac590cae870899ea172b57bc3785fe33de8cb7bb9d0a680e3d587d573743a41e9f9724188511ecbe12c480bdd68575f2904646f6b107e7eab03e6422e85ef6a7c103299ff2b9e7f792bcbc712936c20ce78578d872d062b0d86e4bf45aec7fe77de0546e7897de3870f1a4861b0df07b037dc2c07be1010d402577bb96c686d90e6ddfd6c3c7f62e11c9f5755216e45892b2548ef523bc2bfea9cfd8e170ad97ba560285e93816af74abf64bfd5a6d0fc7e2898c9bfcadfd8336ea70c4ea84581d17bb8f4bf818dfdc5110e5cc3e695bbd8c46b9d11892a24d3e2f3a46e154dd76f96711c199109bf5932e60c96e020c9a915b8ed332a69ea681def7082c41fb3d071ade4387af4d3f39dad7f8c3835a5efcc3cd4b811428ec0c810a9475666014f43bf381cd495337f392d8d7159d416c76d5ce4cbcba4a6784889274de935d4e70d0a399265050b5990329a8d90c050627696dec1274013c1ccdf4d9f680df8cfdee0ce4cdc43e38e92514bacbb8e17818f96b72d29c01cccf3ae901cc0fd63292232824cdc4cb03777fac225d1ab8fb599919c07cca42497cbc60d466ab96f6900798bf69cd935b4e7ea415491e1d313e7aca5af249d24dab3add2bb4933afdd1fca65f32c522f0352d97e4ee28f153390445dd081a7549ffbc319cfef3e3b3e0664aa376d12e9fbcdad28ae07ac1fe88c05f75073474ab1f3f6740dcca787b08c83a5ac8760894b141d26d05d05276f593f508102bf56fc0faeb3a08f311a40418871eb4264ea27c6dc2bf4b847a62db541df9b876c61d32b69c3a325f011960d291b74c3e93e186ed8f471090e76ba63c4f81bf63ffe7b4a168b135bae257bceaeb7f71a724c4da88a4bf01a46edf5e1feeb2f4e8a70eb4ef7873c1cfa81b774730c67af9acf1f661b6d1af16394404261cfc5c1be1f8ba4417f73f7536725c7f8e5aa1fbed70ecf91eed7c375617f8cfec6e97eca18341a01287c25fa49455dd4a3f7ce0165ec7bf9751c34bd1d8928491bba706f2ab2079759cd8f1d12b70dec16398306c2bb37491ec5377a1c379faa817badf0f61cfebd54ef6b1bae1e986be78c91e2a18052a7528fc472a4965bc76e507e9d14dfd7919053895ed214b4864fcd463b29af86cb5aef61aa44b55cc01fe844ddfa7ddc2bfd188fe1fb814b77a6616b1debc7934effbe5b42c2f25b7e12f321f28fe776b965a401752d93fb264ab81e272c9b83f6f36ea70d383bd857be8601071d887b63229c5380abba249128cbb31cc8e2766d182b080d2e464eccfa52055331b5cae30dcb8e95b14542658a316387790e50868f4eb69dc3290901ee1608067ff58587eb92e35eb471a8ab87c81e0a15e2a631264262afaaca72e3242b0079958f12a01f0a7280eb8af5cf201102ceb909a520b3ad8f92e933d82560859dcfa2a036069c3e41d7e3c99a29947705d82e9b9cb00056f07907738d1ac259f1f9d2aac1f67729694247ffca6987644eb11ca583d8a1371a5038921056fdbf25b2c42071d2f0983d3c3d987e4e233144815076b8c6af0ded6fc2a513289a47cd1d554aa7758c7fff089fa6f56da14b7b2e358288eb6c28d98bc4ab27f6d634c8f271c8a098a34542b2e3e59bcd365851604b55805a5b09ed2d7636ba3507c039967aba755eb7fcc301d152313044fd1a891da04ffa0445219b8e1ae525c473aa86961c497aace11bfacdf8299df314e157443c0d1a8507cde63f381161ec7bfb0b002ffc241af156658ee264cd79fafbd5581996f70ffa47ddc1854dbfbfa050f78e8d78841a96270a32ff8365a6ab302291d7e869dd3b1d6c7601c56184a85976cdfc18a1fd702b1a38723521b4ff079c34a1823d2163084d5b41ce5e9ec17a92fe5a89d8476d605f48cc491d4da87009f1420c347ff414346cb5a190ec3c54d12781002506e07aeb90f9cf56f86d1fd4c4c5c9c911081e445f72f0fdfc04e0a9411e99f855491a1bed6b092be56953703ae45432918cb9515cdef481b24f31f2982a2bf7ca4702d51a1dca1cc5831dea9dab25bf797f7e0d80e413d228528b42432ab35d53c35cf97d4fa3462847e7463e5c3f22ecd7a5f0fcea9f3f22c62baa1099aa9e5ca13d180c6dc071770580492026d9e4911ca77fbdee16d2f27996cd4106b570818c27aaad8a8d307bac59b26f9b157ad7d38a4c0d28b0aa3a237904a149516fec5507f3d623dbd677df2da9d6e6fe1e71a46800084b18681db72ab1a352ad64d44ba4973dc0dfacb80d84c30acd43ba216c85c9834fdbe5a2d57207de396ee817e5d4b93a4dd281412059efea767109c4b16e674ad94ae16380ca6b1129d4e41f2b6415d3d11ffdd1845f6c50231e09ed3177aaa518ac4a4de7be3797dd68e7a496dbe62e535a9704b565629c21754cfb2f16f02f5a870e5d20768843027a40f00d6b008bc9f8c513f586d14c4541154309aa8f60c17535831c8a80a5acae4b5a4e22bdfe43ae4b51ee1341ad34aa51433fd2c95183449fa6b7e84ef2b81ce3d9e91b46e53441c76024f04818b52630c8976bd200109b11a559ead04e67a82810a7692d5a072cb0602fbb3266abdec54fcb392e295d4463d77a10a954ce81c7fb08481ee19f91432ad091215171cea58d45e49bbcbd786bdd23147338de47ae81de9c0400e14b00d99608235c2669de26ab6a31bc9c40688042b654b8590eb4e2844d2d464d83d8e2ea635b20e792b98270b6e1e43fd144e6674aa49405693d3a439165469c820e04449a08d7bb0076af2e2f6d4be797767040fa60b0bb071084176cbb5bb3b9bb58ed8251108c4c3d57d623a04901d66306d92fa12253f76197ef1aa4b147edb04d574f94189cd3be768e5e8098300d5ed05f59050fa6ef4def15504e82e03af3aa0c87ac7030bdf2b5a1f3c76c3c1b8294b7efb2f0427cd6f074bf780c0a720d6ca5363742697b134ab74888dc92751950570d5a439ddc2b4219a3b20dc08bca8040eae04660f591058c86ad201680df19b76216b9ea56ee87707924b8f027d0593c20e11acf49cb6d9ea0ac2cb7d9529e5997ee8ece122437a07e3fd2f07666460177e884ed49d311b8e2f359f92e57b78d4ef600ce946885a238410b2e5de019c0e870e5610e30d4728f8eb2c1138d599790afe8ebf0cc33d9633dd59a11762685a2880adb5e60f42b8ebb3cfae7fc21405a3b27e48b07608f86ae6f1bbdd876b7ff69e49bbae6d0ad7ead50fd7be0a36b41fadb59dc964c47ddba180d5219050bfbef0e012dac5c2eaccbaeaab15edaa67b1ca59b5a2ab52bda2cd6a8512ca4ca33e22fa95cf29fdca3674d7990dc3b2a4c79d9994fa502fb1301b9c724ddad067bd0fac003158020b86e0451318909d1ad8e2082a982801133f90d5fb007957aa6843aa351d3cdecee702b5a89da11629a8525b6441291d6fb26eed77e6c4843499a51a32647066b6f0983c1ca638de92165e64726ac43c27019e165ee705c6bc6358435a99342f657df66a5bfc0bbfc5c7b3b3c5165e023c2dbcdb169e0df7d4e89ab01556d42694f5d6da1c09f0b4f07a7424c0d3c2cbe1a301b15569506a858ded28a59f940f099f3703fc404a69682ba5d4e2ec927579c05a6bedf7c988aa98511c439b8d0925393201664596e3a76495561f13482b90ba3197982e9531945299948cb14e21ce8034236be6eb6a28edaced9db5d6da9ad1d6caaeebba8eb5844ac3a65b346e5af42d10a90ecc3bbd778bb4ab02f5594d189c5066fa05aeb7a15fd61442d2af3693bc9f9ef964459f3cbaee40427dd6ab50f2c0423c4e1e40fdf25e2fd47515775d9f3a61502007504988c04909a31b184a48d0e204961b00a1b32861c030a5123f92abd3e3e897501dfbf4b42faf0e3ded61ee494ffbd5bda1a73d7959f4b41fef0c3dedc55bd2d35e75477adaa76e483b7a7e3af8fb0d6ba61c3dafebcca4181e313d9a8006658417554c6082121c91a3312d71399a81ac9ed687655b18d4dbd51dd4b65faf3aef03c3d49822c20dd3abaec0f42a25da432f8ee44cb922893a22849e0dd353240f89e84a87bd5a1943129544902942993d253c43338324aaa1a9a9a999912153531353d6d4d4c0d4acc89a9a9a14c22088224c598580443e6c2db5d6ce307dd9819e6de6a954bac9d18bb64aa0b46d574270c2acb11c82ad9440993b1872c80d96212323a6942143860c1936a92f9eb0f7dd48664f3fb3c81112f5aa2351cd22b2b48cf4eb8bfa35bc91cc2c126fcd29a22783545259eae0bd15f955ea45f5a914684e016fbf5aad562569f6c72b5e15f68bbab72d1a2f9a53fae3ab7d51bd85fa4c27d6e5dd3e7c18bebe28fcc29a8f596bd6e5b539c5f35eab3965e8de55e5bd293e50040758a0369b3dc9c8a88cca64a428656454567f040d7401811510d0801740905199f7213b5e2a9385ef4fc95264c3db7baf66ca9c5271d6e5bdbefbd69df55e9baccbf33c1b1a75832a686a8feb6b12cab064d1b046c0fb6eb37755e8bcaeeb3aaf7a1dae754ba300a66bd8d1d7a7593c1fdabb9d0c14659fe2aeab8c924da46dd728cc4a4f4f01b88978b88974b327df9fe8f00e8585bb29b85f9b48cf707d359f7c8712e24a7bbc7b990aeed7182cfeec10dd5da5ddcab3b5eb6a79c3dab0bf8de7ee3a7d9eddf6c673d7d9ce763685c3e02e07772bdcd5c01d893b1cdc8db8bbc19d88bb16ee54b8a381bb14ee6c704703e2ae06771fee66e0cec31d0deeba19dc59dcc9c0dd9bc6c07731b893c13416be7b496336efcc9cc2cf8b29dcd3e6e0eeef5777de76295c8abb3bce6db26e5c1b77a6cf535c0964bdf2e8ab550dea971968c6de0a617b1b5257675a20201d14ccfb9c1a38372d1a36ac9a19345de386e005ae65f7191932dd63ba97dd61baafba936377b17baaebba7fd7ebec8d9a4a1abbc644a80c3020830c8c3210a98e780aa3b8872df4595fa4cf7a18b6e07ab15cd2a778975893873eeb99743548e5dddaa7becb64890fca8f87ebb02a25fdca43a8ee4775582121063168064125e5d11eebe1c6a1d9024f714b48c5a0d9d11e6b22e91357524887bd579248873d5389e5610913d16432626bb688b4357f18b1923e5fa833dce6185230703d9286c1d08285010175398404802083183c610a21e840567f5ad80c4fb0000a1ba4008ba324c8ea4b0b33c2e24b174948d8028c309a20ab4248620b0a3860042582a0029977a3de37627b6b662642df0bd6076bb688d4f0e0bb874c843cdcf5d95f39d3e71865bf91f86e1e2dc32ba9418d04ee7b483416c3fd01542a06a54cf83e646129b36479f62a13ec30ec937edf052f7eb64fc9c2ca97df656e24c3a17ccc8b602e03f3cf2c2a1ff31823d9eae4fbb3f5d9ffaef8befacace884ace5062959914f3ef7e56d82cecabc57cb6afd667ffabe1fe37eb7fb4be07d5f7a2fae53dcccb77640ce3551747d5079ec988c5212aee4473fc77b30ffeae5221c1df77fc7dfc6ec6814792e8eb5aaca990240a4d21a02394d50865f6a070df83ea57172fea2349a26f8c7ac10beedb7e852a337d0abf9bd8aa0ae04aa0a4015c96272272e8960d48004761d96e099bb5e050536adf9dde1f80b00fe17b5652dbb09409bebbdec39bcf8ca3bbbda079a919d2686c76e402118b0e4a51801b08365b45684f1f670aa328404116b87e2933ed02858829d6565bb70491ea3001f2b8a0f4523a6681339311e3e8b3be4d9d0694e5ed0d6ffad566c76ad347cb0cdbd0c276bc571ed7456fbc293b2750fa24a297386159d705eaed2bb8f3aab4055b1e0f1c47fa8177f5bdbb6eed422b73ed825de74f93962e33b78ac4e08a5b550bedb1f7b08df23c25896a54973eedcfe7eeeeddaf5e8e5d87c19d837b85bb066e12370eee1bdc22ee166e156e1ab86d703fa4b1f0ac67d6cc33a8f35c83fbc36de379066e0ff7334d2cf59ec16d713fcbc0fda631d55b0677bfd298f87e4963376f33bb5cb77ab929dcf5dae0910165a65a84ac709024b4607bb17649914494e0eeb54b54bf3c2f2049e4b92ec55f9b15eabc950b12396c9fc20d1b3a2898f73935706e5a346c58bd846bd9afe9cfe8d3f467fa32fa32fd9812a6bfea93fdb12ff655fd543f6c15f00057250b5b625df66158962cd67f9e3eb92ad15c55a2312a6343e202d81ed11e18b6ef7c40c081dcb85e80c6bcdb2fb1301cf5160485552ca8cb1e0868c70e97eb5e33dbd8c4e05c9532cd1969190eb32a59977ded7254562f35aacf3ea2ccd4a90bb6b7a17fd02ee3b93d921a344489fbf63af09ce799ea00db9dfbece0e401b697ed3cd31eb0c2288708719b493bcff407423b6608046ccf435e6a02a622609e6b23dbb954268ee3cea5329e9ba52f961ed0028bed010a60d18e5160191e58006ef575e017aeb7d566cdc2822c2080290bb2f8028b5f16a62d1d95c586db84d2b866a13df6325f603135f61d711a9879005691d51cc7beaada0ddb2c46714557ab7a05edb12771bd94051d38028babd1bbd7b927cfc5d12efb9d4bfba7be1750fe6c1895d9535389c63e6c5f9bfaf59de776cbce735dc2f6621923634719dd38e38d34df388384a1c6ab4a469414ef9cc72ea131fb03d019a54ba046c05689e7ee5c98b15ff1079a6755ead715371aa3d8be1fd6201a46adbd5b2f407bec6148211fee7e0c60eb050c04d8560e60fb2689aa12edb1af5fcabe12595950da53ec99f59f79de6ad7b314fac608224da1a44e5884f98e858adf15d2e125b4c706018f7dce601b688f3df568ad765504fdad2c28e92bb66f5cad1132fab006dda8617a4b9d70ed867c9852332300534c04a6098f7ddab6ef3739a40aa9d8a3dd2bcfa421eda1519f505292c86640a4de773310fef289471c78f4c1e3f3f1f8ccaa2f19f41c4f2d6cf55ce2f13126cb5ad6d932e88b879e3603e1f0e2591676daf37478bc1dbfbaa399bf57d2a4b4274c7da348c2308e63ea29d13cfb0caf0afbedfd7a2613f205192b52f004ee1053d2476caf0421eb372cd67fc362dddc36ebe5d88077f6e9d3f4ca238acb9267ba2645ad7d8103100849b4eb28384cbba8a47c48f8ac08c4ee5b8f6bd22dac33cb76d5ae3028fbd5309d3ce25c0ab1b8cd53a0cc3c703d8f7ee510a8eb49afce6d50d6ebf4cbbe56ca01280ca56f0053bbc2c4ee9ec0043a1b8cadd2aee255796955544b204a15652b9f74301e8c0dc66661de9576590f4b47a0ccaa285594ca4bc35456802a9b04ca279d0ddb08944fba24f1350b134d30c4d6ae3295f01d0e8fedc78689b72d5edaae0ec33749e441a0ac0f431ae699172ceecc24f1f58ea185a9a2b22aca68045930b1c45481a513f42088131e0001135734598089cf48acb6b26229c3571c9a7900ddcd0b8f1b02650dc6262a554c039c55362c707d77a3b1e36a05ce23ae3012c0f659c4f5291a040301aac3beaa5e53304a7d3616181b8c0d7bb47ed13213af86c37bb66ea45eed7a2117573385a33df62a241ace612d06da03dc79355cff3dbcb9e2f0956c2aca1cde63e2d5700763058c0dc686ed431c8efd4addbe5ba2b1637b189b85d5198c52bf1e5066af866d6e2cb000088cb8428b0a64f69e92850900a90a1cc840055834a1019951084cf0846d8c1a0f24d00399bd87646136d460065acc96d0850460c8ec45dba367c7af1bc16f4c913080a40b26a832536017e561c1f66247e9099a49e2bd2b350b0b4593b6ad614b8a3395973e6daba2b085b9852d3304314b9123fbfe38254b910803031ee62ab368f5d19c4257174fb6283e4bd1cced6b7dbfa8bebb88e6a339653c8df972c604df5d98977775f24ec95294a565a43bcc8b3ad5556651f99539458975812f5f5f4dd19c327498c39853b214c1dc9a538a58177818935a90a6c45bd3c7bac04f214fad2b8c29632ec347c6a94ce654067ec89297cac44b65dda5b212bc8c3b254b1119734a95c1bc7ccc2dc2c965a4284d18b3ceac0b042f8354c4942c00a032af338bf04e65b9ce30b8ba54461e1539fa8e449579b89ba5a8deac0b7c7dd79953b2147536eb02df99b829598aeaad39a5be9a536acdbac05b935a1b065f6b5b07d6531978f1529968d4f57784caaad64c3dbc79f50cf3ac7a1e9f4971d5adba2e87ff2ec294e5614a13af54abd555ab8e1cc999f156ac12c57ba24af54f65d23eed2b1eb296c989bb8be1cd1fb69de7f5d891449d996dd799ddd852802325adb5de459024f22caea719a47dd68e76a1b5244c8cccd85058178d39b5309919b68676971629ad8bbe59b735f60d4e8d32e7a26a6ca4b2a43d776c0798230a3ad67ab7b73f1b635fa9d67a3752a0d4e2c6e239d27ed5aecfce6c29c27ea2dbd9e1e1b13c2c4bc3da789eb5b136d6c65a1a3468dcd01c14c75a8a4371280ea55ecd31d27ed52e878e71c70e156650d6772ad441374912b4a75fe42661000e6839dc96853789114916407bfa3f6e9223684fdfc74d72447b72b85536c395e126d17c64471ec34da201c98e1c86178edc853b4488532772872871ea2ddc215866124a57c8140751e07e123e324a77648a0307e07e1227e84f0f790e45370928b3e10e3193cc684f43cdf88ce7108827209c80d80284121057007103220b209610b0829af711c054c37a9fb2cefa0d2924880944d396d206c9a559ea9f3e0d9706675d7dc33fcf1407b332761c92283c0e39a413fa8a1ce2c34482fb719fcc68cf11d671a3b9a24f25dad31fc0cdc7e743260f6e388721b762167cc4783cfb20ed40a23d56d0d868b0b85c392339430f5d24857458e8e18dfd3cf3d4f00c9f4297a6467bfab14b83850669e8d25c81aa4194eab0f1b6416760633c7271308f8efc082a33190e34d0709c10d05700aaa04bfd0a800d0767614772210e2ee338e1be906bc489f6f46fb8469aacab4f748d7cd1673fc835c2d4679f856bc4d6675f768d085d234a7df6f13512802a6c006c30ef1fc97559a6cc1cb6122e6fc395e14986a02b03973efb355c19a0acabff21d3c82d06e591fb644677b40a4597865b22ddfcb825adcfbe8f5b56e933bce511905b1a417b90acab6fc32d6dfdd3ff71938aeea3c81475c65a1e471205dd186b11cafcdbce18bba98b35dc23606ab8683d5b7956a91aee71d6d50fbae7c235b69863ec2187ece0d403708fb208fd082701b828741fbb35b098638c5d1ad892434e9cfa91fb5bffc4ee0daee136396a5707994d66302855ffba52db856f1c9a39003865261d390db7a45957bfe89635ebeac76e0bffb6e33b9e5976c7ed88b9e339a4c1a34ad9f0b06a7842906646c6e775258f6d5b6156941cc59b558fdfbc9cd1588f2e70ae18e8d9f302f75340af37c3fef53029ed0132f3ebdf2fc5af4bf1cfcd218f9bcb15ee930ee752859bbfe3661a25dcf7b999e602b8afe3669a2c703fc7cd63cfcd3448b88fe3e690e7e6efdcdc60b86ea69902f7cf9b696cb89fc2cdac1b373fd338917bcb939acfdc61e43206b97ce60ee349c5b99ce17eebe692c6cdb4085fe413678a8359a645e002f76b6ea645d801eecfb89916212af7964c5fb003dc975103a8dc5b626ea6602c21b3722f6175736fc199b6e029d3221801f7c79b2fdedc60e04c8730c37dd56fb8bff0d6cc4485c3d534bb1fbe1bee4c262a1cb62655026964a40b904427f64d8f86ebeb15a57dedceab4cbc5a17af5651008423af767bd1c64851d6dbd9aceb6e4450504dc600e227dcdd6d6bd812596b3b72c8d7b8c6755de791433cef238774d5863f901c02d384439228ac1f92732bc47688379bc56429bb3e2d3924a4b5edaabb6776b736419baa0977d7755d05ea13a85dfd168898cd660bc09d29d4afead1e82b7cd7bdbb99f50a016129eb330f1e7df6d71e5388a3f3e9deb652ec3cda2b3b000c9aa928dad3ef177458e0ec31611e4042b59a5e67b688d8d71fbc1af6e9b36918d89a1d0e1c0dbb695787edeaab9abc16d4a61ba30dd650a3701bcff5415a4001059dd7b7ce6f3cd7aa739dfb503fd442bf6af80c836b0eae2b5c6be04ae28a83eb0dae2a5c69e09ac2d50683b8d6e0fae13a03d74a836bb5b869cc7b8dc15506d7fabdd6bcb670de20f54341fa2d741d6aa1a260ea98337db60d9d0694d907b74fbf9a360109f52ba3f0aaf3219df75bd0316d7994a59a679f8d8289a39b4a9f16fa050443d6c17d1f6b7a3ae67d4e0d9c9b160d1b56cd0c9a19197526035ccb7a9998fa12a67e45d68fa2aa3e157eb7deaeb7f506031c1aa5505330f8da76f7bd3fcfebbcafab5fa5e207862955155df8aa522128bec3e276739dfe4019fb811f188617044122c07fdf876cd7755d7829df4b2cda91f6789ff53c0f14c391d6b69dd71f387637db775eae6377c5d4d8795df789a1edc2d07eefbe303563c390be53594b555445559476a22802219555c49d19365269474ae98a01624351d66f55a96be535218a63df5cadbdd6daeefe486db54874777fdfe779dff77ddff77ddfbbaed556c05a6b6bada9063db07d64157cbdd43b65f534960c5fda65d5abb57ea8adfd58ced4004496c23e74240e3d93517bd152db85ed23a3e1e9054f69a78cc6925dda65f4b4ebdaf443ed4521b603da0130dd1522d5a132b5b430f02d03fbe0819716ca520faf099e16ca5466f8d18064e155f7288950169e464478f07b102c6cc919402c040cf61e0488ade983f75d0af8128b6d5a994ee57d1dd881d6be9a35d82f5f443b7e20587e4f74fd2521ec6e4c569f7d5a49cddc2a42adabded09e3ad4f48ef68892fe0362aeddb46d63ebadaa9542a43a95c495c439c431880102da43711e5f3188a1053d29ee256c01010d68b14414616c9135494bb24a11bfafbec5dbd730da31f5ef956661e13ff249aa93a6ea539dcac2d4c9d7108627c31741d62af8bb18a6561fea549d599893767dff5629277d660a85bfeffb48f32bc2b2aeb2cfcf2c224bf722ecc5b759442ecd96f7b0a83f9a2dcf2ca27aaa6fcbfb28debe687c5f7ccb88bdf8a2fe784a7657bcd4d61294b4cefafc6e5ddf5397655ddf1265a5f5f91dfc5e8fca5a6b600f78c08b5e9de9aaedae1d4dd5b041fb759e57c31c83eb58d29efa9b7e75b857e0440e4880edbdafad949d0aa7c0990fa7f0978bc09f59d21e0f9b39c441d42f334d9479a6cfcaa23d75e6bf19290ce807610e0ec38359ed6a1ecc84426ee1faef5906ae17bfd71884808b41085b5017c5f51e0ff63c2898d29e6f48f5fecdccccb0700c608c4175501c031846a027c5df3dd06c3dc4e067a6684f3d0bc030428969ad35a40d2bbfe08f0ce8967216a999a270550a9f7065834fd87637db2d2dd5b0fd538ff5b3b5a71c88c3f6d66b9cad59248b0485ed1b673ba8c6355333d9b03d94ad36a8a828dcb75d6db62a55257b1a5fa54e37dc9920499b30a5f5e1d308824fa4fdf23a100471984aa5cc20400c662ab8fb70384369fcddeb2e0758300b724bb89528fb9624d220a83abc5b7b7ba9ea68d3bb0d82aaa36f1b4665f56dbb695fefb5ec7bb7defa6c22b0f7beb83e9b48886de3b211f6ec830031d10dfb24a20fab970331a4dd430aa31ea53456434cad8b7618b258dd1907d3eaddd01e6a5ffdaeb3b69b422c71b70cdc2d7b1df885fbb6ac59694f15c1154b960b30b07d8be008dbd0de11b7bd32b8ed9dc1ddf6aa707725711b75ed1c9020085e6e576c60890815caae0c7359b27a85eb3f0afb280deaaa8e7adb6789295972a0e959373406dac83d98fe607a1c0abb4c752b75794fa5b01723bb850d59977d183ee76003f7872c8ec8f19715d0d5da7dfae47567c4676e5833333737ac9919d6cdcc0d1277804875c453b36524f5f045296a52eb328b545352579946b26a66281da6afe00d4a96859dd6557f9f3087d8070765f8d473883b5b61aa2e2566ca347483f4d9f7c02841f39f892315d2a6afed0944ef54fcea9db1307b7a5aef258d89d80bc2de5916664befd6f7feb36479370b13cd9498baea35f445b308fbbeca2c224b11e153a7b2ef5da73e334925ebda5e56d94c94dd2bb65fcb887dbf28f5f0d6a7c7c525a2fa50b7175e2af36e16e6d9549ecd7be8d9faf4ee79ff59b252b7f5fdb63eb3f5bdc83ef5a27e78fb81adcf34629f3a0ef62913877e6896a0a4b87bf570b6c325885427c4f43d5e128bded8d7068b762cb5a0803746018697f6d89453181ca071e076c52fc6b1c9b1af8c0656e558ef19034361564994f5de3d71d7cafc5b984ae8b4df930865a9d7c8a06cc109b03dad3fb0f5818f6840b2d4930865e093f09185179f4b2dde092aac0619e1ecb5e0046a1775d19fb006fcc1150bdd155699af1ece63f2b4dcbbd19e450d6a338956ca52b7f7b4484aa59ebd0f845ba89e55387bb7ec85800b4f04d54c523d7b23503d7b5d5c5b6355f8aad4908abb7a9dd3f3bc9497c262555d1b596a4c65f12e7a75f4da8e5ecbf3ccf6a0bc7858d8b08705539419184ae985a3971a3dd55859d08107e0ee33f8bb288ede38d6dbddbb1f49576d7adfb51755a355616b0f647fe2fc13dba76e55b7e393c48fa668ad6dd58b3792a96eab9aa239657cea53c4ab6e242349a09fe33dadcb5e041fdeaa9e7a91ea29734a96fa29e055af6651ea53c2370d7bd94b5d5ac5cbeaf5b488d65a1fa09f26b6efc15261c7d29e83f7f5bcd7d4350d9448ed29ec00d9e6009e995338d39c35abac33b30d26bf8b5f509aa4caa918b32b55b314132729264e96e49c2cc9657102f4a438830f48dd24274cc49c0adb3b01534b98c0e05ab1f479185f2ce4a4a06713a338d2e0199998264c26edeafa45118d51d812ebeaaf60ca1819193358427389926e0553c6c8c89801471a1206520814277dd67be1b377247db60d4a6ce8b3bb0f43bf2e9429ca4c9db8e0be122f88855c959085dc1175516ccdec3dbce85d254828ced4e9aff07d252a1b9034ac5a4fa684c1f526d12e539d89937e555a82dc91ea3924edead76b43957249c354ef3b51dd254b649a5b9531323248186646901c32624a7bbc7795f6cabed589a1f79cf27ca8cb3bad6d3b2f87a118aa56990a062f8ee4908a433343c1a049698f67e6108b2269bf5a62bd25d633739d5933d3a8ce6c000c9080eb3b1ae28e1cbf4be411d9ee29af5cb2a45ff69dcbe57ea8302aebe7ae26f1042ccb3409b8cd1696c8d193e26ab6b00417aa83e2dce16ad2f2c3b4a7da58200c4cc95bc182022d4e4a68d182232038601283892c4f78e124a334f0c2011a0f38e0041e3071838b2b52f0012d54200610aa103e63ad65cdccdc84e293b84cf5d0de1b96cd125e75b37c17bfa7bea7be535a7e0743229fd9991d498150563cc3bae9c817a80c3020030c746177468853745d67bbeb68d8afca6285e19f533b4bab5dd77da813e069e1515514ddd9759dd7751d092c8df6d0d72eca7a6aad145dd7750ec09ed94d33f0baaeb36996ade96678e1fb34df0c28239449c5a84a11665c91e46a84114b5518ca84a18c309c09439a309c11863561c8b209431a614863654fc1af69745d67bbaeebaeebbaaeeb7a5079211443d35285d0964f2cfb9ce91768f669f80c84ed0e6c5de5f3c5f63614c6b2aebe1547f22b98dbb3682ce6e0676a5409f6d92c1b96660e532cd690edd453a9f7c07608fe7e5c5eec68ed558cc9a23dd6a634b30160ee0c3d594b56b79c6142deb1741259ea029a10aaa1ddd05a4caddbbdf7de7baf699a28bca6a6c60903915499b8b843e5265954ce663393044027f2a430d6286e2c675d312a3756afdcd817ed22b1143306460c876f6a1354fa35e5fda05a10521096205b90154204dd829682a8c0fda029625c1a769445caade4a45c1e32339f9a194c336e3838383838383838356ad4c8b9cc4c86265393416ac240bc66e5c640592962ba34ec284bf86ac58c81ea970a87a932cf7c62250bab4dd4082c595865220781261b1b1b1b1b1b1b1b1a3468b45ea758da19ebeca270176844a074e8c03366cc983163c68c9a9a1ad68970968889a889c8c9c2aa959e226e832412c2c8a8885392c8e8432da6d038629e8e6897d528c2c784d1b098a77e09f12d5508f3a7690265d0ac5f421cf5ab887e19bd5a318fb23c893db937acd18925c44daa4e8c5fc430c534c53859580cad84922143860c193264c8989999a1f9e8347e313635ac3e91e3064924850a157167ac57ee88b3a3ead58a39e2fa15ce804b36c02bc625314a7c2acbb22ccbb28c898991390882e015e04d4711cafc23ee18c34b63384c85fe34c5b434ec284b9d3d8923ea17590cd72f291f7dc66ae55628eec413558a1b34b3aea02a3748881bf4e40661e9b3bf430ad181ab116610ed06650669e0126003401a5805ac81574024100b6853ddacc20d516129efb0671fd41705cebe1b8d2180244992244972b55ac1fc27063db3b06a45015bfa358453bfa2fc3cfd84f193a308e049a14b97281ca3152be629c50c9ad9a0cc3587fb35d72f29ef572e2dde48c1559bac28d5a62b4a504041e1b464c992254b962c597280031c20bf5a51b272b3b2640597428e5a1b7069d5e1c3c58da58ac46ca92a310b79e0e14b0dbed8e0cb0dbe80f105075f72f045075f9e8c64603403231a7c71327a81110c8c6260b4c5c8c9c8e9cb93161b38023c575c3d8aa23d7d1d52480d5c8f941ac0d480db0e3becb0c30e3becb08312254a04f08ac40c091a123524900670a327a3262327a39c11172328a32e4651465efaf5645667b42f42c07da320ecb8424789094c4a464ca10e3a8c318131283046156358314605c6b0c01849632845814094084491c018b62853447940142aa26089628b621b43290b146e499eeb2d0138530a94518d4ad40d37dc70c30d37dc70030e38e090c313c09480a6043825e009c9a32845418a628b728b924594a5285c44c145d11285294ad3181dc0fd2858a880c54482aa2145a98536d8204610c410430c2188c1458c21884104318a200654f641fe41068218b9bc83cc83dc831c46cee59c1850565c5b009eabcd09ed402c417182823b72e4c89123478e1c292a2aa2e14e6a4e909cd89c28d5f00c959f722e47652f3ecc7c38f281e643151f6a3e2089d104dccf4b5861acb35bab48312b8dcacd4e4a415734ac4a6161358a3f8718862b87810a7af6b3c5a312487d2a41b3b07ac58ce5ac0ba95fb50aa656820afaaa55ac80e6a01aee29cc9c12342bf74707d4f543832a56405f3f31a0b50aa67efd34f5a9839f1cfce0e0078c9f1bfcd8e0a7063f4e3f3488e52c2c86fb99c18f0c7eb6fcc480c6ea02deff81018d5523deff79c14f138d3d792c96c36108e59f1fe0fecf53ff10a584311c000010c311f184c513079e3af094c593079eb478fa00ee1b2d592559946840c9069e6ef8c9557201daa30403465c2519a03dfdba80abe40ada6394041465083389082947c01da2c452ae803b040b4b31936a14a6929b757ddfcab4c2c4c53a525172b3f2b4d4c415219e536b462ec4eda189f6f46b90426ae01eb22dd3628821861862882186186490418619de83530f4f3de47a8082c240bcc8952c5998122525372538255a943029f9424953bf84785fc9967e3d795f899385d52aef2b79b2b0bac45309705f891722d018d6d97d48222954a8d4d9294944e5435d831c92c242987546bb4f5e197085784de2427945e246f9ad4cbc46e0d6da2b046e13af58ee103faa513c89507604f8e5cb1759961f5e91b84f82a032c315898bd45adb49b589ef90dba1890811224488102142c405175c78e13928e570cb61298b022ebec62ac50d3a6a5743f16a2589a099851d552b6696297df693b86c0729248543938821c7018703130e4d38cc6ec805091224489020418210111109390d4b34e06860a2a1e967ac579cb85843059c5e651b705275e2e20dce0abdca08c02a7349ae576e53dce88e7e458aaa484f3f7efcf8f1e3c78f1f408000197abd72b3b0fac49285552770f50a130e29a486949bc2a28f1f546e3d7ab5722bd2ab14b73ae0958a5b97787dc0ad555ea7b83f5bdaf5e488ff34595895e2ca0d62401437a81e1171282cacfa218867a1f27ac4adb35331a1b0b01329541e6485c68a783fa8091a337a3f88091a03e2fda02a3406e5fda025682ccafb414ad0d810ef0735208816c480a0248290a0b11fde0f3aa2b120de0f3a82c69cbc1fb4001aabb3f7838ca03129ef07cd688c4a8de239480adc0faaf58f1a05d2ad565e1d708f866ed501172b02a6c07d053c00f787a002f7a360c17d2810c07d202280fb4612c0fd226c1dc41d8ae2873b24051177c8012a1c12953be404ede94bb9434fccee10144eeed0952280a8566e7493684050a298568e00d515708728b1ea43dc2158586526552be610927525d5274e0b65158a2b7728bc1fae51509ced931d51bfc8ea1397e21e65a90b30910451bfe028af469836d4a0149fa9530d1c2447846461b589f7896c16566bef1329595865e27da21b8d29e07da22cfa35c4fb444bfd8af23e1117fd82f2fee1999aa003b84f84050dfae1fd204e348680f783e0688c88370ce6e3739ec7e1f331d0330b03019d52580e3110100c76f20c837b38790e710f27ef51c3413cf6219b855528de1faa59587de2fd21240bab4ebc3fa46461d5cafba2d068a55ab9f833aae043f4a1e3c3f3814267bb068e0f1f3e6e7cb468f8a83e7cf8f0417f7c564d1ec5ad50bc5eb9d589d7d94daa4fdce82655284c2a4f984538614ea9351309d72be052da93804b698f945bad5c222ea53d08b84dd498b0e1fa216e7d142859e07a212eedf309175596b8d4ba54415c2737775be4ee05b81e889bbda6cce4df7ff032a0237b17c0b589e9c3bf33b914fb7029ce37ff004bac28e1be016e7e017a50c286fb3cdccc22c0cde700be34e1be006ebe921d9098e1be0e37b3f289640ca47c1c6e6002536605e0e6b306319e709f869b5f7404aa86fb00b89965e4e6b3087d52ca9721061f38e13e0c41b3cc7281c80d946ce084fb436e66e5130591c112eeb370334b869b90707fe8e603b9b9c3e247bd72cbacdc5d2088aa2087fbb19bdfe3e60e0ba09bbb14c06e0e5f3f11c0c570413c4b2c1f1d3e4cb89fa3e7da583c3b376eb8ef62e17c53bba9ddd43a9c83747e9a32cbbc3534209c8366efb0c091c914877350d84d6153a7587a874597029ca90970e18c8cd189d55da0f302677029eca620c7205c0df7f71475aa55d030aa0de80a813070d55a227495f6f46ffa54e5fa6ca83ebbaeeb4c151753d5a54f1dcfe89e063737e2027077f742c80edc999f3995aaa83efb5995537981c1abcaa93a54e5745cb8ebbfcf0b570dbbb443e87ca8e6687d707d52713e8d173b7035ef5466307fd6652634d6e19044aa9377a054b88675a7f455e5a9ea2b11ca7af0962a958a62d567544e2a959999a82e8a4d83b2f3ae3233751ab203871f6ad00b77907690682c543d75d52d29c4078726a53daa5a31b883e4bd8addad587c25894493962a1ca699a2962a7c7fa04ed807a7213bb02a751fac32739c68cf0ccaacca71aaf566eae48355e6907d2a47e584bbd26e47553f22f4a04ae81a76940528755a4ba05b2af5dd860054451a3254943dfe1de84d120199b4ecd11de8861be86132f96ed87b77a0fb01ddefa91e406636c2404fa55e8950a684a40e1ee8fea0c2611f5207dfe3faf0bd9a30d85f2f265c73f6d639ab44c3b3c5c1903cdb25d891678bb4c3b3b535815ec1517da69ead135e70ed8267cd25c8b39dbd889eed11ec86674b8b0979b635169edb49f6dc60083df7137eee30869e3b876bead907f73890e78a6b97c37358f45cc26c7866c59e8f839e756a78eac78f949969e08eb2fcf0f1ce59188e92753581be704240cf8eea17ce52db9985e1e4b09de1d8198e13b6e106400cc03d3a42b1088e4428c52e9ebe602840af98922fb0309059ca4e4922164ec921176bf9e39424f2f14314c5cebb01ea479475751728a62fb6dc00258aff61e6521c3fa3d5ae20c510a154e1702e67a238440808514ca23a18e4801095cb819e939353f891422250660a04260330512030cdcc868e7284b4a101dc7cfca8012a080896b1500314911964e6308809c4cc2536334b66e6b360e69306361898b286a6c04906272d4c34054e544b59c6621769edb6ac275ca30952139b9126331c388e703992bb01ea862e3744f52be62368c88160190b411ec66048119a0ba13911cd8398f904820bba4170b4078899c3109bb92c656666b158304333761b0282c33dcfb15b4c8ccc584b19c268c1d3eb3324910c4f7d768f1e30180c067b9d92443d4c1972af2b43187d368d506924675d4f4e5b9a94ae30928591a599999d9d1d9e671c27199eac2b07d5e5862eed1212c220463372eb97501896258bf5e713af7cb0ccb30f967906c2ab1f5b181be2f1fc63c33f3ed440801c08e87508a58f53920848d1cd0224876f1c899a21814212852487e4090918489c90dcb258e242860c1a1a9a146e80ba21ea062fb321166440ee0d5137749119f2a3c9ac5f3ede3f62407011214441828a08210a1254023181f041c2f70789f6f487dc211d16b29539e4e084739e69e08a8a8edc23b923384a7f9f1e49141228245c90e4906841c284e40b244d7d764cccbdd7cce1de00655d51d685c30c071a0e5570a85924cc8289042783b2f52647fd1aa2f5eb47bf621885531a540354d5d147a186a51a7201b0e9e8e8e8e8e804c026232323232353fef80c4934f419724887854ec9213c359f9a8d97a82d4e603ce1e0e0d46842b3ae26b526484db0f469238500d1c403a6190f98686848a21f426636c24207224229f4a1166b6aaaedc74da221cac98c67665d4040019103220c209e96b8c06961eab36db8b4cb8a8e130292a4f19206ae55c36d42e3396a58d1fb3cb3a27b43172a64de0015815286a77efd785f8630fa35f4be0cb97e09bd0f4453bf5878dfc98cc67c04790e839e4b20cf2c2c839f8f6564cf27960962e670f52033972cbc3a1033b38e57c766feea32339f4d446f22164c23b78e41996d44f9b83b51443672b5f24a89d4a78d28dcb791b391c35d0e7d0649143e163b101010508f1e40403d7a00f5b8f9c4401fb24077e894248a7da857e410211388a63fd9786a72d484860b430a69fa5353934c69b3d9624821b55a4d063924cc9fe1fe6c85f757f8ec8eb10b80075d23afe126f98f9b7494c39308653a1c0992fbd0e1384a163680db004a30005c006c31251ca60670b3b01aa0acab04f4858015d001e0fa8500a63e3b66e630a434b0d5000a88dc0e1708a8a2db8401b48915eaeaeb50241504e4599e3c8152fe381416f67128218ebd89151a9be1fd264dd0580cef376182c66478bf49151a83e1fd264bd0d80bef375182c65a78bf4903688cc8fb4d6834e6c2fb4d18d024091a2bf27e1324682ce8fd26473406e4fd2647d0988ff79b2c80c67ebcdfc4081a8bbddf644663a10fdaafe02e7aa6439002f79bd4fa47d1fb22006e12a43efb466e921cae97e41e25b1d11d9d2487dbb9d000dc27a204eeb7b004eebf5005f7616002f7656802f763b082fb33d4700ed702b94990a03dfd483a725431e3b5e4794d2d799c92443b176b46ef123dbc4244f09ab8c6eda3d9acab346d85deb9d2b5aee13e0f0e1a2ea4466a2bc8b6b5add5a23182f7f2708f34ab59570e13ede9a26ce1542fb4cf1659a3a25a6f55b15994b4cf9b1cb385a54f1776992d5a074ada678d2bc373ee0c77dd18ce7361f8ce75e138ee0be771897c85db82cf1df25f23d7718b3cbc3486168cdc1ca62237674986db3a8ae1b660b8ad176ecb85db22725b482ddc16969c1cdf905b69cff71c92a875f15e8e5b0383b7066e99f61e491482172707bc37a78171dc241d6ed626ebba645265b22ea6fae4e454a16e2ec52d1b1cf6f01a15975497acab511851508d2cfc8128ccd0b919c14b836de0cca8cda8f52b3ff71c0a423b2ebe0a57f615aecd79dc9fe3b83fbe735fe7a1f19cdbe3352ed079b84b4e807b808357060d1bda1202b8f906b85556805b6d49f55614c8ea23c9ede1e11572ef12bd7583fce6b270035c2a0b7d8407b857bacdba647846f409f500a251bb8284a54fd08b92f699e3c6aee306fdd7dee70a7dc7f57115eed057b8df795c1ac7715fdfb93fe7b93feeba36cfb940af716107af0ceeb9493afcdea41d747868f3edd08335a3d5906c3fd2fd95d7faa409ddcf826ece52ece6dc6464c850f5306fcc96e999614e2637b3f951fbb9ad2bb785d427965b9d7b6fc404c56e5a1e88877c0805c56e5a1e78c1eb834d338511e7ca9ee3fa18bacfc54241b19b9627c3433e84ccace33c7f16ab2cb16792423c5ce2f80e49c4f31d7248875d17e9ce98e4f670f0c2ac405be226fc770aa405b63c5ae5b4df96a0852e1ad721896cae430ee95c23cec5cf7185aee3dafffab8cf1dfa00ae012e80bbc30b7075f8aec2b579cffd59e186e7717f1cc705face859de7f6b8ebbecec33dc00970977c855f61921b121541b19b96f79c534e0dace345c74b139a40633427ca87d06f41e66f31f3b71bf3b796e999bf81e66fcfe11eb7750454a555b3e14f789392d4b468d635b3b9ada3032c31c00e3a78b8963b8e83240a8f831cd2e11e12df9bd4430f4dfd27504adaf269cd789e516b7db715ded6d16dd15a3ddc160fb785d4b2b5945a05b82d1ca4100ff798ad19ae1c71cf4deae1f726e9f01df7b5fb2bf733dad3c3c31ecc27b9b450a6837924eba2a1790f531725edd3137f7398684f5fc7cd59929121432523232323233383c595735bb3a32ab52b4839728037856bdc7a349bd52ab44aab3ac85a036f8873a7f89044f08ce005718dcb83929ccb6409ce0ed209d48757b814eff0d041051c666b0604d442d29075d9605d7d15c8227daa40be603110202a883a749044f6e0f5c1e28e11e7dae7b83daee3c6fefb739f1bf47b697cc715ba0ad7c757b8aff3b8b0bbee8fe7dcef35aecd935c2a03af877b6e5292240f7fbc90a8f8077ea32d01f4ace3e6f96c0969cd6ccc703feb78f1426334c74b929b03450552acc73ff0db123420f3c66c999e199aa0d99abd6eebe807a4716d42f073591e632d73ce8324c2b9d81ac1db619f9b13457b7a85833158bd0f36addeffcdc1d19e7e8e9b033ac1a8aa284b18d009067482019d604027189898e750a975c5755bb3db3ac2715bb4dbaac2e3b66a544736ea016dce6d1df15861e47151f80ad7bc0a3785efb8e793dc1eee736f5cc7b5f11c57e7e0b5b8e7262979ce4d4a721c376987f3707f23c0fdd200ee7102b867b2aebe779368a14cc94325af91e43c3a7c6787dfdc2142ec32937478eb0e1162f36053bf6ccc3a06f465e3087a824e16f69c750085d9b0426b166683665db67ed940eab30afab241053d5bc779fe2c5659824e79464dc9cd81ba3929dc1c2f3939dad3b771446a967adfc6cdd1b93948391b34f17d1b54f46d208d3a749ce7b12aa77a4e957b0e37f93ca3d69af190426ae09b39b71194e0edbd95f680e72189bc8b3868ed55f85b03d3873bdc92e8db09bf43b26e16d60ac3b2fc2185e8c02d9a0892fc90e0ed17782b0e6139b755e3b68070ccd68c8bb23eb77af4e080f1c04c58cd0c1ad88c0c180c7c924b65e147c3cc46d8e6d6c6a4ac9b5b4aada5d68d68d972b6c849410b29e702395ee42ca145cb9922670239078b9c20b4d3b7a55fd6a95f2018a1d981adc2bd4077508224c01bde9a3fa87018e4c2e5fb3e10043bf0a02deaa24b1710b45ebe2eb80b089a455d74b19db52d1aeed6ac5d6d6d51175c72ad19ee2f6cdbb0618a021518a31cba22b5004af76c369b416956231db44c38c2224da9c63014492c3e9e3f25a1e76f02b8fb37058585b7fc5d017afeaaf478fe8e3c2f5e17dcdde3426121937521d1988e77f772f4e5f3aebb5da230fcee164761b277af677bfb79b617e0f16c27b0c2b39d0277b74e509888f4caf6c8f5dc5e76a8d0bdc3a03011675d340a1b7a775ba3311cefde4ff4d5f3cefe86c678602b25ebea8739debda3af9d1fef4cf08d04dec6b282127cee2bb8af34961ff7280b18de2cac6dd6c56461bd645dedc3cc3042b7b02c7b98998573c8046432c1cc7ce21c32e58ec27d11c9c23c9a75e12cccbb5957bfcc9cc3fa3133abe461e67205a450053387e50e33972e33b3441c6ecfc907b8bf52b2b05009d3a850d64526d59c75e57a46d6a83ea370dbd4279b549daccbc963e11e1cde9df1859706ec57a641a241a2315acb5518ae9648a2551844aba7fa843be686fb326a5839b8e639a7e69955f35cd6b0582c968d28bbb5a9ca2a599956297ca9ec16ce3ca9a0ec4e436bdb2b93939393a3d27159b41a271b376cd8387f162b97394c81e646a3645d3448406ebd5525255a9363215dbc78f1e2c5a73a5c2a45d6a525954a1cc91b83c350258a23b9026fd20f0ad2eea9936868686868509099c95836b0a4c112de128be348ae602eccd28d766f599665696aa13d5cda6547925cc1942b164c169ec465407e9e5218ebbc8e3be3d3b65e6a5bdbf289f3dcb99828eb62d9626e314a31b31a39357298939393a3fa6aa97fbabb3436eba241a241faff9f06a926478394b2c26623575cf4d95f9144aba53e574be5128e89a1f5d997c4716370314b31b525fa45e959c362b1582c9bd517ab25eba241a397c2b02c59ac8bbd64c3adcbae0cbe3234ebea30bc89c9dd9cd29b531f53c7cead58ea6a662905e393d3cd0a88673ef3a6c9cfac195c684f4cae81dc3a23222a8409533f6ead595768c2909526f6dc195c9f1df3989898189967cf2926675d5e64c828c39bc2a9cb839225392831a413a8f08678e722b1c129bc204edd2043e085a105a622b2c887792e0dad68580c9776751b2871e498dc8981b27b3e817c66c8172ccef1ce5472dc4a12b92ed28c5502658e09af9769909c669c684f9375f57bee8c128d7cdf6b9246d25699b56a98ffb82c2bdad5dfb92c5b9fbd5aea90ae96faaca2cc2c2eb8e85766850087606669264763a9f262299662298aa5d89931b4d5520a0c59acf0763b77b534f3645dbde2a25d7def168665c912bd5b09f0f8ec39c574b1c58436dca7e9316798caee200e9086240a6754099479a565c80d5ee1fab43c77c505ede987360b03f2bee7a47a3f54ead76aa96160544ea54d0aadeb8a92f659574b161693cb313952480e8e8142c12deb8ab2bbcd0a6fb549faf1d40f3386cb8e19933b41599f6372ac9a1934ac19192c56784cadcbcc46a13d6ff624f01b3668903c07c8d470df5c2dc56c4183b45a5a2d1de7989c67450609f769ac56aba5182f707f464cee0b9c6372bd041931391a9c637231b9150d15ad05e7985c1b01e7985c26718ec9f516d57de2e1bc5ac25dbf52af8dd46778a5911a4b5771caecc093301861d06412922f545b9312eebc77f7de993f7c373c03f5011e6ab3452488fa058737c2dd3f32ecb3bb91471285af537066c2152cf6181e5712a498863150b86372edea0f045366ee52ffc02f2687bb1b03a53569d370b370427d24091c0d47c32161c1d570b50be06ab81a161c1216dc15dc152c17c082abe168381a8e8643c2d570557057b0e090705570341c0d7784ab82a371e1d2a54b879b75e9a2d4a50b6ee605378b8ac2cdba28e1665db870e1e2a50b54146ee6058a0b971c1417123c3555c10526c1d353152c521e4a8a822660c122eda1a42868c215381b89455d38e50e3c35c266f7ee238bba70b24051174efd720ae3141775f104dea22ec2482ac1d32d5d80812939043469775d77afc420d959a0ac77ea93e2a22e9ab068c919c0a22e9a7051174f4eb8a88b2db8a80b30ca0e8337c26651176178b7a88ba73e8bba68ea9711a6b8c9a22e9a684c51175ff4498bbad8d227c5e0972f5f9e803bb3a88ba63e29b61e0944aa639b56a0562b1a169a63bbcc1eab286bab4563b43b23d0b4f8d601b556c144c5843a418049ea3b1326262d2b134a836a32a14e4ceef75dc544a552a90e9ec9f5e163727d60c284c97df0e194c69a3c754a63097818541bf0dc529ebb3a7b0ea93c8f579ecb289e59569e2fc5f369c4b30f4e2de099079010cf14f77af24cb53c204545ea629d5da3a01992ad4f2b94ac4bfc404b0e01a382519f3c266a4ddc0a2bacb84759ea16f00bcba4848dc98aad895c134d3df4d0430f3df4d0430f052840010cf0aa844d0925256e4a902b98324646c60ccd8c152ed6b06c5812880004cc2308607935e256245e17706b12ffd9d2aea32c9501b49fa609fc3409f1d713b352b941345bb32e24296e90cdba8294fabce2b6c4c5036e108e8a1bc464c31b4175f4a1b070900c2964060ecd201c9499dac009071c9e70c8e10015c4f3895bc02d04f1ccc22d04f15ce21682780e710ba741bc858b2d1935465c05bc4a71a13811e234d040030d371ab2e8d71032a0a1a808638c31c65826935d88cbc28b2015b115518aa28a5badbc5240895bc4eb046e7da2885ac52b14b752e045dcaac4eb13b74ee01398c0451b9beda294ab7ab5dda32c4c5a35558a542952bb6135cd8acc8a1c99346acc0035439719a07850d267dfc60660cc909b4169865a50505050505050909090908fbb907301ca852817c078aa41a3c9852617b6e4b4a80ab4b8a0c505261b518650c0c51b3c2e9a4f5c8a296eec890b54975cd02c965be1414704fd303ba255a95d41b259585de2fd18180b8831c59a624efdaa461415c95571ab62a90a1c05a028e0f4f3f3f3f3f3f3f3f37abd60af11c05958b572a3464e8e1386167c989872e4e059fa61faa1f180d2a143870e1d3a74e8f0f1f1d9711f269f261f2715ceb3c483e361e2f982a789670b8f130f183c4f3c399e15e03e4f0a68d00f29e4904452a85039392485c38b2e217c7865c2c407263e08f1a1ae52ae934f318519d3f2f34449214e9ac8051912c2e512e2da786e3b503b4e3b3b3b3b3b3b3b3b3c3c3c387ee37663e906aee7f72a59d8bddd2ceed2e5e2e2ae96cb749b6e0770ff6271dec5e42293ab73b1a24051b0f51657332300874c2e939b24d1cf963e3b0c99fc3431b93f4e52bc5eb93f4dd6e5647f9efaa7a530635ada25c406366c5c2734836637b55bbb486ffad28d1b376edcb871e3460a29a470fea676837463b3b06ac565c42f4934e5b7a6c21141ca879a0a15a6761dc59afaa7eb957b9445882771594c4bff0861c6704eaf5523926897d5ae2061b159a1748302f7839ca0413f3ca6a55d1d9b80943aab52984701a740a854aad68e9341ec989a111140000000b315003030140e87c4a2e170a4e9ca0714800f85ac5e6044980793248f619062c6184308000000801100c268da000213c7708f8a8415793ec03a432131e00d107f000ded1ada506550f6ce59756a7aa02a638511f40086048d988b6f924a3e00ac0553ca5fe0b845356fd845ce22592c3440b650b2cb2d0e8c790e326edfe3c2fc03737c5053855dfc521ef1e51fc2c6f8106d1cf2e9fdef4876d23f64de721dd3cb0a2573ac7d996fd408dbb20393d3d343f86ba4f6691a43383048f2c34cefbc066a47279f1cb2a96862ee2d88cb948065551d43b44f1150ba173e5364448052915502faa5fad7c93ccd215e52a9a2bd7cb844940f162b6f64502451a8e40c5ba71b5eb5b5d59a80074dd1d20f86c9a7931b8b835b0e6f5f14de9f91f3832d6fcddbb5ea607a8439213649f498057d74c396113b7dd9f68bcda020c5616fa5944b0a301d51120cf5faade7721bd97833a8b9b913df30ba7bc3f125b071700fd8c953da80641475e23156565a880d39335669da00c2e4f448904008b6ec6a207158f0ce8c0a21439052a4530c024d952f06c11f843b2ce6e14e12d5d7ca88f894e213595120d379576406a0f1ac56bb52480cfb23ee23235afb7c0c30b696ee5dc8e694079668308621f4243ea505267afcd591388820eacd5b02facbbb2e2d44e0052f35d3043292bcd03345098c99388a9bf9b93bc690a3920a36890117f0cc60f8e497efc6dea7e3fda8c9bf9b1e50590ea42614fc6977a6c18368bcdf45a301dba18d06f201740785570bd660b83a5b238bef353488b133ee03ca92a432ffa559ddd39cf176091b514218d99688d8d780abc57246b281439aaa127480f33da584c42c4e7583c1491c93ec4d0e496328b709e6c498873531038b199ca67fa71833b5f55927821809f75dbcbb92e1b515f03aaafd4d0d1c4e8f9b93383571fc017cfe656f4d5db4d2b11abab7e69407ae7cacdbe0a6da2ee66675d4591d2c375b4dcdacf47d0e02d7d3ad6aa9c65fded05537cdcd5cf9c1c30faeb1723e44f7dd635fcf73b466dd0276f353c447756326a6db082b0c3930ebca9d484f4c7cec6a0c8d05926ab8144aa77215b0d4c8c4b2805e83b6f2da361918133ad2159ad0453a8e48acda8291c1bdad4955e8d6f266d367d23943eb7756e707223370a013b1d3e8471c12547424dcad5cfbe7abad8b990f117ae444c8a18d154552d5755a43205402463ac7565b51275c34db3fe24d26a3e58ad584d038378182afe6d534138bfbc17a29308d21d592cdfda4fcca9eb083daa0ede6e0577064ba140c8f478c74871f25386c991d8c127c0797ce658613e8ed7cfa4f4350edae109d4373eb041900f3088a36aa0f04eef31a58d8f7c41b0b7cadc61298ea5672c0499c80943a894f484b8f5208ebc6786d4cbfd79fd05f258c500ff6e5b90a87d255c8908165510b6a941486a3e80eced037a33ac50fe2fa1435390782111aa20c34ca3baf1c7d71569b98426545257ef1beb1556012811e70e8ca01539e4d6b863e4230104259f697e868e861684cf15d5bb6507a5e838643df941f84d4acb39a70c694e8c77813dfe4ed7e12653b872eb27138b08e6cc64818d335554f35e9ad7a8de315d83c65bc567605054cbc93b695107b725478ea56108b375953408d542a472ff98a4c64535411a67ced6f10d7b87abd6b1478a90610519296760157179c548d10504bb644b635b1b20e4821d104540259700fc1f40586a6721e80b8627b74ab3e32f0da32a364eb80a33d051fda483c65977d96f1d6c696a324d89166999e84fa84153b18836261305c444ea4390a9002a4a0af3e4dfa9744a302493e697dba25e963152d8497d3c6cde7c2f22971b760704c4cef3a834d27301e2ee04c7ec16ce0090442d309520e304574eed3f066052afc0dffbab33a600c420234b92ed15f880dea565e1502fe2f6404d32b117b4d45f3cd90c1c32e2f1dba724d0c4c2db0e8e849aaeddb7498c573d29c727c5ae32c4be1607fc37b505f46ed4694040361b0814e23dd8706cf1b5b814929455cd2d75a395682117a0a964ea2c79ee0d5d4f8d3fdae9f18d6a50b0e7f46aa30b8cc65fcf31a0262f429b9783a4abcedf153770a8a2886b0eb16474a158b14e76746b39083f2104e10e0a3c1e093b6d96bc5803a3bf38525664acde0d800a84d0bf2574878dc3d875d2c7ed44877bffb49848991b817085fd6b7487129f23abc1a4b017c4bd902cd3cd23eb4a2a09e3b50eab2be291a30f8804511d55dbf03b9aad0b788e4688596c3c6812a3b9036a0d9cf8518819d63854e9430d9c9dc8390c58e949a18efbc448889599d635311f48178f9b29c12bc7ff7807bf116f39b912553307265f6d6b14539f91b667aa36b1734412048d9a52371ed3863d4261e09a804da9126024615e0e9b5e73ba1adbcb55d7df7d1d1beec751fe4ba9227545c3f4cf3fc64e26affb0ca7155c33ad27498716a80469b9db770dd81a7be3b9e8f6ed83e80d562c00e3eee80b31ca45b8639e826f304af2907751de5fa5835ee983fe9c25dea6331663acea19ca446bedcca137eed78d497b8a279410afb5cf887bab02e923a9449ddfd2e58235ed869fb51a1a5166e2f84eefd9a1653c1cad4a2d2d46859bd4132e5ebf4cd35b28917c0062ffdfdf12753c7da9298739b6e5bf178c835ea404de2ff9823a30e03e53f100e7fd25fa649b46a5b3e71ea8f16a00f917d6b0e50949dbef0eb8380ec2e85f5205518e187323bb64186d8c33a041ed95d4a08cfce655a99f39052ddca63973aef4ed35616901e0afd8f0a68e899ffb51f930ccd300af899436210fda5160b014bbcd036b1dc912040bdf8c9ba7d93227b46980aff91654a6eae613791f18b1bd4e9794d4e78501991d77f700003a9c81441dac04d20bacb09d7bbd06f7411a240b064fc5e321c0b3e4ed0703464cd01be0b51c1cb933941ea1c8d4d7aca7140cbbd7c8d9f1e501af69e7a33aa8e4c285b1cee99e842346c94451931401304fcd4dca0dd966a88e1891e4343845e1f095df7b0455024b31022affd3006c80d1a2862252353f40a450857ca14a8872cf1beec177ee99341c8e5a51fbabb1f0cae6abf126acee9651500d7b108321fe3cad141cf47895cfb86c71874631b4f0ec2db3f8969f0ffec6f1f40504aff5b56cad5fabbcd6678be6ac786f6f847de4ffe1b6cf60e16eea16b4be80475721fc8fffd542c033df0594f226e901f09ec97501df5b16fe3f9728e16ff0cd07e71fd2b767f643d74d5104f95a5fd194fde03282022dd5bcf1cfbe0902d0e6f1459d77cf15fe3647698e6b1c76dca6c902bf8ccbca14e39f593117c42c058d1b160b72517e3b551a0a8f71a7863d2ff5a8c61f88afa37272079ae742c51dffff40d0434ede3351bb98d677bf168a0acad58cdf88ed72b08071320f34217617a4807777dc557aa3c2d2030d8ac5205a2a4834daa65704207b2e826167268102eb29b591f22601ee3ea499b5903d0aebc7ec48d5625d2a311c64924d81701a3ff40717c482862630e825b17d719780a533bbe2b4106a43f209593d1bd639a4ee24ba96fe1a17df9389054d2a25b64e1378009c3cef12fec5697ae1e9db9989e658f6764a6dc7c22c43acb0af2141a69cd5085aa774dc32330bc3658788045d21ff20c74a833abaef162e091540cf0b20518837130c80b91e4ebf9e2c49957d83a1dd21b9c5c743b923ff7c15aa7501a12eef94c5ab3fed62d08a5dcf603f0cc660e5bec0c791e7f3d1cfd267d2f7d8dc0f400ed1e1e223d45558f7253ba19271b94b4a1cbe9bf1a85ddbb9093d3a878f47378b76843a229c8d75c316949dadb35e247975c081fc353c8b14d8adffa7436f91f131735189ed1bd08e8e18b2be53f446dcdc1f1b910c79e04130ff47b6888d4a28d83b1f3a66985988d2d38082f859a06718d893749d487b040eed9099ff07fb2b0639a4e679ffa18f49417c95e1644a0cb1f17b5f37d596925fa928370c4745462a954201b02d111b0f63b943da5ea3e51a7c24fa0a85a8748ec4cb8c33f3c211d08ff320a64cd37ba6dbd108e786a7d054332de00e446daf1f8553ab9785e4836761eadcbaed735caf75784edc343dc0f76a76016435133d5a8311931d4ab02f213d8fba78928876aff48717371d6d2789f89ed2082e9f919801c45ebe88fa399571ecb9e010f0d50fece70d4dabf43f75d75d8e7cb8d6a6086f2a11fb59380b87f68e6b283aa9b6a2db18059e2dda22c9674f4751eedc96eec38031239d2f34a639b30230692f01010cafdfa6c05c4f051253a0214042fc1bcb97509c6851975dbe45cfe7efaf2b9a2a243fd5f5dab7c38c52472d134e9ecf57d60c3e434b0d42dde0f2d090df6eb050ccc007b4c115e89e87b5bb2c0e40d7a087d443f01f806a2a3762dfb540fd2f2a05a31f42743f5bd511b1edf00c66daf672c1a03c4cb1f577dd609fe510d5bd3be201a2905df1435006df727bdd53cc76e65bb13d2f642c3131256a2542329324866069841ac0b058f9c728f93bd22d42eeced609be4ea7faf1f2c723ba9cfd37116998e257b3e960fd53a849543c488dac893c118644d8e0629459e2b518c24686ae8809eee99f10b3c9c4d44ed41513641048911cfec658e4de752a97beba20781c923e90172ac1d2169c26ca730b74de59e09d47c6936f485a30c2c1b225b2ee066cb703c5f762abc08311a4e441349bd5c6d09fbf6e4fd022819a353ae3e1c91c7d3c201994fd08ee03c9d70ea3e39ad8cb4c289e4e50fecf7965ceec05a4ce40313832706caf490e363d14c86abd120732675c2f7667c7ae07084ae1a988443a8f524161a1a43ea7f3de052d500993dee678ea469b12cb7773a8016b8f7a97cac81839d29fb227de10ebf930464c717505010bd08208965984d58c513fa34dc5a6c54b9c16365e565523602ba2e4b792b8ae36497e1ba8f2ead8f7da9c76d464b4148f4f6096d318e306cf05a367c1e83b3d9c9a329a99d32e30c37194a2534f14858610046a609ff200208349259bbd519c0e748786a36d3cd3f301fbfbf0a03070f56d91e0d0e27bd8e7ae31affc649b234c37533b79490849a5718ceaf8d422eb5afeffde0481b6767bc0a90e201a15fae479d3172fdbfcf73a172b3f7d75f4aff00d321540269de7619bcaf44050510b1707456f9328d95315f54e8026ec440b81903212a6e12905e04e4438cdb0d16adb051d1cb51219dfb693043eb2dcb8032979ca90075729b58c71a51969bf77d2fcda1919cee87c955ef20d6dddf2b8aee588e50550e40540c82fa018638d0ea484529cffb7d866f51e9c11dd2f5cc3a1439f17c9f89ce77d4b9313c01ca60fc0a22c09b10ea885d676af006c08e1ad641f8bb4e3a0f791e2a8133c2160b9afb3d4530b18bed49741fd84a2f8d8d41c018173da8f7924b9e4f7e14038caa9c86886d520451fce0b2fba06d5b279e0f2d9bbe1170f76da2e2f81c34e95f060dbc8b61813b76c9e34311bea0b0f0d02bcb1fa66d0e0e563dad8cf7c2466541efe74640e12d67f83c64093e6d888a48a0ae0e1fb0b7e64c4c03891389669d5053b14167a4e2c5fdb8ae61263ac5a64c5e71a3594051fc031925a40757d8be8f29ab3cab486874a1a59bb84c788b102818c64e0c5609a4b4f980557166f1b9890780567991e106fa315dd7921f12c3672228c6ac487587825067858951c7251c383ac67533e6ae8616e647384f8f98e0f62497c306c774f0162ff9e64d2571cd80aeb82ba90d3eca7348eebac4753fd78a69bfaa0a5c3cf20eae81539cd4567de18f08ddab183167dbb939da44e2039c1785c645efb702a35e43c5eeab0f69a2ff61b1b943213b19d091a818850404db0fdedde625c54330b28889b7aabe3699aab5d0d7d0561198883756e7437b6dad3110e6fc4d6099b88b0a8684569fb38ab308c708e0392c36abad2a26c982feb0386b48b55aa97e64e5242c39c15b276d5f1993158348a0efb1304d0b9489ae55e9727787662cfd942cce33ed8650ef04b000321be2f47641895cb083cc44a2fcdb3d713c8bc5c12898ca0ef6e5734e8978362d4fe0b11cfc6c8d51496ae92c37f5484c92a41c63cbfef92026f645ffa474553746b775c793245fc88f9fe0651aabc02d7499ae42401ea47b6efa018e926c8e1d17fe40e629257495d16653013d9578baead9a78f16a523d84f096156c9e57e7e06b08ee294dad929c3b72b17db081d3be2063b722bd817e2b83c4e55fb16fe10f1563d95815c8e88c2b0e76543241cd81b58467594364bcb1c04f82790ad3e56df26e245a7737d6150b16e84f5c673cc9ee4325e3ae3c48efd033b1acf94d7ac1d2a1c9616350a386cd8cedca007663cdd389a94e8759ebe991701e8ae110bde27a231ed56fa3483791aa04d244992ee99a782ddcb5073d711e90443d9e61d4816327eeb0e9bae672d0fce680baaf457e708d3eb26371ce91937d569b937c55a7f6c3d54a96f38ff731e875d3b9f261e676b4aae7d4d0477bd84169d8b3b052fb37e8d3c0a04c8c70f6a2341587573b75134ed5658e686e211d965766497cb65dd09ddee7bccb80379e2df88281af0f03f6cbd4a2ee85c1599e82ee95ff9cff85de929686edcda5ccede3d90ab1edbb6d9e430e2549ae9c33516ec5cddcac69ca0c8927f9cd127c76e434ceb8d3c22131190d52a9b2accb10088570aebfe73a6e6488d35452ca0c83c0751d08eb86a173e01c5d93cf50bd64d451ca3dc8851d53cd2d3bce63ccbe6771ccceb47efc379dd645e5f78336a0f79c35759dbd4fc9a5e722880099d8e9326f2641d52e05ddc691ab2c9aeb7cb05ae37978ad084f6cc89e3bb854003e81045f142eff8cee4395279d7b4889a13b2e462fd8a8e1d01711034adeb3e38000e4bbf3f3ed858a9b7a5df53798c33d23e5724498c2be08dd6ad469c9d33e64fb20cbd69ec5fc9a720ad8498ce3040321b53c333c6b67b938bd5274fd1ce16d6665c801ead46467daca640a99abc48970178a288adbc93862e684a3962cc867af0bdbd72aa3664ff83118d828c5e364c7b254281d8dbf77fcedfc640b5054aee2a55cd0f331d74bcc353eb7a8a9fd77ca115920f53b0d59866a57faa439213d5c1d9e89370f58d777630277363b35589561dfbc6bbab3543228847a88a625a1daa71f3969f1dc75adc3979d5479f728e0627bd87557d9a8cdc657fb21107a42d1ffa5ef8f157605d3ed1e9431467b2b86d8a02cf8deafb0c1da9b47e7c5c807079be84440dc28a689df4cd87d28eaed853cfd7c8c8626a78acd727def0ee23de021fa928a686918578472a3d18ef97dbde8e1310f514bb637b68b611b74f2a159075d565c57aaa1e35e1c8f31e56ba60d28d9604b949314126251e892d19d168609947fd00b5063461fd5779ddb4bb3ff84646d340eff874c4960b86e88af3b9a1e3c332a739c77c1c440ce01a60dd5241b19caa3048b6e755550309b5ecb27c62e64d0821e4ca3d27c6f9e4d8151653b82b0ddbd2b42d3ea205ce5d34092362cde6aecdfc64704014cd2413693110c8c78286b773de30a5159d0c3bf9f9189185b253210b45a9fac641ebd1ccfa68bfae63d84698f794704ce74ad13daa8dd15b19011aacc65e0fb098c297eec84d49a300734d8583a526136124f86643aad9b6f511ccc1d7f4aa64c1f749c73db26daa63da34d61308b6581601494aa85d7d2d751bda5e12cd210c58f20e654794ed735d82dcb80ede6efabcd2080c3ed0ad42a4aa5c01b41fc31616c0f8921ca19098800a7c1654ed38a913679c42526c26bfa9ec55de13f51f6f45af55d83fbf2057eeed76933ab06f0a4d6813aaa1ff7d3400f9b1330a2212533f1116fe3c717105a902a75200680d8d15608d626d3d64055ca3acd08853563ba04555db8d9b48407868fecdf61342a4b06ccc02189dfded841f43c2e9152e8ef5662eaa3055835aafeedda78c96c3777e803018f34e2c4040301855c231c4b27a918ab09229136360960c70b40c446f62e97589bb209ba5d35f069ea4643a2147d66545e21d3a8bae44c01806b93601a983e534e286292b58191727daf8231bccbfb1c2862f1a82b6af5a3f6910b9902e8986bd1b5799e0bc0cd331a436d50836aa45fc2cf01b3891d9f3ad472757a7508e17587bd5788d78e36374fabdd9222cf171522394809f3f4adaed9a981441c8a1f497a61382d8ec1682361cb83cfdf8c68d6db48baaa967aa2dccd91d7f178e9585b178be319c37b11440b05d81042f0c1b2a38160d3504889831f0014f35af84d204bebf09c515db097a20356ee42dce5b28a0c79f57d83580731f0c1d210eb6f6ad167ae3ba15f654c797a28794ddd0afb1ba2641bda43b6d7c00403657c4892807d521170bae5b742f065fb4331d29c718c258794809aa45b061a122cb919ad9c83344bf8deb6651dd3aa12cfccbb7d5a5cd1ab12c4331fdfc11e15c333c27c0dc9111e035d668a52dbc3731bf54fc25f046590df512ac4f12d44bbad3069f0eb2b1224e043950097209d450dc598219c031c2b2a180603a538be650cbc2132cac8c3c1002374bed032c33948e6ecc75b218eae9e10faa73aec21c0243478804acfda685b6113d0a7aabf5adec42ca6e48f770bdc9704fd29c1a2b6a908d953bb1e484229153a442e2c4106c0cc618d6060506eb9422029f322b8f656460f682695c380a3f60d3a774ee5a5cd584550d3a14268dda70d5cc454646cd0d3edb4cbde289149b5295a67c417589906d3210b801b4cfcada0cebbb1bf4117b2ec5c23cac33954d4925f688fdc657763d856b66909f65296e9068e9b2a542ecb2914ccc0057a4bb408072fd7bcc0772b9a7fa8eb34277ab9da2da8be032e138fca4adb90253412418debc97ad005649c67c771e9e016bf97c5b21bf372dfb9e7fea7a2eb851b62902a0980f35365b98c122814a66bc97eeb7a76120bbca976165bf80f8fd09f1ce060e7e0041e6f365e3f1708b1c0e36069e46a0061bc48f6ceb23242881bad8405c56d3fef16c30854d543b510db2f9a81aae7196fd9b5cac3eca65abec28b91282a7088289266cf1107a1b5bd81c23f17ab8db434808ccc5b67f823dd499795a5185c33ce9629300e87d9a622675d6416d22405ff8e422e170aa87bebc55284d8ae98863da071cc332c4b2a582623955e1902abfacf1b273a4e13c48a3ab51f1da05c1bec7d7e77f61a232494e6da4334229d49713cb2e0a49f5c2b4a38467d3504762e017b1c81ad8181b246319a9f2eabad6f46ec9db4491b6b18278fe4b8e621b60d852c1b19cab2c067d595ca55181961ce675a88c8473d1e0348312c778187f65e7ceece6945e528473ae985b06d1c4b160775389e7f715c41bb1c5269038204e406bd8eaacee2aec96b3783eb9afc24bd16986112bbf3ed8a939114fd59738cf6d5657551e689d6235aa1e4b5cbd04b2f53297f105fe39a96f8031a47f36a7b40d634eadacc5738ce396b481c8aded28b258b9cff168b54743ac6eb6661653801dc1397b23dca8eec331658c9ac13f0699a891308a5d3d13f5c419f7812822b3cd8e2c9a27d543c278322f9f3bbba5e793d0165aec518beee8ccbdfdb5e827f33d25d02759f710469a79f8b172e213e856dcea32c6b0e8d87d8c08889d2803a9892e75e9daa39ee88140d6f20086fac814ce821c541b44004d78ccf7c2c304cf776d36de93d3d583f1ee9b79137bda1e3d87bc8503101df0b705e68ecb46564f774e568c81450cbcd9153767643002af322ab433550b2f0e6b16533243fc0f702533808ea61481473f50bb7470e43b1300b49c6a61ba8670fbf8cc673d815289a1178e040313aed02f1828b283c1835160772a80683915437515e1fae154e01f57e8933a8348e5219048060047724d30f6866401216605b8e5591621cbf817dd4c639ad98c43b762b82daea0195a0b0bd41f8ffcfdc28d10b3822c4cbe61dd50b82d52fa821ba12f9c300c1c9202e07334ef6981b80eeb3aa83c2135e7f1e9df7a27819ba696254c8c339ffce3d5f3382a0e8a0fc866de87279fdb343706667672f4c76a2ef3ac5ff81d77ebd91533f342334738dccc62fab8b5afe6cc2e3633a463336f0f77d85459e6f8d7641605fdf631372f66ebbd5e8d1febc97b31239d68ccc00e9f1433e514c62c9647d60a6af18f3a9d4ab5986d673cbe37351262a2ddf4f8326b870e7a1fea384b114cd47e8fec54e0c9729eec384263e7ef9daf0a1a1da519f9e52084f37703bc45d5bbf252431a005615875380ec1ff87dd1e1ea159ec8621ae0baba41243e98c5dcf13d17bda765e3a19560efa3133e6f9f4c93b55688fd869fe8d1c080e0fc871d5fcf8022df02bbd21cf5e6a7b13875554babd0663a331ca188796e669a72b10fce92d6dc482205a24d67e8fb1c3bba4add41767862145928b23c9e8f4b5fe23725dd47891b4351e988a42b45f32bb5fd15fdeb1b7d6a63f8ec088a6cfc5cb1a08f37170d9ccd55e7d4e7c623040412516ca9904f032f4e6715582c3e707fc5223681f5ebe4fabb0472d1dae9cacea07622ae03d0f76405665d26cac5e95657cc4b5aa24fa868823b1cbd421a71513bf59b4b50a4f55004b841b947828372f9348e9021f33912b0bbb06643aa832b8491cf515c8c0a51929e5624b0620db5c62f010842c53448d7d4241b858344bb0aa0accfc0b1c23f65070ebdb6037802fdc2da45d995de0d46214a8e3274f489861a12528634970bc165dd1e2e18c83fa9d113992364c9c2986f63cea92c99b7110895181cbe076113552aea4951df126bd19d6b3f4eca1d9d1d81f2279f8ecc084f5a2cd788ba3e28cce1c935b2cb12610873f972b044803d3c657bdc0caa69734cc63283f10e46240f8899e1b47d979d2cf756e92a87aa0ecd96abb05cebc42460bf2f1d39c87a38ccd17b5ff395449e34b0663547c64d39adb14166948703a5e2dae25c6c6124b09e7943cb91cb50f5acb56e356d195a04170dce8eedb12a4b83a8d66ecee731f3a8e27dec77d1825760feeac75aaa535a7d79249332a3c04691e37fdd5b2b4846238340ae38a8fd5f10feedeb53e91266b97e3463735280711808341b0fca14b88a8dac03f86eb8e678e1daa7d0977e45ac8462e899690f18aec0d4318380a88c8d25b284eed4c44959f410f66ea2db3aefb178e79254447acf0556683a7c401bae564d4b158af36425a43d3c3a10f3e40b13e172bfeb35ee041fe73014e92d311f57e4673794f97ef79308a051040066cb74c2633bc2c057088cfe98b700b0576c6d4648e22ef33d7c201cf09028a269d4a547b0a7f636f4421b33f24abfcbbdf23417385ad1d893f2af6fc8f5e2de67f979f2c0560d78e8ad78644e0485febc25e9f80bf80ae82f7148dac5bfc4197b1e9a609fcddaeccb03004c460eb11cf851b7058d12911e96d21421a97c8ddcaeac84bdcfc638620da86e4f68d9f81e221270dca39522f46ab68dd5c3498a87941023f5c4225b125dac7a4212c4273e410402f0394520010876a50a04e1960d820915ca0082e5ee60006a869b40cdedada15ec3c6143331f828a5de9c988b831b21f22385328f925af94f0f56c723aeb99a5bc81bda215d070127e33a739c40a8f37d3960ab012f8306a55f930387a8a4306310d0771098cc3fcaa3040e880ee23b834c068e7b09101792b2701933ca71828f3d42aa1ead2235fa9121d4a26459344d0ee55e9745161f8350afc9919993cb08f7e5eb3bb8f7ef9e8fa844c0ebe70012c4ee28ece5c3fb143cbe2e870b9cc0d8d3d8935d962c3eca9cdb21e7b982ed5fb3fdcb0bbc219f0063d7e102cd17be1315036faf0dcdfce8e2e2efe2d9f3133bed9f4f0ffe0cd256f14c2560af3bb12d7010c6ccf37ba0b5a11289212e17b701b03b15d69f5cc23018d1bac3d0b6785ca17eecad39c9f5faa82da42ebc91e63c0d8995c1d092e499788edbe0d6738dd01979968ac89ccd1ae3a292c6a5a9d3db7da15aeec76834c545bfa83667b69e8d792f296d14c9b9814412614258837fce136f8ef50fee357e17dce30d15f94c7096d7aeba782ac50c3d6764b35d42d36926f29556eab6c81c539d559d00b1d8e831f638f1412b8cb55bbf369a0c0f113e522fdce00617c32ddcc5f86390e8ae9a6c77b6b5b724ffb31a210a71abf2c96ca67ce580fdc9e1064da5cab12799f37571bbe664ec4c1f85089d83abe9da53e163f4a2fa0fc1eef6500e5e2529ea19b9c693dcacbfe520cc556a692814c9aaa1b3aaeadedd391396deee2c7871d137e6f6f0ed4bc69784d77e9df8259584cd8035eb04189bda26eae7d104d1ac203af097e8f94f38b076d992e40bb5ee7982748d9a15c746290933ea84b3bbebe02b30f0d7b640cada4aeaf905c4addc44ecd2ec06f6fc4e2b69bfd47208c02ab2d5c809812bd136cfc2bab9a0a788e02d424160eb21c620d9bb145b12e5368f076033d9a7ff485b4303ab424c6f6f5379d32b74ea27229c96f1c6f3aeb5438e2da436cb4a27139c395460c36599874307b237c3bd79d282f92a764118fa2b4f6b1098ce409fc241f42a5087b0c44404938dff51268a289c3cd3c054afcd2adfe98eccf43c3ea091a417a0441b2cbc04e56620d32d08d097ce40238265c328c0b1b082457d5ee4a52bf3580191f898d6e1f79fdffcf3fc601133bb6b472b22ca25c3eddd0fba1dc7ea987611e474e3cba20bbc5c2f6a651181283ed275f169104ee38bb7be44f00c987c5a580f4fd53cacab616432975a03f2b7d3cc68431e38baa001617781b6694a96112a5942575aef3912a569c8395bcd78bd3a025b78b83e357f33d9bb33f469d48580a7dfbd1f25bc9fa7d1253e68dfc3024edf03fd47b026f5ebde43d7ff8679a4a3434eb2663c70390991ddaf451a34a972f448375b0260690ec190390a76aed0702c3a509bc2f253a189ead35027140ab0f62b064ddcb82cdc0848b0a4b74073a5da26519844399b149ff94a4b98272eae3fba0fafe94863b33c0e9def807d5bbfe420e5a1bd847753632690374237ee1f4feeae03a73de26382054142590cecb469fea3982a8fdd3c615d56a311973ca88d502dbc2ba3bf7a82146c7a0ef96612bbbc071721ac8aeb56356493e4d2404a95958055affa0eb94ca16e0cbade353dd27494042ec79d91fbe6e58104bab1dcd5e5bf579db8d95b510857b17f7646bc2aa08de50806e58f9829af5260a909e2ca0f5a572da2b6bbdb1017f21d1390bfd879dce0784264297e8528b14e780f827ba6ab16c7a38ba7cd76be22cf29831ffba55181a7cbf2b05e8e895ec074f9efb77ffcbbeda5b9c3a19d26417ea9af0caa65473753fac59146be4f31f1639a45d08e5672b2b825a0bb2693cf34a8d5d672c50197a6a8dd97bf2f7b07a7cac0b1b00a85ede3c4049a2c9a18a85b05aae8ab35bb3c7c955d382cb8965e9fe080180fd0d7de0e8fb73512129c1e27c0b2f1c6737cf2fc27ffa145e3f80deae6db378d6bd365197c9960a8d0438b34fcf788ba3ee8ec3d2ebcd827898bb15eac0d8f7a9f49b7ce616acbe8c53ae160a6c63189a81bb757dc1a8a0e38f1cb9b2ab526b374fad5cf5acf52f0ed557dec972c7d93adbf91950b92c3e4c4c9f2dd1c30f7ada10bb992fea3824adc12743a20c2b9f301b16f5f2f5760ef44daeb050180ee7a9d837673bd541148b65efe3ea4f5c61303a691f54e5a60bd20967264ab17f93ba2ac74243d201485f0582ff9ec30ae5e4a2703b37a91b00758f248a703d5db001c9a15512fe5d840a92b7b1c6013083c3ccba91b7d84f51bbe224e62d4e953f45e83ed7ba8c3f6847bfc84fbc87012b8df872bd7279a9751a5486b34cda37d79f8db3d19d7172031cadbfa232584bd31cfc4d7d15528e95710be96bff3d06594a8d47c531a216061d0e6eebf4bdb0bd32ff949fdb021ce6da35160cbab1c9f399b44481851ef1d3934ac3b45339e3dd1a6cae4d3f4b9c0eab8fef167d9ea7c5a24fd44ea00a44433eae1cdad78dd9baaa3b45b21fe33414bfa191af7b0795ea2a12013c2afac0b8345a5a1e0a56f85aae11221f20eb0a9d92415fe24d572a3da1e0ba2a573eadf9ae57af3e937cb3f7b2ec9fdc11edea67b26849b171c904c4f535bb1be78d0dee3109bceecaceb9ea26963d44fd57923c8a88cc949de7a0834f8f60cf60de618dd561cfa2b50944d3845f7a938ad8c94e64db0fc6d7e1611c3309b2f5caf76512f6fd1fa43dc895285da7a0cc297088715032d2525885c09943834d69966a2f01a032a58051e672591a28e5f41e1489bfee140eaec9e060ddd1720d291318cfe3e4c243c553ca8a944cf7b80ed482b779d09349d566b27fc59c64ab39e7c66f24abc52bbf2125e9ef68116ac2626a9333cdc5a6c8199c640023059274e615b3e84e0bffe263edb559f8e4a2181dc713f45c1172e54ef94d261bde092abf3c5c51c23d7a06ec741903bffc23357dac4c10c47764ca5a9e044d6439d34644439cb9fdfaf2a33dce1b5f82282cf371b33240cce7a9ac3ad192e6bca4e21f3916b8b2d58b9c735823fc20815c2b97087de95b86e80db38c3913b6207f7e737b43f4ee063f0e91812b974b11bd3ae969fb222f248c1c8be0fe1cf4d9525b4816aaae140f9236caac109d2913cbb19fe49dfe78677a627543e7e3fbacdab7a9cdb6cd3c240f0c8b4449085c437a57e3697810293e2ed0dec10edbb8495bda1779085e8b764cd53197d2f19250eec143df901602c5070b17b8d5e74878de4dfc55f7876ba56c19c771b58f4ce6e5d2a23aaca9c3b3b4840577257c3637f2fd4ce8ef68804a96e67d7d942fb7a355d8ef593dd991c6c9f9c5f7ab42fd3387dd4fd6f6ff10f31e1aea54f5bbc8f31f8fc08960b8bf9b7ef6b2b07f34e97a90305e4c18be670d4b10076073301988cf21abb88466192f780c86adb8f870f0681c9220262df509df7ca0ee8de72da41b1c3771b6aec620ec083014149759dd7e6055af9a126795854f70f124729dc3716413442887599d0e1160603132b2eaa2e83deef0fa55914412c06f4868d4f6a1e7df2468174367b1ea584dcad13d6856b610d4aaf638558faf09c5cb32377ac0db56405c769639adad8855301509d1648ade5473b00566d4e5e56d94d5aff86eae666422bf7ea31cefe69ee4665a09792929ff2640dd4d0bd4020b1ff2879cd1228774915bb483434646653f70b81994fd77b24d2b642eaa8912b1e5e2d0879ac46da8a8f9fbf49b1906811e90cc6bf931ab9df8b1043491c6c3809a6857a6bbc59f9ba51ae0076f9e226c578491320acaa5a68416056331572c8b2cf3fc26b66da5832674a6162956afd767234a3d26be513011276b907c80a007beb60b533a63e22a87dcdf037490752889bf61e72d67d3546d0781c64834610cb0e0f00c5329f37d7ec50f3da7571bfd01451509940498b1738870427ce05bed00d3d356ea324b6f086d8f0506237d5a3ea44018382007c809328568040c498f8b8f1e8f8e5faff1b4faa7a6707a224c331332df87e09d63927c3db798e9765e74d6835e00bbc1b94160822e84dfd9fe874d04fec48f6c8447288e41d18cf0681fbb768fdf42f77a591036fb97dc990fb48e95117d11f65abba31c2ed9479e17141b571b3090a5bacb63bbda038ab77ee40e8074d8fad6aff99c6df566114520fc853083e265bfa7cfd9d17f14cb147763e07a15f867696934a7512e7acd0d84b22b583afd4ec72b9556bd576c18ad713bbabd862a20022a11b7e4067cf42d544890b6546154b6d7f601920789a0c116e2f817565e6b7006a9553b14e10e63f7d8a28d33d85f9abeaa3bfcc1a53c27c377e6a9cd47985724bdf9860fa0c19191522343c54087076a6ee7edd05143f4efc19c6d941dec516d3bac64080e83edde49f49087c07d7a2a18b5304557f0679275b66df6194abaa6df1ee889864cd98453f5c632c22a81bdb87cd41c543bc3c8d2284e0c716e9b286b086840e04b60ed491fdce60694b2c1e7aaa56b3098d013bed5eb1ff5a2bb49518431a903bf6bab86f5b525595c50c7ec9cab5cfc25a763bd4b0a74c855aff846fa42ed2343879512cd2b956424bcc79a8d4b3491d2e177e45ad6f9af62c98aa3833e809fdc3e36e94ed33572720c2bf3c7375680dc28ba4b194f1065d016d99b3488929ea6d4848ba27fdb4fb2be9ae879075daa41b94d28d1494a2b229a786292d94f3d86c95a238c8dda88cc5619655811639f0f87c69619fbb548ee87bbe7355e6023c54dfb3efe74e2fafce52a05a9f43da6ea512b2d4313740163e0e50774d8c866432c40bd39c55ad907e3f95722ee4c818bcb009fd6fa9025cef5bdc3a0727812428710f5f68b3c0a94e26c23d52428c2b03c19cf2ed0542ba50b6dbf1dd7b06fdf9348cf7d891bcdbd10205d4ef1a6a8acb017ccbf6e49f38e8723a9582fe62702489160a54cd32fa36dccb0ef1af1fe9e1b56ce198bf7b1235e234bbe0875afe8932f3b9c4c25f4fa5e4ebfd0b2bda4fe441d8e3e85f4b0f873ce9203685a56830fde82d5c24a8e843d05ab8436b2c6b09757b00e0e8ea713b5c98467c6f62de396fb0af78c344f54a4569eeacae5fbc0cd230aa131dd5d89d399643c009a89e7b05b35bccf809a34e3503ce1efb87dbb554d7aed64658bd0111507ba1c2933f2a9526405bcb301b1033e22d5a3127cdc98d9db6196257c751c850b2482439b77749f34e6a76061fddf4683c0b6cf41146df00bb2eea4976d0d3ad9081d3354bfb01e8b7b256c90e636b3321dee024e95a0907ab8536565a500884a15e95b98adf0ea0e0f70f825e33adcd29b27deb62073539e0ba3759e16eedd1755cff0eb2b5ac6ce78090062b65ccc7c9f0521008de5e91e3e8a844969dab1cc0f3c62d39e0f666c10e1d23b4c27f1e34fe3a589f969c1c02e7db83d785a6eeb4817ff1efe7f75e445f22d6fb9220c3bbee804496f0f8cd80f4086bc5100c9249a6adc888d424828abdfc86687ada82fbce8596b7221160ee8e6e65b9326887667ed36691a343b6413e353f7abccdc876b469b5af3343e54531736f8ffb7fc41ec65bad5c727932681399ca53a71c164b241fb0989e4271bb385644491e262fb4c89a1a6704a3855c01d5fcabfb53c37f26491887754801f8c5c92db221e5bed89c82ac0a9559ec729359e9699c8d8159405643162eadd15e23baea6490a1af61ac5f3a2e9e879d214ae15e22090679a28bc696ad9cb2eeb17c57b6b6bb18e01e94a21813357918b10c75220f16271c00a280b9b8ae69d6ed63053abe38564489c8005e35ebe450ee1773028d63d70d6ceee2e114e0434b6a48513d0f6ab81fc7474c04c5d709ec75a8f825940206c09eb4efb0e92abcb7c551b4302db6eb8830b10088b298b9136c42ff7d348acdc887f7ffb97468354f28897e958d7e356e6e84f48790a8ab848ed4bf95ccb8b3290e7510e0a56e20485587f18548c5e1b52d8ecfeb5bc2d3789e09f5ff4a9cbd743eab07d7fce805258d028acd1c8952a270be0625ff1eed7f2ecc2cf1e70155479dbdf0c2622d973352be399f0f99e9f38b9f05470dba4c05f58f1e15c5b879b0c37140655543a408bf3afbcf1bd3659bfc0b2cdcdde4818102ce2128ab2df86804b032076d4230448d3940d7a387f86e4a76adc1dc100b2d8c4140f4e85fbffba80b553756914ee4d144fe0adb01d2d3016b84e73625274a874721c6d28c123586121720ba2c1f5d0da543276c21a9616822ae94c6ca8f615b07498fdf43a4f0cf1253a990e9f307df0f5c889a71987c2915abdac7e8a8200303ae49a4471b3c1cc4bcd5d6c67ddf2f616ea1b8aecf3bfd3b60d592becd4207646016474b82490967355e80acd29e516673815ac900eb81dd4f4957472e862824ec0aba03d38f8fdc140c53f7e0307328bc35460d2863b4182cec87b8e7b24d09be60e68b6dbd9d29921cc53a0213179717cb659e5bf7a2cf17a9d5209826623ab868dcf828425f1ba1113dfec87ad6936ef76de2ae33004367ca0e121b8d66209c4df06907ce9768da1e56aa5ce702d2672d04d098fe0f5503f150dfdd0c120f39f4f27b4bcbd877478653965079f4c26fb4d8e02f756ce4cff2d6fc120cf0386d85c51f5964ce3a5b316552afd2ff28f2a775d68dec2e7f8aa479bfa11ff19cde15a0ba48ce19237f9d77f5e4056030cb4d12942d24777a8ef2bb9233da60347743d9e8adc78b6ab5bf371e452bd106e0d6d0ae86805436381d40099a4188e562243d99d787214212df971fca14910a1c28a8f01ecdd248c488da94d338432223104a30fbd74be107d82c8640043395319404f4f01394f88aa02123afcb243073291a2cb7a054c299ae514083f7480d4a7bc7f7b3ef61e22f7a5002b50c4e6ed499112e9426a502a33fd41588526c8c909bbecf524c3866fb990bff22034b22ed262a660a836206ab6fe0f4f10538f1958527c73c4a19c97e03a514e889b20f03cd03ab41108fc56c55f715c68202097e763686d6f1cf6515d7aeeaca682dbc6616e8099620105943f00427c271cdc8d5638f9c691d8fbcc6f452dbf8b9c0bbc4579579039a05630b2f02ede646e92026f08054523063e9af8115f0121994f73aed290f19beffe2a5839bc0858a804fb2debcd3217fa1c1d3b08e163871bd18bc3a6f456e37a288fbec4a70a85d55d299d808d1f90050511f617e513f004a039e054e2bb526c524cb1569ea768f3a60bcf181b14320385060dba61b54af07ecc710ae9fd0430b09f4d0c36c793844891b10b23e2a5e97e0afb54dc08d54cf6138ba536730729fa2f75b118dbbc3a812d407b26206e7591414eb076a838f9a1ad398c066ad846edf04e3e28d990f3dfe08bc112ba4ff2e65e5433a1c103014d040e099a714294a42867c814242e329e0f8093c05efd82c976f0cc1a3b737314286e17a58eac1834232e8e01c27f3003b6c1a79a2e58e54ffde00c93c2be732bce0b2b35600e07a0511eda9074dda748adf99cec7ac39e6ff0e954f584643eb2df0212d9b27acce666ffc2756a7d8413d988bfe701c58b49ac1233d71514eed4532bc2b469000c6f0aa77285e46770358e9d16cf90510ddbf2e6b07ea76e56737c8535bcfa82166b810fbac58cc0a41dfd2031c3d1eb2eb6c89b91a22bd6577a8f305e57fb83ce146345c031722596b7fb26afef4175f282df57df0bace42e24157706679b397d69dda4d26084ebd425716aa5dbdff78784f4bece97d7a478047eaa1d4609693b97a3564f395fda7ca546bc71b3fb3c4f3edb314d579316fe7fb1aaec614bacbf8c7c1d5120c25e316bc7eb73cf71de0f1fb522ff98f45e225600652c2ad36b74069be59583cabe22d384c780a5569b50f568c9783a35cf77c290d580441d5b49730249376d0e3c32ccbf2660945876cb0efdf502c52b289eab1fd246a6cb270604b29191836b775b410917aba6b51d21a1d730ee8452903951daa7e4622f4376d6992400663375ada3dbdd06833f19a76518ec9ae93906054f6c3092e575d501c910a9a9b58dbae1418b90bb2ae78b1b6de75f7e8dd3fe05987d223e5891a8603034be0d0d4f09a9db9248899b519f62f3eb9595e8588a4d3f727220247d5fe302b8624223f7e6055880c82d6a94341fdd9d5085288a0136ce4428403f8d3edbc61c462849d0121126bcd10ba33c3ca48b0f97d7ffc839db69e279d8cf4a926a9e5501294d4d13998286b60f431a99c8b33e3b3ce5004a4fcea39895b99957b1a4a1e8c94900871a1c9864d41379a6e41c0f71101a8a935eab29085b996b254fba32cc74dde01be4f1081c07a03fa150259697fe4c5582bac5f3cff015ef4de3352c0abe2b36a07a41c41ff8cccd1301b2090251708b1f870d36d4e88717c2f540ee4bce9978659487a854c88d6e40110120168589dd6000e038c97999bfd051fef803ad4245aada59a0224ad5842d1fedcc24889d6badcefec6dbc165f5946f2e43d361a7854856f2c47cceb7c24daff51d197674388900f212e20e7d821c7ced98ecdea9bf1931d8b67eb26fbacdf7634c5cf7a10af1f66f973e3dd4cd85e69dd5c8a80056850b7d5b5ac562d308cc59bd493fdbc1f3e2806270bf99dac0ab201d4a6be1641eb1abf347650db9cf4ae3c7122b94372dc07edee329b60e00e7c9c8f1d886bc2965bc40a394cc657998e8ad8ebee8843eff0a1295ee79cc276348e6a45b5da7150804354b6e3deb3e42680201cdf2073b6e363a19aa22a9caa34fe1893e8adc9da7ecb0ff9f8a97cef4752612d842d8b7844a0cdbbd2c4acc8663bf6a84087407236a801b5c37e3c7ac59bbdac74edd4f015306b929a4ba298bd6ac450e62c14e71fa3770cbaa4ef290bf44743a472a8be6a492aa78188f16671c13743239df15575568128e327d24b553870eec4f3d17924fd25f4782bd16a737a7fc8fe9156bb83be73474545c5bab38fd6252f02e23f2e2e4dbac025ff9633e7fecb7a37ed29a5947d75bc7ef736c78ff288b170f2f375df49fe6edabb3d99d280e14b332275d5a90eb028073ed8c10f72f083ede02a96937a00aee361700a4bdc7ad6d38c8b2c902a0e4d147d6619235c7fd0811c7850071c94b64f6e90770ea4bd37f73ffd2806c9279d62123967f48f6c9c9848715535ef19cf1f605b88e0f726fef34ee7dda1c63dedb329424366605751d91d93bb1ee3a2124022bf080858ad2ca30c1ee66907b6b87aacb993e51a53e87c9ac076db60e7a921c72ee520496f10c26808b6c20411972aaccbdc2545a9313f4c1cb7b77676ef17d7d36a360130c45616264ebc873cd95740678e7d3c8d153b40409c79d5b566f67ff5720bda3968db4b1d43c945e79fb4ca01b2b55e8c16f214b15ac1ff46746fb39b6af82d7710610452558e0eff10fb7eea518bbae0d7bd00844f1ea0bb0213971eaffd745bef6b30e73c50a63c35877db4bb98e7cfc03b8ad3134a912975d5d7f3b59d88bd835f48b9ca484529023a6228d32b91ac60c5ff94f5f3523b2a204b1f9d0d173111e59ed6e319fc3068b8d9bb61446b48f43da280c40be3da1ffb3320ae24450401a23ff7c1d6ba9e5635cb8880183ce47530b49b97d1ae04f835c15b422e4f749e65613ef588ddc39471e6de57b4c1b4573f7b9afd6e355ee6d12f4eecb0857dd76e2a63c03a8c4bb217ccd3ac290d1572f3c44f59f7099b1bd0b07502cfed2f79228775e66c9eee114cb21ad9a09739aa44f6825e46782c773bbb477f35fbfcbc2220e2fc1f00bd66772df5a721402f799893d8bac1fb91e6cf34b46d1ac1a9d949e104c06f54b522f576a04cef8b461573543218c36ec6145b17f49a48cbf83ddebb0954182464b1e9c18049d35235d8c0a48cdb7097278a7b1192e2b9e1ac94cbea66d228d306a2fe2a93130ea5070c1ea26e01afd602c831e143e9c2a8929325c616f903ca948f4b5e6f8a4c1779a49097fd4726dc1ebf8eb98bc59bf6bfdac705cf1be1ec05060a9b090e923b423792feb7311383d2efa9cf7bbffcf51a0b7508030c03058587e3e76bf03d68ac8351e0af9bc595e9e0f6e66bb0aece507eb4d78a17ec0a059d0fc58cc731f4b950af0686900929682a866ae0badb35c9cc655facc4f03ea4c95355374c9b1d7c374d9738112e80126bf563bf469767ba2ce49cbd0bf258b860a715c3d4aafa0c6d04524752a318cf019c24650a4499c77df3aa2b9f9cc91a5e9b9146006c9ae218bf0047656365cab0311fd82f8a456491aecb890b5ca66adce2a91636b96f175f43c0015b23ecdbe41a3d3780859b672404ffc40325706e1b321efc0dd87b06d4073a86121fd50d4234251906a1a0d004e91aab6675edc183edd0ff65c36f0f4eb5b2bb5f49813850dfa0968e745a5600ee5793bf478167767d3c9c61d06838802bc1ee938dc1bebb8846fad81e6dbc3ec20fa8bac07c941e1edbc5de235934184238b4ea732d63559a90e054cc556b4300533555a50b014ed5d054e7537d07d75d1fc51d1fc1839fe80a9dc48d0ea8b9c23dbc60cec15a848613e760737f0aada74d11185f14b8f32ed5b50c0335f942bb25ffc57883ead49ebe58fa2e301d8d0337b82d0835d22ae3d8dcbb9c60ee01dbd2c9e596c04a0cdff4fd7f291601ba4fa0b2e73e0d8b2c247c20079d39e06ca36a80fec6432d46117968540878a706aa8880dc564e43748ff6aeb46232016b51b334aa1cded1ab4d0fe5f0563efe94a6020c4ccbb532b328cc345a4d58413f95821342b0d8f1fb0fdf7db365d526f9c1a443c3e2d482d3ad611e462896085541ac3cc570fa1ea654868044a9046d3ab3d97fd20422b487c2bc84fcfe9a9ea1aa69f2ad5ca4be79f4d4697a324cdf5d84a920d3bde41ea20e5ad514b98b026beaa24c2f776a04440f96fa247f1848e8b076d92f97198c1c1089aeca974d4cfdd101d5cecc85edb71931ac0fe3d5d70738a226949574e3229366c6c1a350bf6d1c570395f18130d038f249c6b8cb7f40973d7c98977499cfe76d1305b0895d604f622fa78256b2cf388dc49529cbab87fe80cc6a5251cb3057bc60e20ff2d4cb84dc8e0b11c284353eb898e590f61f0a94200e988707988e87a60a94410366ff295927495069598cfc366e2fcc89a6d8abde8f619c0b1abb447417b5c8e1f921d961a120e76ea011132c9ffbdff13329895ff644a897278ce893cc26a178f8696b6cbe398190e7510c4a07bb0c5e81663e39da4f380feab5d6905cb25e147b4ec375dddaf3db4df8a29c41a45c27ce229ca3b51c9d92a0994bb29ada66a6a58be82acf51f125bcd9ca3342adf8c051759a22edc9d03dfaaaa8edf91cc75f874820cbc670c729273f7da75c3096c41401551ada194b742d651db3cace0d42c4b385af23d41fc41d82762234a008635b448f3c8efcdba84ddf5c2254fdb8ac28b92d6474e04b3994207e878ba27efb102eb25aa27222a6a2cdde81d3e28da89a34682d6857ea2b009e898eaee1688493af5eaa7b9fe56ead952a2007a4ffc74747b303578cdb4b2cdc08fc04ebdd7ee15d762e3fb32d7c947a4eb265365d22152c323bd323b3033d3e33d6b4c5b1f101dc33c7754040049fa9993eb4431bbad26ad6c13f97a0c878490a6b87a735e2bf1d9882615e822947574ad70864849b09d716ba227b50d41065f4ade46927bea4935e025d39f4c3c787543aea879beab4e5fce126a498c2075d80f92d2a2f91be83740c22c12f6dac0ce55e2cc8478ade7ba0f2412a2f51fda30be38a59235324a18ea1415e827d4f3554444c1e74fbd12ae27e99d8cfd0dfa2dcfd13baaee8c3ea259d70c17a63df1e0307e090f63048389cfb27184c0b2c1bf40a1becd14c042eff317574f4e75637bbf0b5f5124321a7d089d1b466dd5914d0e82a5c97d3caf1bb7a2d27b47b094468f3bb87f625499fef8d5a9f6294f8e3dc81d7c4fe046df7bd9f2e063bd4ab830320881f07b89ef8fb2c723f0f1d3ebcb7cad8977003ba87a90c2866618c7d49de2a138edb155f22b279d93870b5a2fc28e4c4122ba93a84d80ae2ed92a33a446825b640a93692dfd3cb9df9659e3383d1d33c42f4472bbe0996bf11dc4b8e6624d01605650443514136fcc1a547e1b60093411a633604a39edda8f5700ce14a58ad9581b7c52912c6c9fa91891fe2da87c7b796ab9b07fe18c589b5c52e4ed1cd2e102864e587de12b7f50fed844ad08a79c65962901ec27b50e6ef44da75b68f5677965da71cfc1a2ff9b8da31cdb4f4214eb85523fe1eba7946ef0d9c39e80f5ad898e7fd3f4df1c9b82d8f4e5cfe20ce3a729a9072822c569fd080051793d8915b549fc41c32208396b01841f305ea4fe2612e48b8073c4a1b469066fca563c5a68e1df2d2c472d5704418f7da76f318dc6dd5e2a900ac1f2eb9af007a800a9cd47e6855e351277663d121930e23c21b3426c5698f406fa52840ef5abc156003a040baa1a1e2d11a29e1bc4cc53ad63a69054c836655f7600c9a47bc9853debe9517d166a99c5cf74ef7a089f20bdebe6198e911063e4c530ecaf5f8d67159641b80eb3f3d87a0cd76fb41062e6b9f278ac6bf08fa6154895f4285ad32b53fab6d2d3267423a89ca8fe7fb3ddd2c1fe4868d315c279c64f3adc5a5be85b4f5a1f4bb9eb50aff375114ab8fb3d3221ecf3a24e7b87a3069c56d3acc14c28f17e3141846c25870ea5e6d19702039ebf5dc109b8ebe82826757c4c555acc924b889c410c6416009d2ce8095258758b07d01b711d2d5aa483971f250a8bea41f66b91d4d0da933213b84841d1107a477b23f0d89e18f11b57c5c9af8548a6a00100fc91b3587856bc9575ab61556ce0bb3800645929e8a6ac3e42033aee1657d4c090085a2570b8677f083049dad8a70c1f56d6bce86c1093d1a56a259d2ba68ed5eac3367c51f82a5bcf6d59ac4060d21fd92d3f91d90e5ebe4515c6ae39499195fae132c304a1252d7a2b0dee641d6d19498d8cd1ea451fc3760b79579fbbd9e3db674ef55fceee754ec2ef4e95d0633b9fda697052eef838f3e3fac02924efc0a44cb7b0b5c86f5f41072025821b922c50f359d4584e0c09027d15a213736cdfc17226f41e978c5512aca98e413795031c061862e0f91e89cdaa14871ed814cc259cf4092993df78f9888f811dbe1e1e9451d92fc48ebc28a426d18bf2b0223f7fb684bf5ea1263fc8e62d96ddbed8aa2740a860e53fbf00ae2fc83114c76ee4f0a9fd4882c6f83330abce71a64b27426f03bc18444d4a12b293a7dc900c47e3b16e279681408a9dc521b6beb3e302c1d2515a2368d4e9392a3b69132ee32d978b7bed1130dfe56287c3e3d47000ecff48573a7116b8ae96b3f7c6f264d5c0f33678e7010603b8e16f45d1ea3fc3630515722a679e5ff4e22900b9d7856361c83063945381d89a84a9341400a0d7a251cd60ce52589d69623c81ac12cbc8c2ff668f9751f630fcdbcaeccb14f426f68c8df4927526f63fcbd3c134a512384c9a371339dc628a4fcc2260023e7af178c31389ab46b9ab2ed3586f6b5d80654a0c77624dc698317d1c74925c710d2f691a8f8d13fe8e1b1da4aa383dd8c12998e11c2cd42dd2d6400aae8a42f4b7040fea50bf263cfa373acf90ffe8e663ef9a033a7b7606012816ccd18199e2b138fac84f4034c0cab2f06fe0a239c50e4e855bd0c654c8819247fc8e786d0a5040e5b4c53e82db768dafcb69d67789343c19e28547df8fb4e4fe244ef242c8e69da471a02643b3ccbdd60596c8c87357fc80c08cb068a83d55f417a3f6077da12670551de7b03f69920d58c76586ac358cd38aa0388ae1915d993562cf535eb1ad19954d9a2d2a8c3c8b0a22656aba08d455b4e4582be908ecfd175f3cbdd17f5e71ac9dfd2c3af4b539f047ad6798e77afb17cb2941018e175f8cf834fc9be6a869afa180779f5c0c2e13bbb397c56fa7d959b8156767f146e065b27bcf37d91a1bb81639420b9c5b1a73e553e5c4ca9a91bf87b259bd905a54faf64e5e263caabd2bb8bbad6adaa366915f5b808f2df818dd15c730d2c40381a56f5d67e37089f9db5a8b8c4da6868555f3ce3f50f993a2e9f3092ed41ef2133ca597bc6910ca863d1289f2ba1bc468dcb26b3a976dc3e0027de8e6d7652145dcb2d3abacb5850b59708c47ae2650935f309813b32e4536ceb115927d73171950bf352bdc4ad30ab6dd49be686aebedcad6721cb2c3366e54f5e3d5ac47d9ca69db48489e3792f5b9929bc460037773c1c297b865ff21ada962468b81bb530faa6b70f55b351e13f4d8f1a0f9808b11d5d5acb65607dfbd8f13e055a1fc90549c81e859829ee59f25bd19f229b28d3565596e2ea000ac0e0ec37053a72a0eaba19c59d9c38675aac05d11bbe37b5321d3d7420c7a62a13317c6ff4d05a55b93d0d8d41ae496f0f65a930c83b719f8cd90384413ec451c6f24e34a6bcd242222a630e8a4a19beb556d4080dd5fd1129536e8b612c3d6b42f64ae5004aa5cc3fe88d1e8283ea698f469443b38d123584d9e82154cb66b41d0aaae37dbd43a01dcee4f8757d10a7469487dccecdd893e45e62ec26d58cb0e1e7a335b768b681da34135961e9a97388e35d6ac30d1cd89d36a03013992bb328e590056e65dc57660aa25fbb59f7db0f808324128164402108f1882a88fd670a3e40b0e692b6c57907cd5502a3e4fcadb630e1618748bc18993bf50315178ac25249c3b832509b4467322a51f2482b91252d3fa3a844e093b21285ff1de71697a9c359a621f1c408d852c34e476c74b3b5fa5404e3f1d380d08ead9d9fa4e38c44e108c51fb31f39bea1196806a7fc685191a8832cb25da77749d2842739674397ded5723ad4922374fc92e7d139ff377ef3a6996731e3513bfad7ad77a15ccf8228c8a1f3587d80013881ff94e4a1f2ea339f397ff709c6c807c2e6a5284c31b0123ad346449a999c7cbc6128d10c211548912b8c823841952be7d358a6502f6b4eb9176ba18e3305305a9d134dc85371860c227ce5e429df02de72d08dac3f412f7e4e17ba086e733b0299202fe80ff232e645c78774f10139d6ebcc7122560bc746f4e24d9145292fa2db3ee039f86a26e4101a4dbe2527f49a82be52d972cfaf16077f45ba4212d54d9ff9f15f3c187158683c43a66241eca3eca1eb7cef0d73774bd42ec058b2d4e08352fbb2620d3e7e97bc35201c70ee5b499c350a6da974e5fcf0663fb7d079817eb9a750b573bdc014a746c9c2416ebb4ff4e36d2fc532c0e8d6a42d4e51ca64b00eab98816d536ed5c7ccaa206c475544fbd146c76bd1186e0fc113b0caf2110eb15a2666c19a017f65a39c7c0464cf0da2957eeb5e721998a963f573046245e892c222a52279abd1258cd6fcc07717e3b5910282831f54b68bf3d4dac20f0b5b2482148a1d94bec2cb0409d53987698c29e34899425f2f1635ab74911dbebcc9c420c3937302b5bdd82d97a7e07835e7f24f30af1201cea95205c9d442b051e3314a982c29f7ffcfce1aeaac1942301a58e383cec37b33943ad990b6a2613146e483bb2a35d1a63447cde15f969843aa3bd9692b2101f5770cd28ca15d81072e434bf770013a804e6ff5990d4d426da8525fe384ba8204983fd2e6f1b902d5f317ce518fd3f459c3e139c5aeffc485e800797257839d1c80a24347de67d605155f26a1c6c5b7ba222a2e35571e2d6fe995594c02149093a77f0333f227d707784c38042c3bcec7d10624954b59f43a4075ac5bda7a170cc38bd80760078bcd8c54195be49dfb670fb7e6b0dec7fd11c47915adec57253c26180a0a67c046122bc2ff9bc903db460021833a9c548da4d3d2cf3d679b8c7fe75ac658469b1dada8776d389267b20c39c3d0d01194ab7982036b2b46949a4a0b7671b7912dc7e58ed218264714bb47fe296fb741b56620c017a902fbe8492736f171a88881d3ebe1c453578207a497de7d02d859b964ea33188e0323198861483a790c87e89a7b0ccdc9bda6177cddeb5dd509dd8a1c74d141d4036a86485b624774240b479834e46c84aab05c0446471762c00562c30940ef49a18812460864fad93342ce58682ddd1566a3a2730b989d291d484a6c4e55dcc45be0051ba9abb879de23674bdfafd14b63617704aad1fc31d96c3599a2e36804c05e59e094ad5b10dbfc78b6b50267c00a4cba65010869bd5613d9f222fc28abc34610c6e9f62c8235c0f0a2ce24050c32044ae8a4e13f6b6a1a600e4a4d6c4ab10c8d13903740569f91be2e22db7bd549baf4b8245fcb4b0b9f1dd8463d4080168b6b371603a52a4924661479f54232869be05d4846902a504d12b6015a78899452a16b460df13d9d05e2481daa96243200e60c928e64e314ad083830b2f9483f71a7bf8f998bdb7689f44d06ab3e9bed549c8c7f7089c7bebb0a7112b345f231a0a8be42812659b41507d1c48ebf8a136ec7f9da3270a31c8b16e776f31cb9931727ee86f6ae19dbc441e390a8b8af49dcf276d21d670c1c6b45e24e0eb33600b71c51c529a4059c9c0315b8dfec490bae07b92920a5085a8810533c41ada00d745e2f2c9d8d088bd092093490092cd872d2166279d241b7676d7bd3ba15e6d62a59d3a17eee0ead4701fe83679c100bb308c9276cc9d12c9652fb78fe6010982c2248dcb1848af5e5a2ec88f68b1102ae4dd4ad95e48f95f93010fb61462aa40590a6276e02b832429e0c839d4206596ebfc90dc48303462cea247c6ec00c1382082dc552fbe0e5e610491d4e5eb558ea2c08955de1aa1e2ee78ac332beb5305b2e037f31083adbbc0e54d0fbd623d093aec2b63021cac281552cc1300d0ec43af2dba72a62e3a4eca689ea03b232467d2dbf82f683bf621953393ef5a00853e98e77ad24eede8aa8879dc0851e8ee4634a5e5e1b14deed14c90d610a6b990c58805009808c2fce172e72df087ac604e16d3d695449cc8cf81be21b3a2cf8e1a695df8c09004dab10e33369852ff8ddd7ecaf3b749e23ce1564bffa74069c6fc20cf34c00de9f4ec28df4cee886d5380d432c4b039d04353e6355b6fa27b3fa69fdcf83f1dfe15a1fd2fc95021598e0109f802b1bd6909f0a17eec20d71e1c7c534c660987763e27126474600fa058b9e125a85f106c58cfc587127e21ec2386e475d5c27211a6b5cf38546b792ac7ee7f21ae7e33f2bdf44983d1bd12a4e9dfc550f96f15a6ba5ab50a53a5407cda39aa5cfe1c5b89e67bc876dbab0300ab34f68e10244b5d900225771266b0bfd2388edd50967abd044522b0399ab7a486018161dadc227b8ea37d8217a26332f2bef1c041ef233279e458f599d169be85d4e1ffc7d566cfe954154fce091e80a54c8b2f8ae37c9d8eeb79f8abbfd428bc4a0ebec9fe50a2c9108fa49fb01230ada07cefce9b4eddc31cab0a8b4d5da12131d6edcc37a1de372163e41696e5f2c8a23c4bbf1fc526b6583944bd3308c7f49c400a69c2c634cee0e1de85762969cb5fdaf2a2b456e927e93475ef2cade8db1bc113980be3517b04cae4788fc7384493ed6725d353c3080f251e9cd848b7478ed266187ad670b26b8684ea74e63ab4dd02711a01182d6b081f770941199c941700fd0819d2dd43b785fcf0f90c810b67e02bc9c071c352e48462064522bac4d0f6b0703b24a27acac1aaf54c08f8d9addedad6cd0e93b99cc3ddc5fd9e0012b007febefe64d96580a13c7362336fdf40007f7b3031addaf49dfcc7c8705ffe0f6f6294300cb21238e49e9c9296afe62ca909d690d710794b77c7076880c45d49ab8e6fb06b34291b1db601ac696d29a82085d51a4465e35f5df966610f54d819528e4b95821c31b8d82d9e6fd70ba8ae633962bce011376566f8ce4244e020ca35d95fe382591092b29e1e2e0f62cb9b515b51e79f63fb092918a45ffd7d07020a42d8f4137865949ddbd0dd1e85b30e2f813bab14899f066dc18aa6454f3c67f0f2b3a13b882a054bad416b3a2e4f30b292580fad01fae98fd7c8f15ba960e8f243f45a271efc9902c1431a36e7cba1b282587f969de83cd6fe18a82be7b540010f63e10d3cd0b17e765df6b880ee7a9acc4b081013a4ce228b4c41b31ccc71aefee580bd92d8dbc9c15ce69be616c1264a62da039c50444ba54546393e97bb39edeab42f2bd60151108841c335e41fbf7bde5c98d39152565cb0374b2442a442bd3527896cc3dc0cbee6761d988132830d95154c3605cd5a53a3505dea47719b1f3e383c29f45daa8789d289b6d0f690d4cf80c4752c7c847a75dc6fb29493ec5991b99f2f64dd340ccac84b5131b2284b94f4ee881baf9269ca14ed79f22a60220dd646d82e1647de9664431d41a4571f0ca0df4572b92de3e471bd901ea742f2b2c455f3ba64232b6ff6224163e12133b59326b09cd68536833ea2f8c5bce176272d24610e5673466e5fa41d2b55a528a6943100d2c4f33c962bd96cfac3a2b782fe689e6d40db6d982909af88de25dcf107290b3b8480d6daae31ec8805c3878dbb9d87a4268b2cf0df1abd33b100c758c476fca1136ec378f51a6aadf99f7139c55ba3677856b5148e1ab233e47b0ada2e1f2b434f23c88af2e0a29cedf9e8fdc1183f9210234f0965ef2d0801436f7d3be2d7ff4d4922842ea838ac8225e6d635bea50d0bc600eb4ea1e37be7fb8a9264c5813916b89f6d863ce70b27a0b03f7f663961655ac033fcaf76ef235b26d6e7394dd703d7f98aba354b37f3cb232473432b829aec3d18b02e8f8317bfcb4f310bea396dec5245ed5b3d5c9f9fc2ac18546421983635dd3d4032f8c63f5d51d02205e4909ed460da32477de22310df606aa9e73184194d41144dc2e4054036fbc086eb0ea109a84b1f5f33b49dc18e8d2384a5daeded52dff169668bd13ba687f8a8b83e1bb9df4ced0d3683c1fc39ae177573bbca6c8577c09eead6cc38209f969c76621b0009b476b49bc2ff60d215c99f9d728fbc8c732826a8b2b0c75fa297f9f29091f1b95aa3074dae4c05f06423c163212fba90aebf45fb16ebe4333a88191a7a778d2bb2de3d0a7d3867127d590ad6efbbf747b2a3493d606fd1c4daf62366120671ce8d8c894988bea3317473a9209aa11716eb4c7c88dd71464de8cbd2698d0e5c54f0949073d9b1376e5fd1f503a4343464aa898324122a08bcc90cb2d425f7dd890f95d293c94dcda68548358c9418655a6a9a0c7ff102e416c10e38dab7946c30628523de9cf950d1f408e196bdb4ee51a3ee1a7798354ce2ab5818a461a94531c83ade93de24efda7de46b56b0e0a1965a2a902d75d63c6251d8c38d663a43424d0ac02372ef7299739aac862bba8f8e785021c976459ba0753c58014a379623ca81239c27eb02da0c62663aaf0b67b74f4e2a201d2a03fd7a001d894464f925b45f41114336483008616cd83ec045e4e3118cdbd57a29b4aa81c1e7fcd1a388a6a574b47b671f1dfe8ad7f5c0113d71af65e098aecf79135847c967f4329705d8e4fc2313b74b757754153c8d2d7d8a6d3e6d130dbaa52e43ef97c67b74724d72809622708cc7edbf2613eacd68d750cfc19095aaea8dca1b2cfe9edb8c38464f42f44f289180157aaea9bb7ec104f9593600e2c77685ee4fe1ed499d275b73509372bac089b7d000284e0323caf0394a36e6910bc938c64ad627b8895c3897a84f24101ceb80485d3d23f4ead73485cb564544ab6d3f603bb51a28d45782175aea10f96658504f169bf647e2aa5b10671b6a1f3792f698aa891f9e6294beff19503585b494c90e465f616d718b2b5210823be48c2eda80dd5d429e3484fe588d3f7420812f20f5913b848ae92de1d347ccf17778c6e2e05d9e62d55467461ae5bcb20d0e6b6d920f40468b20058680f833c6dbb5576fab8929aa4009d482504702f7cfc9dd8277e19ee5c78abdb7e7cfe6c7160a97672b4aeaa66ab115a11a212854b090e9c07f6b4ac24685d80ca8fbc56e508dad760780b9a5c0d8fb4464068ea5d6083b6490b8aa4a06e0431a63ff424fcc96b16448223b5b34c157ea45fd4bcffa46b8738c016a168b64cf76769a80502441a04ead42bc65620da516d0ec99c4a72a94057bb98ed4504e6d6e4b29698774dcd5085f9e9ba868d9b8219274ec8d706402196923cc0db4a21e8ae4e593f15e96736adb51ec5fa1f53f8185b51ae7fafec7eb535db2bbbbbb794324919040cd20bca0c3b93d4cbcf43b2bbe766bb7bee3159b43b4bde01ec21729cd2afde2be498fcd062891c23f9f3ba4c9fd31ddd36bdfee55c7e8a019eb7edb66db46a30e72f8ff0d36b44757bbad45495a9d7234a2c99399e0edf57d2a45cc86f522c806fd2293c618fdea44b18755d288ae038961fa0364210f1c6816289aeb8d72187da401f85dab03d4dd9f4233ff026cd62c37611c40f8880ae2c69630b551c17e6b8bbfbdc7dde9138dcd968eb0455f2287d2d818df32cebcb4f37fdacdfa6c64a41907ed7e5a8521ae59986308129d5a69f43dffb8074dc86afcddbc31168a98ad2526f8adab4b46985bd19c4b60fb3ac0fd8947fd789e348691536b34d4a9bb901a029ff2b684d0b1717dba9b0fd674d6cff2f27276fea2beb8d482009a4b475cadf087f95756ef222bbc50e76ceeae9ec53db596badb59db56fc94bebfbb6a38de26bc53232285a564ff7ded30feb992db36360be0d88a7479106b9f5fe17979d3f10eb96cdb2330862bdb2730862ad82da9904623dee2c8258a7ec5c02b13e6d94dd79a310787a4331ed6c02b13ed91905c4da64e7138875698b3b8f20d6a49d5120d6a39d5540acc39d5740acc19d5940acf3ce2d20d6dfce2ee00bf820d6dcce3448106bbc338d0c03626d778e01b1f69d6540acebce3552e508ff0930b5c128d8f63e2ddd56eb3ec2153b5e82df145937f80daa21cb1a80ad7405849774f5005afa0fc0b282b029ffc003e84a0460ed21963ef47cdfbd873cdbc6ff7d1f846595c0a6dc4704745591a0a57f75209af2f7749d82e2f897c0b2eacba67c7c556148d055f551d33d554815c2f6af31bfe15f5f9655a740533e0525057158d8eea2484a60dbaf9c075dcb729aba261db0307bedd7d4fd1be6e8f676826dbf96f77198e37eba4e4159400e34e2f74a6dee6ffb233b4a04b156ea234bdec0830d559d7167f01e14692b3ffdfce91b34cb386ecee3348cf5c8ffbc396dbaa128621b9efe3698c9955a6bcdb171036d6d204776d65481531b8cef687b6ffb0a6cb8136e56f8f44d10dca56585a48d4a6d845e7db43d4766c753187de511f136e53aaf088b07f73a5adf1c55a597951c000f04a0adedfdaff527ae0d722f97c31f93e6bdd6d61554af40c81466d3dea6b29d6da532221a04dbf93a2cc8870aabb49db1dd34181495e6e7cedb1daa343f7f5e3b73f8f5e3e7cbce76eeb69712762674633b7b9c9ddd98cf76ed95fbdeeef6767d6e8cb62e1537e4997d615f56093ffd0a5a1d0356a1f0e14dcac5c69121a8270f8268dfb16be3cef346db63fb1bbe27a04c617cf0d26d36fe1f381a7c70bb7d10d261d20720eabebe0afffef6a0e8abd16fdb8fbe027f7b165f917efbf795f8dbc3f8aaf41b690e516eec568838e4f1c15a6badf75eb883dfd33f78198e3ee370a7eb4ca2c8328edc97b817b92f91b81749dcd33d7a30dc199154e4461cc79130ff2421ee8f3e3f89fb11c9699325c4fb7487ef853b2149c550d79d233f0d7b50ed4bd66c6f9636545efa6fa40a9b77a77b2eece17df801085f7de40d77bc21ca8d5772748fbf7b4c76a0bcbb0b7ba0e7eec81bec9b3919bfaeb4acd9345bd6b87db0294f6d20aa1043d034b14cbec6a6bf836599903e788aec7cf0556a535fdd6cfa79e3b61147374c9a97f6292153cb0ab23de7e1027c828020670b4184ddf8cb5d840ef79ba620a867f4706f441010e46c22ecb6dfbd6bd32c62e3984514b1b7e74c3757b01d5ffdc3c528b861bffe5075388f04db3f573cc2e4087c5f7b5dae6badbd3b87bbee13d1ed6d05180c3202dff7377ce975413679e9f85f4834bb108789257cd3c6b65fe32c20ac2d4b5ad4d4a6bc27e991a69eb3b7cd87206e4070efbf01cb0a49f069d1d3a27f9316a9dc0e1e2267db1cd3be0e3ad6c276ef86efbd9bbdd4e6e26dc3f88bc024f5920aecb2d608f5c0cf92fae461b4e9b5c6ffba7bfb4203e5a45d565e452cb1ca99b79f4359326f7f071ee88ae5ed0f80b26abc7d1f288bc6da1a056bed11bd9dfc8fa617539e886ee32fa19e886e779f4d1ae673054d1c17863b1c698f58a21c88377b05a8e6b79a98542a26f5b946f704b13f6ade2dccc26adc729b8bae3ba56d0cc8da186d3db4d6bab8d4eea46d0fb73ff6877ba1a19dde941e455f5a96fea43b5a96de458bb42cfd8a36d1b2f42a9a8596a54fd14f6d4a202d4b232d4b25f1f5163f77e089a6fbbe94c4d2df70c7e433fe6af078bdf0965349f8f86c1c58da1eebb22eae0bc1134d3ea76786644433244227112f97d46adbbfdbba4e0685e55bde9f45bb9216d269b4ed4f53e2f2f1ec8cd3312e9fd74f8f0bbb41366567241a595c7930dc71f95cc594a7e18e0ae9459e863ee446bc94c9b67f0a8aaa1c7122a2edbf32822e339f79122f8b8ac2d0c445db99456293d85a114d494c0c51dc4f3f3b2b84d32f35920b698b3a4d4f1ac8e6379bcf36ba27880f99f9920bcd944c3831dcd94e5a4a2cdb44a5a686f41790fbd894c380a44e420ff2723c69a099df4e487701d51a10141414c8e9b12aa934da88723a711a654b41e1813882b587e87d6e01397db2596e4c68889744bc7419d1cca6849c43455a74dd34ba120155994dc96a91adb33af396162b8e5fdad918e2f0f6f50142ed21923ebb809c36d932faca866ceab44292d0623865c8a3421cdeb6b598cae87365a12c2b2b9c2e15191aea5456c2cfb585b6b0b08844ce738505fc5c5da84b4b0b25c14ebf8bbe31215ebe600e772852b0b4e4cff585beb8b824e1f46897fad2fc0e8915dd1365c9125f99d78c6bc6a5f9ca3eeafdaf12daeaea1014598584ba961ee2f7b93efd1bdb42bc748e1be2a5d3d01f6e147d63fa0ab943eee9e515aa42e848d6586c4bd9dcc36d675460709fb49dd9a2156d935825d76553342fdd2ef1d253485bf39878d215480df274edf1f3c34b18acdb28ba26517d7c64689c68a0d0f8e7f4b6519aaa688be44403f994b6086624ed8c005efadb010c01e1834d6d00084cd883dd2fdbcb0613ee70313131bc57be43915788ba9076e6250f44f326d9fe3789afccbb64096d09f155cafbdf98af54def1fba7681e54c81d704c154f2f13eea0fcb69db693157d830b99d38d1d77d216898cdd270d947a170d14f3a5beb1197d8564f43df51d62d1b748ea57625e45e65133a5cecc9bf408ba3d9240328ff508ba9d4202cd7c4ee99e20b7fc991087b75148fa03b17ea92bc5e17ea644d1de8c8c8c8ccc8d5996edee8dd9d94c88e366dba220313335bca4a9a99a93b648485b44da991462aabcb979b94939a1dc984e6e6e4c279a964cc4b7cf9548dabc43db7f1482f98b5da2acadb59612652e88d0c4b64b10826be3b0e912847cb1e27ee7d1b594080c7ba048e8f9dace9ffae238cf5a969d79ca3fb7dfe9635d341c014f888159224f39108e33e2a5d70a926e44009ae579d02bbfa3f1e3d0dd634047e0fd88834252d0d2dfc4496cfbf808ba2a0579e9949581b78f4fcaa2c0dbc742945581b78f63946581675de0ed63186561e0ede31e740580b78f7fe80ae7ede3176549e0ed631fda52bdca9bd82580376f19770cf38661de2f506f5e2a704159a518ab04069af237efd0b67faf289941876cfbd70aba2ad564b4f56fff9eb47543c35b1317a7c3db776a0302562d6fbfa3ab97b70fd295cbdb17e96af5f64d7445f3f647baaa79fb2c7455e3ed3f5dd978fb307475e32db9bd136dda8f60216e6fba11237e440236df8331a0f51bd1e944e057a927a2db2c7fa38627a2db2b3e0bf215277329e88aeb829631cbe2882c1828ab6b82165956f7b2a99aaf3a58c90cbaea7cd0d25548f3a67c1e48f3c71dd21c7190a5188a2cc53a13578843b5b9cfd9410bb2680138ce18fc4017dd807fd10ef89406e2693403be8656c0d7e805bc0dfdc3d7a013f018027fa31f8003800722013e48dc6c083cc0010d0082010b50c00f7ae527017aa50748830a0c69e03609688f0179904d053918ba10109b1cee70346c5caed10101dae9cdf62dfad272fb17ddd1727b170dd2727b1a6da2e5f6357aa4e5f635340b2db7b7a19fda6c222db7d71308c31c7aa33e8320cd2691c21d95cf610d10dc46a91d861c6902b4728f461d0d37be029a034f01bd81cf80ca05b4106f016d3ea767684fb23748341594107ae547afc082624238b8bde2baa245e3f0a87f6d4305b406ecd7a055de865e7d8d667d0d9dc3d3e89c4fe91b9c96b9a112801797108797da6874b690f00940114bd14a112eea8a4a37725839ac545c368a7c891d61c56527f0030b431c5908628c4f1764327d90a90b327d90e94d719bbaa0531044eaa5a611afd72b8336a834da79dacfefd5b3ab673db94adb6cf6babc7d2791f33c4f87c160f5d268b44aabb4adc23ec76eda2676966d62f73e5fe14bbbb44bbbb44b73992ddd6555e630efdd619bc3be1fee9126089a4c1c69b2b06ca409035353e332cbc2463ce57f8408c96c498d2cd1a0fee66ac00ffeb5dd57a80f15dbf7b1aeb572351c576e14f7426c5ffffbed5128928a1fca7ee4103686d2dbf699abda44752a8fd2a68d6dd487dac4b11dd250596a4b75a92ff5eb4a7d15f2355a58ade1cd8e0500fd4f9add0b698a2fef429aa3cbb7902dcfc2f23478f66559a3229bb2305f8d6a5efaa0ab9119b4749234bb0d009d6bd0990b34e53f3283ae3218b4f49aaff2cc4b30d0568df7cf5fa0adf2fdb317682bf5fe39096dcdbc7f1683b664de3f7781b668bc7f0e83b660de3f17d156ccfb672ed016f9fea322cb22b9b6405b37ef9fb5405baaf7cf48680be7fd3318b445f3fe390bb455f3fef90bdab279ff3ca32d1a46007812007ef499e63900e0fd33c9e579ae4aa9cdf8309a529b94a7a129b539bd8ca6d406e56734a536a64f694a6d4ebed494da987c0dd171b40d627f60a7b04134681b05b5f1b7d1d607d4c6bf465b1a6d81509b1a9a529b540d5fd6f03332357c0cf930e4d32049a01a481b0380587a6b05e69edb35681bd3411cd1a73ba2ef885e3aa2ef5d3aa2968e48b5cd8e28cf56c8512cbf9967dbdf06d11657b70a0e4e8534c3dfbe6a13f5db0e8ed2748fdab4545820d82aac101b042b043b048b046b84ac248f816e1b64834642b6bfa78386d87e7b1bdb97f45239e98c97eee88395ecc0d069972952b192f7fcc2d517933ef302bedafaad97dae07ac9d71572716c3b7efc66774921f077a3d884ff5f4cf2d02d8a3f22c658638d6b9090fb10d5a0da8f688eded799e3c578db38aeeb3ceffb4e11932c60371abdc4d5f1506759f807fed184b515149960a288893b54dbfe9c65a288898d0967a28809cc0413454c30c14411134c1431c18467f1b66ddb7dd38bb8bb61bc5dce762eda4034f18f8a81f8ca1bddeff67520267e1c843506e2a5e31ff687da600c0407e11fdbada67b332bb0edefe038c857407c453ddcb9e3ae7b7cda1aa2addfb116306436e34e07b1f7da6bafddb1f6de4e665d7ee408122449b6295657e24bb629569f0d3111a4d26f57520435894dbbe7eeee74ed7125e1a58f4fa789f8002d0525f04e5f2088d9101b80001044dbefe05d57dd5e9d37d0eadc815667ef05398320882f18d6307395477d8d2a257da3edf17691f062b06b90d71f3ff880cae0884dffd33b7839393e39c360d3bf1d681ac26443a57210046560c4b677fbd15a0b96a5d7cf7177254510863bf647302cf6e22dbb60670b6efa7a640df214a534c423b3fe984234815ee9bd5bd723831ae35cbc225318d3be7420d6af16d3b0d48468769bc879ea16431d45ec213cdb06faf3600eb422cfedc4cf9ea562dbf7421edfe2e78fe77bf16998e37bf133066b48529bfa81da94698578b8fd7de9b3f891dfd710c7e74ff3d0fdfd47ae842f04f8df87e07f0f7ed5fb5c0df07d767777776badb5f6de7b2fc6186fdbc6711ff9e9120f91fb0e0443f0bb0d72218fc98726e10d458023fc5cc1a721cf0e9e718883dbdc36f991e6b68906413da344f2705b2429b5b14fa90d89346d6cdbd18b2be20b41fad18ba4094c5cc941fad193c899b547b6fdfa65ef736d88524a696ddbafb5d65addda7777778b7facb5f682235d24742fc67833afcfb6f6b7901c8d74c90991fb6e441249f1bb2d823fd2a4ef3689f4a3b7610e1249b4ed8fc8a06ddf7e4fc390c491498f34b7a06dbfd366695aa26dcd14c8b67d1d88f6b5290d141fd6a710b284dd60b22836ee861ccfb13e3526ec26dbbcd836a28d0cdb1914c77f1bb2ac6d665344beda685e7a41571b19b4f41fdac86097b0dd9ed46641591c8f87511e5b6c7fcbf1b0e125c763fb5b15d7c319b1fd39d736b32cbb9d61b799b5b7b6d1b00537da46e3b6ed8c8d0c9d87af1d61ee565bed49b7d7134e2e5694298ce9c1ac0f6d197196786444e249ac6ac9b64e9ebbe299978ec453fe1b8f9c24545b4fb9e495e0242e5a1e5efacbcb179e55ebb3cdeda72b7a8268dfdc7eae8faa7b133fdbcfcff6f3e3c1ae70d248a02b6667af130bd1bc7eb7ee5b0949a513143daab0b8c8681a3135ca1a36372a1cb0a506d0e5300ff2980bf9493bcff33ccf582c168bc55c36e572b95c2e279a15d16a2c2b2aa831168bc562292e97cbe582b1c183567397fbf8ebd2ea908c88363bcff33ccfd7ebf57abdec2c66531e8bc562310ff2980b9d3e74a2d168341aca799ee7697abd5eafd789492c168bc562dbbf54e3c612671daab24a34abaff33ccff3f57abd5e2f3ba3d5dce53efef29f92789ee779925eafd7eb35b2b3d92cbc59d5a0beba1f58508d05719ee7799ee7ebf57abdea90ac12cd8a421a099ee7799ef9f57abd5eafedffd5c05222085a446bd57569e7eb3ccfb3fe54580daab12af479e7799ee72ae7a5ea72c4731cfa4bb34194e5ff388a625724d2c67789e37797c65dda7669f8d2eea5d95bb1100340836df28488613665966e52190649b3eb3ed214c58e34c78d34ff2f96951c963901cb66402db5d44beaa5135a32d8f4b30da0cb7c5573d5d1e3a5dec72ef7c5ed86dae34eb9a78db24920d897a329fc5b98a3dbd5b4f3066efac464e70edc7469670fdcb448da1904bbd1ce20b8e910dc79046e3aef4c0237fded5c2a819beeb89d4fc04d6f3b9bc04de39d51c04ddf9d4fe0a6edce29e008aea88cb6e74cb0519bfa27f0b4d31b9853e1aa7db91dcb264da6954dff055baff73725380cb3d55a2b696e8f7509824d10cdd1963035beeabaef4d0bc4b43f4cdb237cd3f218bd6993d89664df9778cd57de5bf20e6163bbfb9ab288d8bd0903e3a5dbd9f6db8834c590dc1eb433df3edbd9f61f59dba6ea7aa40db24060ca9a8dc336d6834f21a406d8c6b8d7a6348a18d54d091e4751a44fbfbaeaa5623b69f284782fb62fb6fa08fb50cea48a637b7bfbc30f7b14e2d83608ea19fee2a81b7dee407cb789a9f0d1670f8fcc22f6e877701eef473ffabc81360c010ff85bb87200d39fbc034c24b5a91307086d7ff079b6ed3df879b43d52e87d0d717cf6340fddde7bfeee6badb5d6eaa1014e5bfceceeeeeed65a6beded7eeebd1863bc6d1bc779640907e1e70ef4740907e1875da94486df853c265f3229955e043870e45024439d2ff71b496f9b8620c9b3ed1149c1e70640b7f7dfd79087277f6729a534574fe8fa6cff5a6bad152cb242eeee60f75364adc5971bba17d730decc2d08041d0885e8fdf75ec85337d803b7c3cf9923b9ef36c7fd4a8e6e87dfedd0439ef0c1b7618e9024f2b798cba4f9f994983437fc97bc2790ed2170ace4261fc48e7005126252f84efa451630f8ce22f93ccb4ff60d7d449f17df90657d339b22f2d547f3d20bbafac818dace71dfccb2be9a4dd17c957d3e32e82a7f5fce5fcdb272f6f155ce48d055ce5fce39bf2ccb0039673a859c939373ce39035156a9faef9c0839f8bc49c8e71032853149315c5bf45eb7d87dabda37eebea04baedc7befce1b58434cdebbcd2fe8657b324f8268dfc430af74bb23b7c17b7f537c41540a8a6196d591e357851df110520c13890d0d0dd1968b1dc23f385a6cfa2aae8e8e62f9a6f7c5f6377162d492deac7acc85328531bfeaf6e28debbc2f83e18824964c4e4c28a79411a5b2c2d2e202ce60ae1b635951418d292e2f2d302c9b9d504c2726a5976781d95749248dc2d72bf5b5d14230fb7f9a54797127edf3fc3b9206ea556a255fdbdf2bac7bd18019f76c5399179bedd3f64c2f96b24d9e1029a536fef56f4c06bc31da72d9f4bdcf85e23324bb462f3d5b6d2262622e70ccb39862776f622e70707b23dfe2188e65970543f56b43a5ba2127c7ad85f9af294bebabead407e38831c6d7d6d306714fdfe3be1f2ef63ccfabdc47963c50da36dc593940feef1790c9ef17409fd3749bd8400401ad754f8b76113a458439f09366edf1fed33ddf9bf01041406b9b089b2dce0d716cda1cf7b6daf41598b7868d3fd36ac351478ec0397cefb5f6f30573b88b73cee220d639cef1901255640b42cadc753bd8aed68dbc9b0d794a40448e347376f7dde78ca39e91bb2e578ef39087f36ddb66c7b3598c6fc883efbd38e4b9d8da2de4b19bfb0e793890d3a52944efebee429ebab9cf1ec8e56f6416114b76f7260e916d1fc73ece199e4452507af4808d208cf6799d18724789a7ec9f76685810cdf0f50a79384bbffd8cca2b2c608cd60fa349edb24b7d5d2e9d19bd43b329fba0de91d1385c3665df45e3f0b129fb2b218e9b9dc99dda1544330c4a026ddb0f81d80f83866c1b0ad9f6738a8bf5930b8ac9e5c4a5bad4b1e3e886535846f56334d58101faa7a13d4df6d81d69b26cfd5ab4a97bff455b219bbaefa2ed6953b79294da5c903a012c62c53970a4eefa9576d185b1e97b45b475fac1b24dc7a78f7bf84ae5e96398af509e3efee12bd4d3c7419625f334e5e96321be0a9ffed397a72e4f1f3b416d1899f8894daf193c367dac045ddd9a97fe3e30cc829f32b06b0c9f1dc61794855df8455be0d3bf33dae2aaa0e5112ad0158e795904235cd01516c24ba0f8a4b80b8a655e127d4fbda7ddd3ceaebc39b329e9e97bb5417cf2f5d37f5fb53c7d18da628979fa31d48615bfc9f1cab144d7893f380cf1296568c8572befbfa388b368f871e2acddddb8cefb32d8435cf970045a2db3470de4f25803bdbccbabbca8707070707070707046154cc2092a12e345ab6424958c0a954c4505854ae6844a86e3021c170e12383e384de0bc707ce0c0704824d0ea98dd43fe0a4d12680d24497305bd41e353ab71d16a449b86b6fda4a3ad209a9d68358dbda59c7018e253ca36235a528692318abe3842057165542a853b9bd57ab768a07f96ff9567f9f0e78951caa9e55554c484080d39abd8fe608dda0089a20cd29c39a51c31ba2922922919234918332a18e1e29a34e7994b6df2b3c5508e2e72c872602107519118b4f4ef3a511c7f3ec7cf1674b5438a215fed08ea82ae7654414b99af76c4bcc40265ed40024d59d68e1c45beda31cb21065ded00431cdf1c5f8b9fea3e322bd069975d809a65a99c38ad184973149ba06f68d46687143b827654b123b603099bcc883ac1c87623be1abdff0e226759dd7202ad664919c77067fb28c67143e1848ccc68f5490728ab5174209a57a50b627dab4d64748f8c112a9d99a7214324e385cc6c668999d7cc13333f334033b099d84c15e29d1152b1e0544493045a83ff8ca65902c5f1a7a169aea037fc536636e54fa3a7e101b08e0cce2945c60b19229999cc4c664692992921339399cd2c31f39a7962e6670668063653c54c6c4668fbaf8c543064666969b1bab4354d93a16ba41464105905192391400a9143e4165624659a0c4dd3b5195db3a429ea1a290519445641c648249042e416e410296b7141197f0777015b7c5a906879b564d172b66cd132d432851659cbaca5eb4a2d5dd789e238bed9e98b9f6e2d48b4f8b4bc2c4ba58450cb8b6b79b5bc5e6c7989fbd59245cb162d432d536891b580d1326b29dafef71c921921aa53f068434057c0acf85151f12069aaa21837728534554e90f038f22462ad2290fe9770e7f42f21cfdd296f35893c7d4f90940f0111e2463d4a175ef14b2a2d868a885b9caf192a3e0aece3a2d5c64812c6a35056f449d310f1526684680a2924cde96188a6b85f40ef828b215a3d1a613efa1883462f46a2118c713682612c1a6b63d78928d74b091d541d4d2d8b2be80d91212f8bc86c8ac8cb235ece8a5c52c61d453b92380be65d361f236c0c1a9f30068de71834c6462f46a2118c71e62b150b4630d4c622cbba19633423c958f3d54d188f5a19c17817c23d3a394fc838a19d98715233c1c0e432bd4c4bd0d2df964c3faa1fd615dbbffb7fb3db352e3ad382ded468365564533448686634443446686434456886a8cd16db69aea0344468e9bf0292346718e2c969ce88a43d39555490487c729ec8ede43c21e3847662c649cd040393cbf4322d61fa197575d456832a2be8cde8ab102a581004f44a1cf12111cddf32246975fe71e2659bd8e51a0dc9888ecc5e3ad78278bda4e15b10473030e1cef6be0571f4b98a98d4275afbbc8e1e0d8db618c9482e20b94848907c484d905e2418c947ca29245d52d0891067e9f73f89f9aaf444dcbeb8f2f56b4907a21d6d311a1ac9b639929146b29034c76d8e64a791ec0819c90524170909920fa909d28be4830423056dff95fa91e4a8875863c29ded41ad3a5fd4c665467fcd20b2555750258480a280c2091f57511247f4b0e0a6b67f9b34e748d34481de9c4332234453d83bb82311633ccf2bfa9ef87e3e1f1fec9b82a6be20fb09f957045afa97deecec78face11e96ff878e681e1157d4f7c3f9f8f0ff64df1055956016a42becaf15304bacaf1448934bb8d34459134c71369fee92de9157da7ca0a7a23446d869c5e12d145d8316ae3a23642a850b12068fb9f4873268534df8915b236235a528692318abe3842055a8a4bd03449a03727914fbb6cabedc61ac8e56534cda9698a782923b22977791ae2b8d92924cd5091387a93e6cc8138facf458ba34dbd8c0c68b5cfcc8056574a694b95593692ae7ab4a96a35a5214273724134ef4cc8e3ed15d265a3348c97f551f47b595f45b3e811456fea8f722c91e3e5ab122deb5b6db3b30d68758d8c8cd842b349ba521bab6b6c224e10a1e54022874fe9046dfbe7f0c98144caead4d6010a1d6a3c2cc1c36b46072874a86d7f1e5e3c2c51e2b4a2b0c1a5c3133afc9c40dbbfdc26cd6975b9655a51e8f0a3c313335803c1fc85f995fa2b37a850fd509b960fda82b9ca099af23f67b615302a7ea2d8ee82c247f552f9d4aae0b1554ad024b6ea08eada3765d09b31a8925d4463057ae30f436a9a212ba321a239a269663458d8fe31ddca0d2a7e563edbbf05f355016adef24157053083a26820f2470d4492053083ae68b2a0a5af68ce4b0a314b4be6502a28dfe875a0c6fbeece1b3f1415d488cb2abb7ecb0a0e5daab6bbf3c60f4505953282da77e78d9f09d47587a48b59b8dc32c6cce85db71fe95741e9710422c9df4871db3763d07eb639d2bc689a21174d230b71b8ecd34ace1644d5a61f13f2b86c4f0be2e84f6fd29c289a7a393eea6574a85f579da2fd7788418ad466072feb7f30bb9a36cc1c6a03b41a66e71ba0d5e40ab4fa1f6b204dfa0d1668f50ba963e84da75d364cc8e3ed91cbce0100ad5ed9b9055aadb2b1069279528b365565c8cea6b20da0d5a88d3590feabc9cea6eae71b40ab5376c601b4dab4730e5803f5d078233a1d98bf1ac867d481f95bf3d58e9997ab1d60b82c2b07cda6aaa0ab1c5ed0d27d9583081781ae7614010bf96a8790977809945580330a50c3325fe5c05dd0558eae13c571c42e73863aa148156a1511d5a94269951014ad8aa980a8463a04db839f494db3049af2d79ae60934e50fa35d40cf38e38c33f008a5724249799a27d096c9fbd33881b64ede9fa609b4657a7f9a22b4557a7f9a2d684b7c7f1a26d0967d7f1a2d68eb92de9f6609b4357a7f1a25d0167e7f9a24d016eafd6988d096cafbd364415b28ef4f83056d9dde9fe60ada4a797f9a93b6c637a2dfa491c2ce41bf7fa6a1b14e8ba65eb2c0ec8ab3327363c6d0d09774ad68daf54ba6d3ae7f92b22b0d6d75c694138a0a0a8f6868d719534e283474d531917f42be89a6d4a624924f1afdd52310f7e8ad1ec1b8472410499a2dbbbedc3051bb86346034d5a1a1371d52aba2a037fe9b26425449b1fd7780a1fab1fd5550d0d22deb660c9aba29438b1a0bc423b4a059255edb7f98d0c44dd1cdeca6d33739bc489283c8573761d072471190ec10f2d5cd17b43c83a80035cbbae182a69eb82992e3c757375ad0d27db649733aa168fbc370e40d0d894873ee6053f55f5cb20ea0d5de0ea0d55ddea0d5dc06534e38d4100200c4c99f6753d3711b0dae91ba56c72b1d5f5c5a58565254a36fd32e5c2c5e87a090298c39726dff0caa7c5c6683b32cafd9947fd789e238fe97a6d74c1b35cb0a8b78cadf88eb880f925792eda192ede192dc0364fb5ffdd5be259ec2643864625aadb3d4de11a7b1124f61daa56d27472e1c455e3226a94f6bb6edebe3cf58dbdf34c61b75bb69682fde34c41bb769c875dea6a1f76d1a7e79d330839b8660b869188e360d47a44d4312b5a96269d3b0446daac9c9a6e18969d3d084b2698872da343ca56c1aa68c9b86236ad310a5b269a8b2b269b8c2b269c8d2b269d8e2b269e8a25f360d5fa8cd6f4ca48891b124b24d5fe23457e2b54ae2b30bdaf6633b69d5ed8d61eea475a7f7cd326883f0a48d4e92089a9cb493132d055459a1b4a3221d95d07799ff70960ac816a21aa222a22ab2e93bac2a7156104b66dee349cc9c87f7a0f9ca23e2acfa7a157919791d79217925d9f4eb597938ab9e67ed71d61f6705720aa17f0167d52e89a8645cd2b3e9d797afb8a71f84afb63ff246c622222562deb069cdc38c2c2e33c96c38eb93a9542a95ca457a53dce3db1bcba45108b6648fe524729ee7b9f2e2f17abd5e2a1b2a2624168bc546574f4a1031954aa552a9b29d792168738ad79d4e22e7799e282f1eafd7eb65fa4e624262b158cca4be5eafd7eb7cd95910e1837466e92cd3199d79f6d585a0cd261d573a899ce7798a2f1eafd7eb45b248ecccceecccce6cc542ace7799ee7ebf57abd5eafce4fce66af1b715b781239cff3045f3c5e01f9892ac5f7d5700a9e17fa6cffb0abaf0b5ab1fd412e7761fbd78dcb4f6caf67fd1f13acc24cb01a6482d5980956854cb0ed7f42ab5fd4519a486b23cd87b6733a3b129d9ce318f29c36fe8a85c87dc542ec567691ed9e19bc1f7458a865f0be229d6786ed9f39ef4def07db9f7a1d16eaab9e2895fde2aed8c2a0455749ee4d948bbb62fb7f1bb98561fb7b75565f2ad7c77623dcdd7c7046e86cd7c742a4e37e81e5c2865f505950ae2b044ca25cd4e606623d6d94acc6cb6bfbe7122c75a7734b3f28e7ce162c754f908aab58ff597e4413131df115f7be4d78b8f7fe6aefbbee617c8549f33bb26f1528153ea27dbf38e2f3d99708cb506636e5f7e945a13915fbbe8985ca4b9add8f3e8f4a3a10312a8a2f506696e512e422e452045a3acaacd4798a94ce402e45a0abecc345c8571936056dc9fc80b66682d0560c10da829182b6481f44415b9ab6de25c8b25c4e9bea41e641fe917de41dd056f8fe190ada5aa12d1697a032c3ca374fdbccb01c4a32f536341f53f333350f43d6fcdfd7f792403540357f431f6a2e974ba7a7c607090111e356799677395d4e6e646df4e6f21b8e83fa52d35ac3f8353d34a411af3343e233c890f88c18129b0186c44ba88d3f0d12978124b1199ac46590d82747a70669c4ebbc90780cd4e692980cd4c65d482c06122ba136fe2c1ad3f018980c1c869a2a516a675097330a54ad8a36bb6c4cb3ac0ca329ffaecb305a860971ffb79961a71ffa04f3d24d1b3bb9b6ffbd28356a83b2e4f4e304bbe429487cd3e5dc9e61204992cdc4d29b1986828d345fd98a623ad9503c6a1da3b479da26a699b68969a2154c124acd1437d21c95e022941a890b2731127cc44b222bde516a8f464f51cebd83a7b63fe8abad92660a64765fee4d4c03b94b6618b50143345135da124fddef7af6fd5112fb7ec863df077becfbd9fed8f73f0b64dff7ee9b28af99ddee3ed4a0cedafcdd992a25db9fcbb0edf8e525e32744b7c705a40e7fe6f0670cae6a6c5ab36ddbe6f7dabb32b2badc7e43ecac750f42a6f62beb5453fe66b97700c9ca12356d795c97f3c699ddf63a8cefc5f8de0deb917d233a9dfc9feef1c9d1c96f44a7f37d0ee8790e7ed63d46e4e880a268dfff5459ab2a7abda960a8f34a25da94eb6fe70a7ab6f9e29db7cce5ce5ad22445ba45fa559a47f77dd35bd63d41b6cf7faaacada65c6553ee3da8e9a7c1cf59539dbc5516ad2973db5d4d7d6f844a277ff8b6aa426f7fe1bdd7691e6da0deaa3d92bb7eee367df3e7694d6dbeb4b3ee6c4adc5ec893f74676d446abe849dad903bb90479baa5d79b2999337ae20edb8d2f67d4408416f119d8b47c0acedb9bf1ac4376a88c365f76c4f75f2a6a98ecdfb6e8ff5b7ef2529f6c141dd03f7a64793c99d0b8fb3da667fba2788c7fda63b505eb1391a768e92e3bcdf7ee3381ae2a81d28b710dee3e7bedb6a980367d7b0fb0dd4a297389bdbdeb71d2cd7914394bbbee791d8d3f4be078a23cbf6e9ee9a17932603f625535bdcb680a5bc11af130485faec293928540af728dd811cd46f9f9242521195d2030b450e91939292327e8a367376ca73dc8ab85279b386c6dbbc88896cca7191d3a02b2c86c667504c0631e80a93414b2ff215a67979063e036de9f7c766c04b68ebdf1f9b415b2fef8fcb405b2c65d0560b6db9bc3f26036dadfcf6b2a9316031d0560a9381c380c7c034da9a798d683efccde5396cafede5d2c2f2f2a4feec35b40db5f14f691af4cd4a39a353ba460db5019fb4796df3ff42baac7c8b66d12318f70a09644392c40f00ca46aba84d81a81add1304a451f9deecb08ce2b86c5cd2166afc8c1a49934bf9cd04400d27dabcb162e980c3d6b319216eb32ccdb2ccd96c1129cf5548896dff6bafb601749a29d18958782432483fb8511446467ca72fb63fbdb18f2cd18e348989586c7f4a2263fb53d28fed4f4751d8fe7464c4f6a7e110b63ff5ab6b7a50d2562df24027b6cc84cf8c0fc84989ed5f676f821e6912039dd8fe353361fbd7cf8ced5f3f20dbbf7a56d8fed55e0dc303d13489a19cb4557f065b87048ea37150746e5f9d3783edef1b6912eb90b0fd7de33828b6bf6f50d8fecef263a46d2ee0b090ab8531b6bff5f3cdcd05dbdf72a4490c0bd9fef66a61fbdbcbc4f6b7f86a930f13cc2ec17a6d33415169a5d976dda3d8fef7da7d35b803b193d156ad020bb6ff898bb6b0498cb6eefb9f70b1fd71695685ed8fb1252b12db1f5f6edf07e206fbdb25375b9ad156fdfb9e69529ad5cd55d22456df348979234ba6c4bc7a02c3099cfeb224a284a3a78f79f8caf4be4a31b92733199471493dc5b225367d4a71824e4fb9805d65108d210b63e80b9c73db372f155838423665532a9a97b68ca15ac7d8a08e47bde97ebcec9ea06507b31d1668aa2bf2b2bb29cbff710c5df526ab36ddf287717e1aee8827f5b5892c3c526fc299972118b4b4a8b0680434828d62a32a46afe340969190891167fd9b14a926325fe5951a1e99ac797df948b521240a67be52158560d0954a0c14191ed9f6c322cb52d14640948533c50866593841a398af760c8daaa0ab1d5b78615158c485456058c442864523a190e8081546bf429a145109897ca0f8512289236437e573e0e84403c5fcc5d9f433fe2f69fc22887bc29ed0076f9b479d3064c790b3db623b623b84789c137eecf8b123e80353a0eea1470f3f3dbc7a48a2071f2f7be8e9c1b5a426a3832080eabc7c4cea61b4cfa89322754a4719f446898e31ccd033a903703a32608826c5e2ec8185916d5fe7d419d291893843bcb4f9d30168c3c74bfba5d61fa3ff61348dcf33ba6706e68d50e9c47cf93814c1418643173810e5d0440eaf1c6039f8283b580e413a429e59c2e81d31d01ac84071c6a037ec67bd53f3f2151e00ebcc903b348a637184702882830c872ee86ac7161f0e45b67d1c726882b25464c8e165592a5a0e305fe104e5e083ae70a4f8381207220e07a2131c8836d21471207ae510b47584aa0daa229d219e50f7697185155444b1d5d689c3496d70847c85bf256b11b54efb1904c1302cdd68c20dd98d2e6e34e106d1aa89d56b055bf95805b586dc90dde8e206d1aa89d56b055bf9b8417483e806d10da21b4437885641ad21ad213135999e6d3fa626e30ac1301c8d52b8cd64e24cd944bf40fb6824f40bf44b3f410f6922edc54bcf6294a47c78e34c6f5ffb6824f44b3f410f6922ed857ee9977ee9977ee9979ec5288951f2e114ce9405d16c99c56041af1f58cb926dbf85d652d30150f9a47027dfcf269fbf27c8e51e54418d5ef7e113156d49cd4b25346aa3630cca0432b67d15186af4ba0f8f63d09b7c72b3209a2a3325ced26faa24d9f655662a4524904412431ebc4d9fab98f27d26a6cf26142f50141a0a19285ed816a5664f43a0a953cc9e4e3f65817af3669b1d0cd6b59c86548eb010e10cf194fd141d0213f78e23b443f355cadbdf51b25343a1f94a558442065da9c440a99d86708a9da72ce86ac71628d2bc1949b3f448f3516a2d244aed34a472a4daa033442586af7484d16221558eac90e68d0a699628d27cafd31972228da8128276c6d82183da28d9a99de89d25aeac71f878998497af9f9489dca1511b9ba281f46f1a887cff42346b5e35af1b5851d00f2c099898f2a570c7f4251328b33caa0d3a43eacda6a2e14c8113b46368c716a3d77d98c311c2996254d13c92d6d13e1aa671341c214749f9d4f8501b4b95104b691c41309bfac1c1c371bc4e348e9eab71b87696ec98f48ece906ac3ce18646c580cd97644eed0be104d1c213d6463c857db5befcd94d0b69f0ab2ac97b79f8a5996cb5b1f4fd97fbd7e7eb6b53058916d79e004e148f1753c368e508a06291f154d4506e6c093bc99b40e245eda4fd13a8abcb46fa27524711d336a43b463a8ded8d7f105bd394205670218dbfef7f2a136f671845234f8d9f695a029151949f84ac711b4946dfb98346fb689c3e108813842273842546cfb1947288a6d7fc311ea76683b4aa88dfd1d43d5861d31d01bbb4306d2f46dbf149acc5066d4a667771f82bb4d93d9b66f323381a1d641d23a745841635b4714540714f4c72e7ac217431d8c0b212d62411f53c3e1f24902870c68b0ed9b610bb67d13149f8926dd029664776fd22d846cfb3dc06c0f413dc47c650296429e229734533e45641a88064c048a79947067fbeec7573b86bc7c82ae766c414bfb36e563b140592a325022cb52d16caac85738415e7662d0158e14e64d69fe68f2a66876b46dffa47514511bfba1abdaa02aaa373bb648b2ed5b1d61d0f2865a078ad6f1436deca77c2c4b07153445061aa4a259960e28684a0a26f4d8f6ede304f94a8713b4b4b36ddf449a3754d8f641d22cb9305f8b6d3f93e678c5b66f429a22658215dbbea77c76126a5edbfe977aa7a671b81c470fb5b1af2aaa36e07801bdb16f9500836ddf54156d4b2275c0a8cd4c34718446da8610b5b11fa3e919a594a8148b16f0aa9e656a4600800000009316002028140c87c4a2c13850e3d03e14800d729856564e2608e45992a5200c330c21650c00800100191818481c00b0700f9057d44896031ce9978412c352a1522c12efc191843f78751a7c290e5e74d8f9d8f1522462c98e12598657979cf36e2faf349fbf6338cfd1d16325ac3500e8a62316b1d14f0353c94400592dc6da33978eed09b8f3a176265145749e9de3d981e8723b7d2ae5944e2173336ec3d090aba66b10ff59304166c553a3469c9437798edc8794150c3ca75e20e05f39b078e179d5366b395d0658ac743adaee4b417691f553118584ddd98b6a2f887d2981677a8a49ddc60fdfcc2b2ffddc2a95caf5c0c833ad595c1f2134499a2f7e2f405a20bdf9a4c4e8843f0aa6c7094f6c63981437171a99f1c882f07b1c07c8154653a5a099730fb55907fd5e3fe17ab9d417fa99f0b31c6b6cf9939ef13d28a2a0130348532773f1c236c2286102709c200568f3c35214586c7d97d02649e7b53500d82e0a5c9ba5788d4de9248da4ee27c993c4485ccc0be2289ec2a2f9b6bdebdd1f8f1fea8b23dc0e77abf579038d64af88b3d94b01d60d43ea42d8bed594500c40dc1a717534e2e143e4f4b14613c0f6a9d1ee31acccb60ca107ea536a4930a85658ddb194b0a83b4558a8826c6c43c81f2d38b5914251323469dee9967b06ffab12b4f4aa11947cd12f16b12e1864ebc336f5911e803cc3d6e5ee53d0c7f533061adaa91c82dd0e1dbcc2d294acf04303e3fc78f2504baf152ebc4d677ed63e74317d95c59116b084d0d2afa5be6eb0ae1da2625de0061a6e65d86a1602e4386dcde25d4bd7aad4c3ca01bf6dcf56d886d5b17b8370c96a37321a99b45c19057626976bb257548743b2bb92eababcb375a6572173014b13078e7873014a2777c6bab476c7781a28ed75c26689884c503366a1fbc3f512be41e219a8cf5a2e7d1d2f7f9dafeb31c1b9c4f8fd59a45e7a6c0b622e3c84f1cfc15ea139c149fcc65bc9b75ffa29426c4e710257b1b9d5595dbeb7f2942018de3c48447a6c72e3618cd0ffc48d58177cbba42ec0582a6eeddbafd961d7198f759abf4d8f5e94184df24a3e742bf82fea424687565dda8d0b15a866726323f9a4d71d838f3ca8f9234eed3a1e411bca1a617416a6a6408457b7fde17476623d9c8da4d3110eed5d7aca4f5f6630e0fb2f8070b877e4e72f9edef8be28942b79914d8fe4a763e1a411fd71d0b5324f32554ed4b504e5f8f8269be8fc7bd9b207396a23cb9020a13c69a6848883ea9c13fe095082e0db10ac39621bedac746a23504902f48e3caee619f5968a4a896c162223e05065571591c3b92e4def6b630ed558fd5bdbfe9baaf0f42e3c19f95b0745ac9a0706ab71d501f503380ce40a757f6ae1e0a6e69152e6f772245565ed67cacc830ea06941d915f49eaa85330f7562b6dc37ed31c05f1d9a26af009aa674c6e2141b151b7dfb5c3b1bdfb566d1b9c72cd51a0805b239e80e8cae6ba8fbff4e035dc81595e8450fd17e63402a6e3455029aba93eba70a76bdd64b3ac188751594a7623fe99a83d3bf792a0f2ea95a40f02ab5ac1ce34a2c02ae9d126dfaa9219ce55417dff5c250b90ea09ebe51ac0d0c2c73aa22f526bf28edff42c8a9c64f262544117813e866519f27cac39603c71f46f9abab9a305bac047c71a52dcaaa2b16021b6a5aba4d7851864594b8358a35d9409f718f9e7f3a215bd7a459a11ceda9db20a567f923fc1b3c11f6abf3f78073c7a34611a20f813768d67a8d934b1366b01a27d3f880ea727b6455986fb6500c3f725e87f8ce3fa8ef5de85713a6dd0a36d830d0f24462004c20966b14ea41d0249b4329c7f6c605fc385441ab7a3bb7a7dc24e4db8d4d756a6a343829d526e8f16b0e585578b1654bff0f94b282fb33ca5c9d66ad1d35b5377162c06b5e5ff55256a0f51d579aa5d6a1756cebf7b213f16d6cd033fd6d9026ab25454b17e0b43363efc55c8b5a31d795e1eb04c6535623d9e0b193522dc8e3bd22af8042374721e0d825decaabb98dc131f98aa393805a1a56bd17216e9418967b0c2d92dc485ac8f0c86f6c19422d28657490f261632693b1d643c08c52cedeb65ce60ae7decec40eefdfc25fa1215fda01e820c52671a26e069b9291536542780acd18e361bec2fb59d6933b3c8f42b3067e9750342a9477ef2b75a430e5585e828acb4c25bdab430f8543d2a831709e7ad783d6f4e2916f7549c47a2d551d7e1cfd5dc1639a79120f1eb88012d704e7c9637520bcf09fafebf1dff5fb3862ff55756eba19eb8d0c18258db0b014ae520a06136ceca5f8952419bb988b6e62547c63fa9fc02bd84a61a7efb278d235def88375ed02b8e79822da0a1117a7b8859aaab431b346a33127fb947361050fb98e01e4f445b5b7651f1c724f5b0ccd6af25fb0645a8eb62410d72b356c05670c5bb8e983cb5ca4f71f0912be2622b3b0826ad046e397440f038ae08c60401a702399a861ae9cefdbd9e908794defe3f92f26743a1f36ed784b4eb57f19ac711f6815265435fed2246de3e2a151c5104b0db1e44716e749360507b809da40c1729f1c93c6d5440ef3316fa1e17e62ff4e14e370897507509246b141b965d093f7c9c57dc64c1418cd6a4cc38c9dfd0fe3e39c3909e26ea16ba158a4143aa6eafa15b74b60d9d79189992478eef29283ecc5630056bff4b50a99956eb215bcb64c8a01e3116743bbc6f0950a5117157ad5275d80354bfc45125eb85c38b0e912fc26f30553e441e12a1d999c09426e8796e4361914070fb7c57b7e972a1e2cb916396597bafdb2b5a38f170b58d7d808c7dfcf7896cf9d6cc2dff1298f17c6e8426abe4ae557f94861a923aa48cc975d52340f196410d21dda014240c85ad5b6700364c5c14bc42aa829dcf9acce46a22f1a8dab96008accaa270a2318bda6d0d4d787bc539abec7bc246842c86e5685e1d1cd4969521b53f5b233ad7815c5e1ee6b76416240ccc92702b4b8762fee2531988cdbe099739c5c12db2eb6413e9dd9e9be7c7f51308b970351d77e68f1e26fb878539fe3bf52f1f2af78cfeaaa5e3b57bd13a22843ef41054c2b0ca487e9d5cbf44297d046d7166fbc148a4e9d354cc8795ae83c9d0467e0926802114ba981aafe5ed982855d2e98400f0582c52d195e7cfd4f5fed8d575fad9f9072a1f37f0b9425a001e76d6fcb2734c487bdde42d7bdd67ad93b5216c35356a028d1085907b61eaaeca37adb5b72c13a41074d63ab8bf9b64b0c38130551e706b9e16c6b062458cfcfcef372b9836f5ffb07b221b289a05913f46d8143393becaa2562068144c1ed057a32f582ce4bf7e8508a44b56a329c8f4a709304763cfcf8e6e1986f800e5f4fdfc38837552f20fd52931043539d95f6108dd9c9ddcf621468a1132d929a8cdd8f7f9b09782f2dc09ce2f4df96f55fb5dc4e40a0195045332027cd807e9a01118df5d40cec898f9ec19574deb4e8e5f9af7232fa2f9cf5dfa6d03858bcc607d35472fd6816c5743c883a6aca8814143fe55ebfe7e81a37a30ece8f6411f9343d99570fa0e01832d028745d38cf1d368d74747dfecba36c61e4859cab72c43f78ac82ff4060e5379f2eb08a1f029b3e5af4dbb8d3f9b6484935230ff0c77d5539c08c839a78d39c237a83a866137b1badd7dde590d796c5bf05da25d08af8072fab60f4a13c521d43d0887ba0a2dcc8c4c19374e7e19987fd3a79d5c08184aee8b2e1fda3430ea8fc28d42f61b6a92db8b5b189c2a45107145a05c307b42a35aeebb6607746ac574100d2b7d29fad7b674098be4397672d6556ee7554b08ba6cca7758cd71b599dc5c79af17e645805bfa8e81cadb9286e39fac934f4f9236b2b6d09e9f617f232d2128dc8470a0004bf87441ed76c4bbb363da97adb0f863b61a523599a5d6be934fd7794fb297de80408122e12fa8ff3005a94e0205fe74a53f486c94c5dcbedc639da62296e52234e07cc0251edb17fc90045ad9672082ba5386c7dd1abdb121d07bd97cb43c8cf9e8df2364c1089a9fd2bb8c9f4745f8b651ba00105f1ecf5eb67d014d16bb0dc8dc70b040b8b753caae84755af65f022ffa73ea27eff3aba930ca75d8c12a298cdd93edb2d67d2626ead45714f3bf847b779cc2c6c978bfe50e7b5f172dd3e6e6decb660e6aea30c5a7132b553e71db357b1863552a8118e2174595b2e1e1af56f5e011a12ab013f58d0bf856b02260a21dc98879f6183c7b16b3ee1dcac90f8b1a23a5dd4bcf2576227562c2b912012f1a575afbd3a9d2956372bbbc3c18fa0cfbcec7b6b1593ea53fab3cfe539696ab7676ea3efa48e89ca95e567af7b37be17bb9ba0f7d76b4bead4c0250a3167c8977a1a52f6c345382eae8a19646f29c44078bd5f6ccc0a8e5162d8ba5e9c29459b833ffc4ca1668af02da031d5f716b8dbe4e9372aade826f4a30eb58a52f0c90012d04f977ca29cd3cde7151ec77e56c52ed6062e98f9d59fdd6785534162b8cacbe31c29beecfc3c881ab3ae2aa069d9ebe6a97103608371b03a4daa172b9fa3206cd1592dc07a6264362c787b8017ddc77135000111c260e908893e1b3dafcced95582d46444981d4f0c8cde99fe4393a0ae9c7ff15d39db4c66302ca96a19f02b2fd1ad6e7d28100b4c393623c170c51faf841139d920a4e06cdc5e66aa94e88ee23c28625a565e5630635974d3effd5cbc4f57a2e2c3d36a020a4b9089e661f18ce9064de015fdcd55414b98d5849494a3b4e5d05b79c79a15c9c846486bc21f9f96fdd813947df0a49f6c7cb11f3cbd0afe2943f7ee7724eba17cd12870fa2506cd97bed10ec227ef14900ef7227dda4360b3ad8c5aa1f2e59d3aadb7c709ca907167c48a8dd95bb35078d159f37809fab386357b48408f212c1089df076a3608f4a70debe4eb8bbb2aa7344f5d8495609964fbb52613671acb1aa8df0b6b9a0ab9f84205b1c1d87a9a60355a4fe65f7932ce4e69a6c61053746e9e61e9c4b772511bdb6bfbb6b4434eadbf9cf4b8bc6ca781a1d8bbb5aca091ac6ad17e5d294c3b745208fac95a78e253a4721d7cf59800f9f64ced5690272ed1f2e4cb5e1cdce39e49af844dad146b675e8942ff70e1b1141a8928df6928067e3ab2ab43b8032a48f86e7359fe3f1a645a02b84fead0fc3ea0f6fb2be2af9043bf46888471258e55a0d2ed6f3bb165b05dceaccabd63ffe97a364248db40600688469a04bcf00ece18783d1da9cecc7e0ad00fa56827b498ef95162ae6e3438ae1e6fb6191fad9f9b26ff5d555a950a1402e8dbd7e63c7288b6a8432d8fd3cfca14d05f48a45fd1ac5de8c9e68991095f108196a2f060a7b868da01a3f7cbd7a3b37f5fd9852e7ca80b7de8c2af7aa0bfbcd57f77e18a423308d21b740e96ab4d7c7d05c3874870c885cbccb95d7b9cd87b4c3d85459436b6c0291e5f499d64f7ca48040d5a32f6946119857848280a718806aa62a067afea5dd1850d0e41e89dbf6fe85231000945e6964326008e8041fe3f1e15f966624538f41b585584c1f5524787dc2bee879163c5ca9ac01fc9e52289efaf00e9d406a06e400e073350870e27bc7095b6890323d150e2300a852d1c87194e24b8dae0857d4dab3dc5b43093892d1ed5cb445f485de206b1c2fc574a276e4830b28f04045f900083815296fa29e5603c9808e8964640110992c2f71d71c24a4ea0d50a37005bd3acc9ee09d379e9f5830d1423851a641980c3b8aaae8a6fb95a1279c85de65aeb82b3d93227298695dbcf4dcf3ff65f512b7286679b86b998438a1ec363c4301cae8430a738b3b458ee1aebca11f04a9733188e2e30de253243bd7956864e740a4048d4d6c3ffd5afffafed587678bdc7997082215f5dcc36ae4c03adfc8cf9c77defe808b4d7633cda82535a5d4e90f54a9dec90aac4736e7dd0d1cf43a4c2a827449b9a63b4e499d2487bf0f5985f63a05de02d7c6cf64759d63e3f816687a2e36542f372aadf5758054e3f851c517b88fc87e9b6d0f50eae7dccb1cfaef329509b240604e0545eb4d0c6648ad35325b34e4bb21c8af35ffb89c92b0fe77c755f2b2ba32a17655326c8f6d30a2527a7f66c3a06132890ee4ab27334e61d2750d9e356bf915b17d9d36a8a4c42f49778a3224d25b4b764bc99a5654d6720a8f78b177b90ccfa446a706f133e8678f50145690ce9237eba09d05905165207306fca7efe80bf3e96a97597eb19b49a0c0fcec08ac9667b5dcc92c9d675194a26ca5186267c5eb286d01d123487e0638bf717143012e630441873bac40d595ac31cf5c31ffab364a1d50d7403d845ba5b26ee3a967750b73aa7b67878143bbdf50a001a28fa2f4c84d9a791dbc8563f8f545e6883f77b3237d48dcc01944066118eb890424b673ec9cbc6cf33541875560041c729df86760bb54351e50ee9e66e2fe1b98642e0ac6db64785f989564e9ea619237ffe68ac6bad3c02906b4233c311fc4614c541c0e6ebd9b2799eaa528e91a370921d94b7f2bb5e223006301fd139ca7cf9b04d8c4a06701ecbcd8d0958714fe671efb857bcdead4dd1c376d79ace35aee8e6e8ee7a267b239fa08f07e0ad6a46e91f0438e2c0d911dddcb288b3f3212fb37e6c4e4ba8bd1209011fbdd5085a020eabd806439c59e7da51eb2926a6cce997c6f258b25d7c463585ca22d405d85ce7318f39cc3f1c2048efa0946380db3c2f3d7b806da4f35d4dba4640522c278c36c667d65cf29d3d5d37d8b2d2fa7dd1a2f9b4c1bb630a4651959d09cae977d178271998686c51af23adef2d5467a3aa95f90998a562b8bf07e31039b781a60fcbb445c02ef2b05a73c9d08aa5f3c18b784194e50bd7c5eb6dedfec6ec94b6e713e467bb6b2e787a8d1472aa16a39b55336e8914d4b4667d8a4d90365a95d1b7ce4c300df4d2678af487952f8459cd3a87f22c4487761672bffc63a0c2c6786c65d274fd35a6bdf837c3dbb42eea4e2f01e05421c9593ce24f9e8df7e6250a4cd0d80b1f05dd0eec5bf1b06191bd1bff259092242df3b8acf325920196396ed12bca1c55c16a76187b2e4fad53c98aeadb7956f6dbd07eee15d1c57f50a4b8f75036d30fc02fed6ab811336560ca5898e4c50357a4acb6c020b1ec94f7492392bbc8ac8160e5ec1f86aceba6951a5d9bbbd9d64f9e2b15dcc57bb414542813ff63be71dc152ba5aa762309404f3a86adb9176658c295d6773e139515f9db535950d387a24e94cf20c9c4e08828a5a06f763190121e4a8818051180872739693bacc4baba1c124c4929a454c4354dfd94138434315819f60a61e1619af8f87ab47428247f10190ef4f8f5f81b9b77f6e6af73de55e11e1024afbaf5dbfd1501408696aeeb70565745582f1918b4a679f6375d934f0644374b29eedcd43282ff7c02cc088477458e59837bb9764f2f2a5d6d32741efb75399053fe1fd98f570027c6b75c6f7fd28e6bfddff9415e38af975aa70c800e3ba25a390700c34440082b3fae3587f48b1931062ad685cdf0a2a1e2aababf222c1ba658099c96a400bf983a6b998d60afa40cc586ce2b448a1bafc8d424210b7453dc06d05bfa0a77e360641da34be00cfa7ce29f5a3c6f447693213f0abc31790669d4dc5653cbb6075ddfbacc1b96e0edc0e8a8a484b8d7a60b75476017b5f959371f117a3adcf91a3538f5c4e214a54164a36f7632865d371a9df3a1c14edb09cb955bd8d9b8c130d29e7e1fbe72ce8669a795d3fcc757a072ec836424aae1d7c0374e4eefb8c154d7872b032b1ee5b8c5f92c3494f26dfaa36e67394d26c0ea03cbf9a3bf46dc510e6297bd638765898740e168dfe63fad34f8a62f0ede1d8975da2c8206dc26888dbb84a4c10b407abc4eed3469e6fad67760e13d25cec9e3b353890fe3c3640c51bd94f99dc65561230acffeee4ccb49abc18ee64e1d6411cf288327c72edb32452358ea14345df34daaa868e544b4d199f659563de511779367da8563d5fe4aaa7c0edbd48117c257153f6134742b1dc08a9d4a5fd8ac20dbc3906949d8169fa1b1852b9073157f1608543190376b3e01b9402a7023eab8e8e7c825be230b789b9bddcf4f56869352e77ad74c2e77fdbf5c3ee4ea3cc1013d26a376491c6d136f4782108b8815a9bb0432f3a380f853a98d3136934f08a0f66d225807c681e24a7f744036e57337db5c028e67cf42c95178eb19a016cfa40580fed0d5da4985209a038320f288038215ecb8034e439418207600fdbf1bf457c0dee4f6ca48a64dcb6f414d31f0a2c0ed68ebcbf812f2fccf608fddbd3d825969aa2c64fde0757d61f20addd79e3c972a7f513c3f18e03bf08f6231dfd2de9de7b7b4b56458bb8d6ccc4523ea1a81c62a7e045ff354d69e63bce9035301c1111a31df36abbfcab9ab2cab6a91e711b3927746a8d70187c851e5d916d7dc9e6ad344163729c12a2868cc3f38ac20b87e5c078f232b738f5503215d5597a7b77b106d7e3f2c02b7ee1a1c97f4002fa343f72e1d2b2a88dcf4cd167c04cce54344d1aac9cb6e48a0bd7ff8ca04771fa2fc002ce5f6c35b45cf304f19b9ccb4192340f64aa92d4343413dfb7df4fceb35bb69d073eb4d8eb71fcea627410336dec6d68011f18b8dd4ae1ba1fde0234761a2e24e2165a21678022278f6ea76db968efaabaed3ab58cb05a22f92e85ace73a886142d775189748c85d506f60cec333067c1a71a1db9d152b20ec279d61a8846784ca757208a6fae2132dab1803f28490efadfcfe9d1dbfd4326220aebbb920a91e3bc44bc78db2d485050c9cfd1f4326d90d23ee2ebcf5c441ea07c6436b2de33843779c2cb130213afe69d7439648487b2d1639ae03b87e1054e71fea3bc57858ce47797e07b43515d0fb2e1a29cd8bb8f54387d92e747701a81bc0cd6c670a97d96c9eb60f69894f08960560dea6b4a2ce0e4c0cb0527f7402aeb81a5f4c0e483427fc954b3668fb0bcd6e1284309adb2afb909ce732b2a497dfd28674720373e7bbe0268e8e441e5ec6bfaf1142e886b35bfeb9378b1412955e34af698376cb5ad8a74c6dce5aa05950d50e13883b686a99aca8b4c03f4d6eb1c47feb106eaa68bd881a4ae1f82f76015e443d2ecc31b79437240418817d705b3a93f936aba31d9cf6ad6e7ac0ae139e2686928bdc70a3edca2a7bbf1a14934fb430ce6328ab72aaa9c06611e3a402ad50a9bfca1095e39ec3d9253e5ee7a87f876887f5fd79cbb809fad051cbb0055f74e1a8464a4e4c9865cffc605cf995067c9a3e7f786256ea0990c8030787b20b3aa146c4193403ee0c878a36be433bc22e0efd44a0d3eaed767a70bc7644cead2de26e0d76480c4251f4e96492123b0fe2db0457b0fb305edf44c4fafe939515ea23a79a983896e00a97f98dc7ed1eed5d21c5fef55adbb624ad71155acdf1028d532010133491383d208f1d444efd38749aa22d9831e22bd67a9025e5dd902004e951cb6f685401661e97abe4a93c3dc16769f2a2dae668c321f6b2ad7c2041adf286317fadc16ca7890a7abef979b1ecd04b4c54c5df62737a4934f3990d7704b3177d29849e9bb21500168a7043d52df4ef18d83547e83fa0c10200b9920458dde5613c648b80dfa73d2d13e06d0ee54835eaa171fdd343e8ac410eff713176ebbe362939cffc4333488b1f21876de887fac64f7672b0bfe88a7d85dd8eaf929cc3b085242bebd121b76a84eb526ebc4980c01c9f5de311cf4723075f1a8cbd61aea14f45350d78f89b7bbfb036fe20fca4b704363286f35663de3d399443f16172db211974c4544cb9959de83c16efb9b8fdd4c351670e2512f95cfb098805e342dcd5632e4a02175810d4c9758921d8e3bfae16dc1d2defe32183970751ce99219c24e2c64b6e142e0b3ad00708290cda652b8fd819241d5dcfc95b64b373ed0b13eacf4275f545d59c8e8d42e5b7707df115ac31dd302999884280fc707805aa6ecb99296ec0e6a82fb6101d7f60617b66ef63ea6466d73500d3029a14af1abdad675a59bfd9c941295c2541b84c61729802dcd6c82ff71fb47660c6574532045830e05ca04b977217fc57f4102de4f9b592434dbc339077f0efe398213a92443d5fb425324973f01c2a39001406eef65cb37674e369b6de23a0bd2770c6d842b1a5d7ac652f4514813c0cbbb2f67c8d809400ca43a32fb867b683b7f23a754c792c345d34528b6c8aa63e17cc53bf1638faad8f02a843b43b1467334d2d88e0ed277288b2cd0256951e00fbf6293e7a1bb83d2fb9d7a5236bf820ddea769452dc5da7ef936c07c3cb7f03c8c82322f545ef321c74e47995e0a1121ae9cd6836c46815a21c1cc50ccda94d6d0d01fc1b68a06dbc4830c9362810112abfb5654a9f4138af4ede0f2a7ef07acd33a74ac4459231b0c703408a5348836375866858c58b51dba327481132a39a4adab40067d20f46de0a70b58395578f023901303e77c9b52c74e6ec41229889736632d8eb89fa79fb9495e46c4b81b12acca4f1a12668bc48923ae0825b7573dc48f91e2c29241c1088026b93d0697fbd4564db47233b56d74ae2c31b714b68898a0e4d387278be776bc46d825082e9da9efe9391e00a6ce64093311ffd111084f6c92b5e170380d5589e0ea0244383e35f2964146d107845bdca4f4165062672fceb6358e3b28d23f68d56a00db5b0d9571c0af9e194aa39db054b3bfd1ee87837a746ffc30b81f0001dc48c36fa39da242d509dc5c791ec478d6ffdfb41f863ee99cfb6cbe928b037fe6903697851b8817fc6bfe184da9b469298c5402fe29ff4d351cd5d1fc44a2feaa4216f061e146c4916005383d71ccc1cd0fc7a95411a495f2982011ec268d361feb1c592fdfb85d09ca0a43ef500ece9f3e87242162afb363e715163c99ca8ad9542185dee11fdb48489635ece3c6e00807349f1d938549f8ee3ab01402e676aa5ddd20cc3784d956a591099635287058077c660ac639afad78c40f58c364e54e49b560212d56cdd4b0d59486f9326dd981319574f51b4054ffd9350d8cfcf42a0b0d525a33d27325a886f56c441c6dd7ee423ce293f05ea10c4c85890d8471e192b2f138884700573d8ea21b3cc000329707c9713eff37177ec03465e6e195895d5ab3744171a4914cdbfd960ce8f88bb6e8d1edcba96eaa5f19b177fb10581629e435ccd5cbaaa328a645aa8b0e20e6bda55a60f99afc98cf114a95f703405a1e57e433a87e905cdd4aa5aa5c6c2aa33a2b30489bdda0f7a98082642fd783c52a908f838ee2efd1445ff4272a20be990dc93852c1e9795ed93d6dfd3b0fe3ffaf7bf1ba0aebd9a29f3979fc342cd03b07a8451ee4b248f89afa32a8fd1514b44321595f7be8f0f2fed38e29f56f37be48fc6966805c431989a858640d4ff0c6ad8f92627d642f3a4fff3fb41d3623b6a932ee1b37d36c4ec85809410435ef12adebe92f6db9009c1a22ad03998935f93215338b1c08d3770538bafaca928eec13b8af124260f623b89502f274cca8e82775d6599d9f57461b64fbe77137597dad48d971f3c66ca889b8516be230cd7f798e947fb13517fc8391c7813e1b4b9764681d68a88e672f10c64cc5cbd2625f3585fd81d8080420e7f73c9aae26471b743a990a1b4cac2a5269f392e43e337fdd0c0ef1d2643b7823d9fb17022d1fe08710f2d236abb1c3253e70041b010a44dd5dc5f0d1f4855d5af5d7bb598908423eef949ed1dee1ddd7349a34733828f58ad0885daf019b1bb3a71bd56bef6877ea5de59e0c52836c05a911b8dca7c12acd16a6f1da742c962a949b3b72b1a8fee40695e74603ca0631468991e55d2bb7c18695fdabf7baf789af9b975baed6e380481335ac5b9eadec7fb6cd5db30fe5ba506e6c511c101e32bb9f6a44343a1254a2faa10b7516fe14fd2e1ea63e01ce49cbc2fab2f6aa0fcd76cc53cd7552e63202a746aa0f360f3c3bf319f09ca091031cb341d4e5cf891e605f0ea549a3f1a453a8c22032a4d1fd17f7e022c42df751fc5d9b3668d3b3811bc7f52c43a6de01934ba982ea629e603a706af35feb7a633a9212ce479bfd92f86d192eff285700d9ed07d2d096260385fb3a3cdb84d26e60b1a74895348271d3c6256b86a918db89a35e4e46289addd92b469f5ca6a4524bc8c22bc4973fd257217cec9112db1ce4f937969296fd32480e77a46cc151966147aa6f0cc96a67955df7a6f5d6a92b300c51ad005251a7d8cd7fabd472c1fb08cedd8598926a5abfe130111b6ecc535ddc3fcb7d26a0c9ba5e3af35c7f297191241d5272c8096e19549ae353fc93e8d04977fa327f2346665c299041c3e76ea72b518f8820d7eea0e9881bae5c53f5e2dc3a2956e4a9b67f8671b057702816d2c19d016c5fd0ce53a88c404fc6a826a185bd029fbde67a03c0d7a1a3aa8d116aa69c9a20a93ca50050fd2f0c2e71e3bb417ad3be6094a67a6cfc9a815cfc33f6ca3ed6cfc58e0c74937cd538ed61b1a69711d3b609dffa79edc225a675bd087275545e91a7a8ae98c73e51e1ed8e8ee70e91dce8c34d471fc036f8e5552069924117d956cd781e55a6ed5e9858afacad678cc64f26bbfd5c80700f1eaa2c047b1d7dbf5fc3f79e647fe43da44dfd6dbc77137b0533aa68091430867e74a922cdff80b55b0359f1dd53db3965bbea82782aff92a888f1e8a0114ea16cc8776b38855ed2643e416da48d9f305db061b2a88589185dc80d8f5e5526ed177e2ca706870e7eb865e9f0ca8f24b172967494421c6a682e2b8d2786e0ca7a5f3ab70686cc2503a3a210708873079d9db8bd4f4581f8066fb9a0fe4332e51e828b88eb3ba12994b9861c7f8a7e5da71fcd44f14d65bdf143cffaaeaffdbc068c080d332044506c5fd957262e9da29c1a57aa19797854c5dc27bef8564d24e49d24396627da80a892418944611edbe40c54b3184d44ba393804520602bd32e5525e82c142057fa2bc85617fb46afd0c700d119fb54b99ca0dfdc531e434a63784dd8153bef6a124b8db24ce6fe5897cad7bcfd7e8fc0bc3f8fef99970685d8310557b7c58146afc8696090ea9350ea44e0ef7640e6cbd330653bad501a0fde77d55771d0c69ee782f2e10857c5b07176c890da09f3a3842cf4e5d0b7b722c1c42e1830e5c0ce14f9465cb8107f3c1069203969ed996423b263e861ba9ef562934a951b867efd8f19b32087df328bd18aadf1a76fe7bc61f201a290484303300ea4f0471308fd2c48071c1f9b97ebe3a46cd5e0350ab57040283dd6310cc08f4b77734f86dc060f507d1343ad62ac02a5e110cd8398abd675c1fde7aba7bbeabbb936bb6f782632c03a292f68e2d5da742316c676993dc9078681b1a14b361d007367d93ec3c48e310d52da2c1bd40c16d81193ed950a3fc66b784ec653cf8b2e1fe32cd02937883f99fb29d7251aa03aa5b557b9a21e31093386e170369614a9cb35bae3445cdba06407f47e1011ba2785200b8e190d42d1ff417ddf2e3c3dc83b0770f3bf0fd701bf02952e1590c5dc7a40071e11e8ab98ae8544239970c1bce981f59b28fcf08db5d3a54ecc5f713e52e59251494a722d79210a7dcd0f4cc8b4f97a908caa2d2c6f854d9392c0daa8049f8506cdfdedafd016110fa6553d682b29b35ee10f7906b6eb28975039edf05e5a3378ea7a89814202edc23edc677f3a858ab81687fd113d032472ba27ef14f7d6496eacb547618d0980e25b6941b730aed94d25f3726d49d7f70c5e446826b0eb38497929e7abe74b55b4a8bc432efa03fa5a851ee726ee8b8a04268de8ec845590db06ef5ea698619424cc5f05d6ca48320f1989dbcd2342ad61a0228508fa3cc801fa3385900b8703cc56d4914022f038fc4a80b502d801af03abed5863b439c913bfa614ae20f63beff3c650ab5361816b00e3e55824a166887226a329b00aa73f2cce9a3ad71c6e36f621026548a74e227f047719ffbb00f004a0e479c33aad7eca383f5d730262fad0cbde31937f136e5308d0a3e08ba7ec28170189c788c52e7d86630ab7d0cc661baf8111d125b50137ccb87ce173803e975af20565e74f8233485aa7a24a6e042b76fd2d03a41e0543bf431fcfb90228d1e787b8c93d24e6e4f2d8920bcca375cd0979fddbfbce179c405cf5041e98292560dc27500d53f9a5e89dcebe7049cd6ed4aa40a673885e5e73a97a13a818c69309a1fc4f5f06f5456f83fc1f8ddc49b22f9807d88e6f94203f7b3953e11f37916228b9227426f70f82f5cf87e027b0ea484044f9b4f52d7d99e1cee09950ab327c1d78c696dcc242f55ffcbe4ec26ccb2b9226a8a7f9b2fcbc59eb6786cf82c1e3247635c1006ccdf2d22ca76dd0b9be864ac3d2f358160d051865f3fae08adbc0804d5c587422ed186b8f570d58bd8dcc7715dfe028d26693d046e7e7c3bb71813c1c7d1b062a30da5adc16aa80a0ba24640d1c5e6c21afd55b108bff57d5c5762f4bed7f3e9e5f0c682e368c34c0084977a5810e982b2e7d1cc628ae3bb8ed01df4ed058a4a871c702977b39fe7a8fda4dd3829c48b1f1a7e60b23bfb4f8b333eaf104641f1c029e5e90ae4d26b7650a056ed119900b25da30e8e5ad35fe9c81ee3d2878aa13dbcb6b89b8e37a4afc5825d93a66cda4b1205e2a41b84454a349b88cf73b181cc56afbacd228caee73bfedb6c91a13ff463c36991e0dce2a5701b9e2635dbfb9d468a5a1307d4c3b355a181afa0e2c2bf3e17987cab465ce43856ba986ec38bc7641cf6a8b3def287e6cacfa79b36a6ec734e5b08a23b34d257594c4a44d12040fd86dfbb1fa2e133742ec0935e8449840b601181a38299c57ae854ea0eb1c44bc28c3dd6c20470df1344a74d214c5dbaf9a73620aee559b432697292a85c2f40e3b410f8aaeec95174b9308d48facf99c3c9f7764f990e9a0837ce0d5c22ca5f294b2c7f218e029e1280ad8d1a179b94e41f8deb0f6a7001e47ad63041288fe57301cda224850e5fb293dcbf380b084d26afd56075b66e6e16accfc0f3aea5654b7e37704a4c79862d6908b6c6e9b90ac8c6a505945566c2e2712cf6bca1803cff60c835645969e983f621bedd6dd3b5eafa0622a788d75fe618ac8727b6823389f528b234aae47b8092c878656986b6d27449e45fef94168a7f5004685adc501ab93fa2087923b3a40c6088d6c473a5db5000aa9f95216fb20cfb0a68d02ec8c6b0c15cf6a272d8fadc9a9b0f8ca05016a1960de49a3474b2715b3fb9cc3fe49549648a55b76242e0e490e255a2ab08a33d5e8f033d2b6880c4c87495a76098671b8c2a5f49583f56b8e1f026110aa8d1c8c5304593e1529f9ed07dddbc9111da564f95122591a1b67518943d65e93e8a60347cd4b8a76a1402033d61b8f896f19092f0418114385e79dfa2d928b090c8e2005f0e8432f9884d9167c9bddd2a73ef8eba970d6c06cbd2a62c581f76e176b45bd0b250cbc296855b16b48f251d04f066d62a837a83446b72abc476ca8e238989e503f65c588b1102e05f9f09176d69fe99c8d536daf37c1f7f0a3781341d434f021b0f15b044fea16d78fba390b1428391aa790df1dcf953fbacd8f1d4e26ba2bb48d7ed9535627358821ad4e022cc0bfb471da833de4aab9aa71667030c07cc0ebe6baa772da1d37426345a212e7c9b0225afdf5fd068947c9501801a6994f72959e9fdf92b76156c1208b642fdae85ec805e18cba60c0c04ca955fbe1b9e1b8dad52a45186d69224b53c0b973adc52ad754e946a1e7b497f2eb03fe4835302a75e07916f788e26cc22c3e9d28de72ec2a4319c32577a649e25fff5e42be5fdda340cac38f163ed7bd3d2623955fee749f4d3bf6a52dc25496041e2a223a6651e2fd070b0d38b8c76c06481a1141f711e8c83cbfd364b2c1ec54660dc01bafe53ec152c34c626e6b77c1c87bcf1e60006894f4674301a24032144150b7c44461f432388f4ce04d68c26e6378109ad42287a19494ecbb5da961dc0b5bf81629938d5532510dc21038d1286d34b470c8d9187f306ad2a847d5da46c236441b64eb46f657ba06b221b2e23f228a5bb92fabe92decec70f74a85b1e42f0e623c96b171928f94953a743ea4478ee44b76fa50a0954a3ac3715e0e14d4511a159051bce217d0d38efa9c0678c8c10e7f19856f9d53a4f865edca6e6f63e89a7934dd4594f9c2b5f132be03ca3cca32ef43572c0ebf34dbfa18896be4356f75306a1b4b03dfb3a82f4e79a230c9c1c4cbe2ee1f4565e3d36e969e0eb5898b4fce7edc2fa1a59f8999b408b20781d41aacb8f293ddc105ce90f613e422eafdc6e31cbec7802527815675cf3907eccf59f183c88b68fd7ce1c70ba3c022d7827a17038bacb9ea51a275bb4e2bd9a55435c7953fecf73b5fb05fd2726014abdb229edc1318ef959f23dfc971490982b6f6c8722784e50884e7282b978e78c668682176313d8c869a3479328742436440ae28652ba218a15964595b6ba8c98f7504159ab0f4202cb244f210958a59610fdcc55a89bb750053e8a66264199bfd62add334ec037c7b5217011b4160908288a4de07c3937925bd9df4d9664b2c0d28e05ae70fa0f44ca7870bb579bf29f5682c5643c9759aebabbec5d5db569f0cc9287a2347085b977f12cf861e5b1e084dd5c419e597478e16a20d114b9ee6a93bd875d236155a93aa809aae1f4ff78d0a50a3a4012ef6a7d9c79fd80c21334debcf41dcc7459466646a5c6068f2a636cca75974c3d7da2361c8f41b2b7bca2e99b0e8261597915c1bea422b949a3d1ab4f28b57bc9e6721f69e3cb5c396a0443e280adb0757131c904ccc302f02ed03f4a37cf85b371691b8dd8c6faf89cb574fa88e0d1590a9e9ef0d7e0dc096a0049af2bf01f227d653509c2f3e344edd1af6e96fd3ea5052858d5afb8ffba3dc24455e2e84784d3e6d62f00ac39a689a6a1aee4ff05986a8ba37ea0a2cec2f59bde44f62533609da840628b5107a8e496c67a7d6a7e5d37a7b2c15ff2a96821df4f8a0d3316bb7f99562c463a05a0af0acb3138c7b2905bd9d37b54d552082f86255a9a19200322b2cf816d2b1f1e82ea20ed6ef9c3c4fafd41c4f147d28d07acf5f72d07ab1d42ccea59c9207519b545db425463a78cf091c154c8113ea217c78bd9dd4fc1f6195945ea7655b3d18d10c5b7164c8ebfeb155a989c3794b97e87dab89d66645b18a218bc72410bab0855bb72eed69a1661a2d8451d1e9df77cd3d5c1ffa77bd4213a946e4b22f971e0b0bc33183776b8b095a044b27e94cc21f58c6fc221fbf03af3eb6ffb5fdd8effc0954a697cfe453432afa36cb757f202acea396044380df2f32f6de006fd850a984fa5a4eaa33b029945ea705df58e5a09b29e8376e2df4303a5745dc3f1ba2ef36a7b11331110f2cc6a8e6f39912f0ae33bbcc2aa6d82e1ca2f144bd1487e2dde4e7ddd780d031bfde62b0025111852de6a1bccccfd4adc2081923dba1329dc656adb59ae6cead04d2865d0d021635cb66d07f1f3034ce1d0992d422bb7d876d5de490eca70150fae41132366648f18cc0c3f52585db42398383722fb9f7268d5bb6a73cf0fe9815ede89231713b1d820c1af64e2fc47898306c952f187d89adf24c68da8d0f86cdb9e79e32f15df09fb96fe83c13c3d1ec021c2d4a41d6dc4789d04c75e322ede5ea6953209e47fb42295f4a214766df210ccc4c4fa6207d52fd42a9b1dc3fc6b2336a3d396673654fff8dc58a31b03b82feb8e0ec08e4fcf182d7b313fd83ddd6f8ca44e1146c6d4f623c214f78c9ebe47dd91f89243c13b92635fbf6adabc9581812cf8aefa973b846a4a5bca311fc2ba55ce7b07be6b59d1454c94ed0b8424a5bb72f6a40a6ef0f82fd5abc3154bb595b1cb7bf760bf668e6a3eff47c57bc2811f55540bfc720715e506a8a30307b310a241d8d7b7fc9b7d49112ca0f4129a6d3de215230f393bd37058645ac4347d9c0cd6ee7b304ba387458e1e7d30693c2e438f6d4c062ad49ac6e0a2db67c6b3328f5f24ed7d883160e43552bafb5f194d6a0f3dc70f9f18602785a96577add00dab1726ac5ecbe0d95eb10d2df09bf20287598a5636201dc10396f86772fcbf6e6450f1021963e2c8af1e7fd30ecb45d713dd8609a7087a1826383aee873589a578b805362a8c54baff678a93aab8ab2fe8a6b934d20c53646e5d46125283bc3421a5787346697e424ea63b906f6f47f3d252dab229ad87087ffdd1f79a5d01ac13e4e0a3a6aa495304c79dab45bc02e0b90e15911f51203da88c749c02f181c28403ca648f9411983f683d27a1e5c60c7eaf9e0835341aa62be51181a140ed780aa3e8f840491a772ce844bf476ef9d0984e1ee1d21d047a6eef366844dfe38ce04df215c251b7cf0dad767c44786de61bc132730337c8cb6102a180efc71c67b20366c2ba134def3d3612bdd76e3ad8965ee7b80e92c3d9eb090c573dcec407fce09303850b972158be1187590e216f0c1fbfbddc240da52100b916cd237c2bc00635f43aa5e20a22b34adcda2077837341c5238d468ed865cec4b33326c6e01544fdd8f2013dbd2aa5cece3349bd6ec14a46371b1f7e2779ced7f10379a1e022817faa9929729e43a5073e29509c89cbe46618fbe200cb35603a9d5d0ee80027058e03e7197d875103bf5cd5b5dcfcef2bd155403b4942972f95b80904c01d77e09a986cacdaf81787f80c17caf14cdfc4baddebf60982d2f1c5b92eeacede2fe78c722c2545e551aceafcc6bc0859ee52e4c74d76907ed592e044708472946fd8e6e46e9a52e10d769cb6a5613d07677caa7c41147126061e177150a3e9a97d7f88855a00f250db9b2f81f01d99f38e3910cd3b3c6d5d0f53c3e99110162722f011cf89b967c94e83f00f4da98217398bfb83df3bd8a112b9c1b058f70fa89cfaafb49963307ab616ca5402b23f2c737e020fed5d53554b31efcfb9cfe2dd501427b33b0ad0e74dc0e32bc20717daac3802d33dccb20e88fa0b0a013de0e40071c1f1b8272ac3180b35d3aeaa20a43f9830f63f19ffc9c227e55d27e25eb155421efa028c51045084b43ff09eb20a72d5ab27d470d1cdc607df2aa9883cf5f677847140fd75ea2ff46a301afd20f4c6f920ae63529d67eb7a1a6c766b708cd73fb84981e3c23d289adad8414e17e3b19d81df26a7711f9ee8f71ef99da2925102bce6d8522ca8bf0e7fb29de45514984f1d092edd72a3f96738e37fbe54a347482082d012652d45f5a381dcbf5289f3699530ee3d7c4ae24a927642fb6c749e48c059259f7c4f724bfd4d4abf84642dc3b5fe608ef80dae39dfd0038a0b76152d6e35522051e3dbb537d449d30b76b86e669544471cf321fa1b01ef79138c5d8192358e1bd6f8ea54bcc3d372813ffcaa50599e56a27c67de57e3cd278368badcc953f2e7587db98985a47b0dbd72a72e9eb70d469e9748cec43321485b8f7545b05031f69020ddc7dfae8e07ff3d30d698d376a8b4c1f65f2c52145aeb20d24306f630cf4e355a70e81a4d198b81e8e0ff66a13df252188058d2117d87d12bb76c751c6b7685c1ec37c00a6484587fa57f73891e078cb936e30f163f18a975d079a1acdd9d57efae1d537132d233e9d4ccc003554ee191997e28ebc759608e3dde844a008e384b8ed7f7e5cf768192f476a58158c05f827ce295ee6f1e70a5d6fc6214f1d7ff730c2133422de09743d673edcebf6091f83e7f8717cb8ace7b07cf71768f6411bb37ec73f5d9e9c10a1aa82cbcf8c69fea267c9b7387155399c06827713a8c73d5fc24f21fe71c4fedafa3017f30e3f019774f4984bd6ed08a7f057b23da5822e7e1acc411436c68f6966b8ebe8020fc7b3d77b61dc6cc2ffdd01217e8085a23a294f79b5d80e055da57fb1c37f13dbdd7d272612e0bb872056e3fc5f7f29c873752907ad55be0bd41a7668fd79f3511e02fd77ff4d96daa02fc88848c72b3dc9f8b40eccd2ebef600cb3ddeb87eadac9bf100d962f147670058b3807244034f0569c4212800f5cf6aa454f64b9a384d1f29707a768d346d80a2d8eef3423b48086c271078760bc6d8b18e061a07d04b71b5f9c21a25fe244ca2c0b055a41b983858c6fffb07d5cb6cc2f41a85af6222b46142078234de699c66b369ee2bb1099a1d780d0a87b918e4cc722722e4dd392f7b59a0bf7ffc3f4ee793ce83f01d2a6a0eb90b51831a0d40f1a83dc2a3afe190c3b1c57a3360e65c036930a840254f02f6cb1e23879c6fad50eb673d34eff652c06a657546f5d65d5556deee7f74667e6bc887da8fd4a83cba2444a8d2723c006e1f12e7ab124310fb599c72cc8f15cdcc11a1b0613f1c76ca430678decd6cc2034f7ec3ef37020bd2f3054455b5ed0e949ef849aef678f3be95c4d739c25c8c03c67b5fc58b055b0faa0ec990cc6f9e201e396de115dd4da6f420683c5ed7359ed48bc36440a2c7d975650db39538808c291ceb37417cbd6b1a0fac70103d51117ab1eaf235f78d5901b254211ca6bc6544173277cb8a384c1c1125d4789e370a6385f8141cc4cc0d52bbe36e513ca2ddf82d3e6116b44ff5d2616d7815e2eb09402943773d2600be9c256631a713bc9eba122154085f28e8a6fc9e3bb2cdfe5eaaf5fa25344cc5fccaa7e1e633f17eaf63989287502200658a457794b6561e6dfe04a6afbda6618c738c69c36720a1e3efbaba061a26c2fd185995a7fce1bd570cca93e115b553af9475668c9931559ac640d061e06e29b8a8550d8edc0e71ca5c6e7a2a8a35dafe8262202a623ccf8eb0d75260234419b4180f2300d86fea0e20b235e1896499ed880b84bef8ff97813c25c1308071d8595b0f6aeeb1b7962100fe600ccf9e8cd793d63b03027a66a4c6115a9bdbad39ae483996a672dc3ba7fc6eae8e6bc8ff58b0a0c2e7bf8444e35d947d05932d1654c20b68e7e386b4a9363cc4eb39baf734dc4ddcb03b540fa7d5bdee0d9d216ae2b788d442044b507ccda37b4d9f77cdea0fd7ec6475bcfde6ca6a46720aebe616be58d86b5a691d03eb0a249c9b465aad29bd67a262bd49cc17cf9a130fdbae126cd49a98b35b932d15feb8cb475425a6c853f2e69a7905b8a05dd780f024664d31b81c90f80f2f990b1f67e99cda405f855bf78d08cc63f6b802e68c34ce713ded97a353dc3dca00d6dd4b207de4633712f721e6276e68e68fa9e4f0916a0ea507af1625ad94d2b491e4a42da2190d6b1c1a5ac0c2a13ee665d931d51f74babd0b3cac65c0b1ed5a758ae45548d90c0b85dbf4461723b020238e172ac7102fbfb7275aea4a2e439b2a3caf9fdb18bd621ffd06e8dfe9ef6d93192d6d692f8023b000e55f929eeba05976ab206c00780170f247d29e8a6c3a08e16005a428e26fb5752231dc1048a7bfd04303eaf4edbdb47a2005c0e9b41acb041d240e6f16cfc7fb79cd0807097b6b237c599cd307f12e751e690770851643093dacb4c9db4c2053d5d400c308bf9ecd5c92f79342b437edef5124f67b94f5fbc974a427a0ef2d6042ae79b992643c0525650f6574ad97e968f62f8775230d0de97a6481427a5b4e6cd68faed2a1ae65727ebd98665ea6329f32c536f333ca277a83ca417021432f67d362f8baf3e6b029fa8fe27e6e22ee39733b842179ed4c0beb9513058923321772570ecb49f9b56294b9c924d8ccad3c1449abf932d60de8b5d6e5191b7e9fc40e55e16862d3cc93c86cd9ffa57ad71910648f72cd8ba7dc2007061fa2e346382f4f7db7a9a97ecb3c6cdbaf8b49765d1e545d124daba2fb6b829857f340d09a1eeb0fa79e74e7c4e8c4433858f118d664c2334a5da03cf0e3e17e180364300c8ea07cfe3fa712947f4bdc88a72eddd3ca7c97221b4ab9784e914dd19ac441acb6fd273547d4b205e72dc2e96b73cb1295b7f2682175f0675a19ef985252b8f62d100c71b3a92d0b9556ec57081da5f2a81be58897d3413a2cbe318e1b66fc7a134b785f60b1c24352936c1fdcb7dd60fbd23eed2ba015b24bbe9b7b83940945759fb566ee0346fde466691f2c5996ad77ef32e4953ccfcd9132a83b441e856c8056668be110cc34a52d03a977ec24738871d4e3536a1e18af1495cc88c2b6a7ecd5a73f1f20f366f5335b0a76c7648e25a2c253e199d942bfe7d35ab66d5289ecf054a36f5f1d6a808ccbb1f1994220949a4301a3294115459974204317030b485d7523366b3908f46af016ab8fecf0a7c0b2088318ef4bcdfc478b549e33b7044a9232f424f56001e7f54bd1ead6bf28fa7501da8e86c21069f81fa7b9bda01fdc20c6b63cdd7d636af406cf4314a2a9880b6ed8cef48419de52fc1ecb9be9e8086d94066185c592ea1d5b9063184bbd9749abd2fc7db8523e4d803deaa318dcb6e11b278ff7645a594e76a3c1a62b305c9a7d91edf4a38e62d0749c1f6f8e912d0666315c95c4007871148a9b8e8bfd635198441148fee499d4bea664b409df834579a95b0896b7fd38980479d9e1270e76e1515379aa2681feae1ce1460c45e485638de41ca4ec42df4f80a89ecf8856b24fbd62a074b258cca83d845278837973e304452820f047fa25814744ad86907e7b90b19bc6819a358956a2085c8bff5bc6b3f66d9e8aaf1e41f197c8d6f6b75ad6a0549af42b0890f14f44f52b88e04126238b7b5731d61c9884813825e5a32a0545708b80399054f6a1d71c0d85e19573af4cb8c04bb7d0482854a84051f13e634d24c3ea660121c8e74145df474f0b5bea9fd2ab74b8d97dc13e64c8de2fccd538dd3cf05d58c9cc8f828949591c40b34732f2cb530e5b6b9658244671136e81594af65e8dea2da6f2ae317cd60922f59515c67716a40b4bda2f9317b3284fb55dbb97c8552ead21992343ed2d608702e4d0edb2370f9b13cc6464bc085aa36a5d968853c61dc45b3ad66af7d29459d004565fde92e34385a92083566f9ca3ba159fc61c5c1bf160b6ccf002c9225bc987017c49672d15e89631ffa3641d8de1213e73df49dbc18ff95e792638e7654a937ad0d7a1c2787a2e34c9a4c55749255003271989689f5fefe2d471b2a96765d4ceaecfd81dd9da185df8bf11cb04442109b823e354f01c711e49f4a813fb99789eb2c64f47168909bed3be24ddb8fb3ad3239fc192be26954432a7d3ac8cc506611aa8a0f48a1f3a5c452cda9de64c5f3e1f3eadcffa5fafb873f1b341e531e0254e2fe0e19c0aa7480ac0e0881392e4270cfbfb362f558e4af7d9e3d42cce0d0c6c0b0e7d2a64898653d577bb0c3ab8c0640abba6af034c5e8aef22fbe2955cb142deef21f39687429302fe2e2ffe40f45efbf7ce3dbf47c6dd004049c22b6f861b04ab2160956529b3fb0bfff00662fb0b9e980b8dd4a5cbbdab97b5b4a473ae3a1c18afbaeb60ed5a7af73d0551154d917754d55c4636fad7b4151bec486f35431827d23eb8a1ec570a2bf0247feb68935a6442de00839ac2b76187dd454123b713a88e8efa28db0133f51c15378e034bf78209a3b795c3bdc820ef2a67a38cc4aa1a6e6ecee2635c2b7c95bc2782036320a827e78c3389605563dfb33f0be2869bf27b89bd72a9b4af2b52f3d727e407f3cad926f993e8589068fedaaad6eca771c39aaf04039e712ca2fc7684250aefd611afa67b7ce27db1d47504f7f8f141382a2cf5a0ee27ec865b41040410a347ebe61684164b49802aef890c5037da3c17354dc26ddd538e17330b07689af88b873067d9f1fc49a59033878be7f276ac20f579066fd262a44cdff66481c35c0d94d179c5fa9562f37cc2b5135d4f18c79932155ce9d409a2a5aca54fcda5e9d4a5cac1ed52167bbed4cfff980dd47c3b623c244404c4a8115c9e33c15afe5b8cb19c3fbd04778f5b432ae657f486be8ab3df5b59bb3e34324a452359ee66ffb612cdeef5054c3bae08d62f42256aa01eee941474f2d2ba6a65669e4c293f8022baefe8375e114671dea68b2c6c5339a2e0d1213538577e3f7abeb31ce7b13f5ae7336da46bcf89d3b733ba41955ad55ac68fcda227efb90f360dfe5c0e7e8d92e1b0839a10492db9848e06925953ce88092a9af8d8e7c7f56b7a9cb1224f79abaae58028c9b76216e98f7e4c830bc0d005f87fb001cc0844f189188076ec9eca351abd349c15ee5ae54eb7f5e2449984332a88089e467899a30ebd864acf914b9922fdb5191bf993999430460f4fc254a6930fca1263579727725745ca9028aab5c8c33692e7eb42f4404ea6ab83661d8204cf1d0ad68ae1de70e4a2d9cdd50fea953ce2babc0f61350fa3563170debad7e7607d6b72a1ef485a60408fbb50a9733f2f21e9c7d941bbe9eecb5c12a611e395698d5e05abfcf0ba8f63051f9ad21d9325dab33f91033cc21938031dadd49c5eec55a57149194ee3a04a226db9669087ce09cc0550d82369495b68c5970524e3c859894186f6db7157be2a3f8b3fa17aa861f64314afc92b64374ca4fd7e84dfa22b7c5378041f43a0dc0b9a7c3d939bb9405f10986334d33315f2de6f825e8e06256d9f5c48250386b2649b0d12ab8069d22fa8ed0dbc1d645a53173aa88fedfc3330b87634d7ba2d460960130ad411817254006d635d628dfb6bec88f49e384cbb6551b4437a693554cb71a1d6a308eb9c53fce7d316525e459a3745d3b49ed0000a8d11445ab9a7613c446f0a969b98d97576f8af1f13df910e06627d5b1725b37d3352124f052672a6fb03cfed3eb3fec529d81f64cec2d8c5c70800c6c97e2da8cb5e8bab2e047cc14544cabd0d971dadb52096a6a95e3a9d3d408327b152c81b102d5ae06f68414766078dbf6d2b903980f4c442d05950d251296d3dc2333cf600db218c2de9d8cae9519f748a414f77c7c02d19d15ee692154b20d885aa068f4ae4428657fc14dc957e55c29337768d8bb0f0f3f44dc2c76209aa6a6c0e7cf31c96bb99dbe8faa3470d3bf903f7e80a39eb7c119daae65ea116c42f8c0af59048b597db298a7b9f274737683b5e1ad751c7ae5c57cb2ce598a7ecb617feb640232abced902ace9699ab82960afd950b6f7e0c576f767a2d3c4900b8f4d2343df437c89028ae6b6476daf20138879bcfa4b5a872772da802359c71bf77d3ae10423d2273d787845b2603637147829a95149ceccc4028b4307c454c743f816be9f6aba3a8fe2a6fa5bfc02cd488b9c3ff481822036e3878acab5f92b5789fb9b6cf37d5b6c57a00ae40db11a59591b259c142b76f1ea60d0304e5a5925296a9903355b9d74e5168f7df38b0fda4dc4203ca97b315bd9e1dbf7285093537d8f31c60991efd775f128aef6acb18560f484106e8af0c733bd180416a40a99f942a35cc27163b3730680f22eeadb3e53a9950dfd4d42eea1c4e7fec0dec1d0d61c6bbb25606e1446f81bae583788b7bc8a4177133a808d5334ac4a4bc3c2a962d33f9f1eca61f1329e1025269472ba649d6eec5b539b9956128a04504622061fab93f99016f419df8917a919f617974de9d16986d4e42bb43f55dc0b639b544eddda318f13b1f37473b58c78bc3fb17de996d65edfc163d3bd7ba054070f0b15646960784df1233ae53fc16caf216215f429b4acd91939dbbc916f73546387b98148157105a918922f35199c02c24351c34b101d2590ddfcd317e9f77a415bab88491068cddba2634b0fc3e2bcc2ee9915ed8897db895ed2a570056e22cc857f477bd77e1cc03d89c720b1abc8d27ab8b33947f8c9bff00c3428e0765b1b23eac44d22490f38d48eed1ac69021b500cec21ad825c408175d2afb0e80e7e527d32b190e8653c8037910875419933489e3804f28d1f0a19717231ed4595084a87952df1e10e19fffc291abe91fd063d287531cbdcc6ce23182d37ad90a2a4ee5360d02b8b91e07f8c17008e3e0b732d1d2839d496732a76f2d1b6fcd2e03c38e38b287135748b867c3b261365320e6ccfdf171e96f581d63163c9b1670c341169340d1b5e9069a4ce3f9ba1410b6a43c943444e1d769b663e1b402ffecc9dbc908a3492d6bde70420fea1cd888a528b7c905c58e53478a9f98bab4bb5eca60293c097735fb26b92d0e33130f7424860dd0ce3f36a9d9f4658f244433aec9a63e33c4be8bf2fe130593607b8bb43417287923fcbbc4b6407f555294de2dff0b721807837b86190b7e1433981b50758b9e5efab661577f49efc83fb794e193810460b8c132ded83a7076b42e18fb3e533be30b8719b836e59d87a08d029fdae3064b9f529ec5d3de43823b0e3608cfc4f7786260045615b816c8ccac2c6819cc897e6781969d6053c50e8a5afcb21e81e384c68c31daac593660df0114ac21d2715ee5cb099d09c4312f5b3f2fa670653baae30cbb9d4ebe9f5edaebc52c0fe04833f5e9f0774d9a86b5e0f7a991e9fd6c3fd5dbf4ee4e26da06830e4a69ec16c9671549d383fc276d727be798a2aa16564dbc5a0947d042735e3dfe1ef8d525c71cc3999ee5f2652297b6fb0d85360552bd820826564085cafff05aadd4e485bacd8d5174855b7cd43bd5afed79c46b894a9027f5003e88165aa52cfe76f383e89347b53b3cb1ed76cd488a87cd6846d70f50ce74619e8089d5137e0680a81322ec087015126847d8833ec32fef086b5c33f21a8bb2df23833bd8285c8a8be5444e689d06d627ee4ef90b0c00b4d25fd10823298eb776ce1e8f23583b92c01c38fb9e8ef0c6a2824964a82e63d0a1b0432464162b31eba01f4738c85a5cda707f0ce226c4cc63524b1e7c9ae2089a8825a9bc6bf95a731255f0379f1bf166c052b3de3c90c1c19e2ec0c7d04693fe701e3a5e80faef8cec8f39ef72d92d8ac915003023089e4c11fd735f0f54454bec6233f301d6742912eebf59032dbaa48f3a9c6339115ee71ea4f68411d43207bbc717f9f69cfdea29ee7b52d56ec0714d557db34c9f9310477ee47514b2448c259f49d037285affa9ce0c600ca1bed642b6f091d5685a3b3f6f23216b4298273ea4e0bac3e232ecf766c08433722568533417666e08964050679540e5cd17d96581601aacdc13a7530b957c68d221b88c0599e0570b48df17cd655976b3b217b6d6d1a65ac404756c2125b9e33a49a7a3bb6f5d462ecd4d083b4ad5e537ea6aed5393cfcab47e81e3739da5832e8cad3f7c4e87d81410b7fe95b3c344a1c140540711ab44bfa973188f7f47301fe71596da35d188b2e104d7d3acd640a659398dd24f6495b29a00d80478fef31dea480ce6e258f58fbcb8bfbff99791610858b8bb2c9c772f708fc07a156a0d8ab6c28c5b9a09c7001fc01ea1193c187fda790b7cdc8eb926c1a907288b4c28a769cd2cbcbf770dbe0ea8870e94f2a2b9bec22b238a3d545cee4d789fed3d11fb1a09e401321da47cc1be425144fd0e52c201aa886593be3c3d99a5a06fd1f095a445d3957fd746cc9a2a064200aa55f9704f63bfabaf123f331283c4a08b3e63260b3c329f0cf121bf9a6c63d0f7301ee09acc28c9e38fbd1e8a9384c9b223d5a34d09b1ce633b10b4d9b8717f41e81f2d5ce1090b4b30b140366626eff9fedd461a52a3d04734b87574935c5c31b77c174e01af6a89c5f27d945b14af900f2351883c7db467b2561d70d000b8ef916e2c8491041820238745d77756061afb25462cf1e45f7bdfef50ceb9dd87a7d0a1686afdf98f06e1aa1eb60af2797c025cd6639cbe3fd0090c7e7a8e24d204f6ef568e262ee347ae9bfac045be0762c0dafae56157e32301bbc29ef83bc55471c1141ab36b2ad377ff4d4f0965ee550cdf80bcd67c5b284443a58576a8e4a5276a10aaf8823aa5a65cb005b3ec3c155d108eb3a88d4dfd45fd08b73e9ffa57c2da2bb0ede74f32080d3f5a3ff7baac013d6a6e78f01e107edceb6f370411f319c5a8a9a875439cd67f574bfa111686afab577488b89d24028cb29cb85259250c9fb486fd9f917a4c66ccc742c97211535473863081da01d8737c51b110285330885583ccba23abf29856109dcadb1661d35febc7b9e42301a3d800aae0287021ecd61d3abcd81c66b7da835f28ac18d313debb3b3836512d020e384688ef02f1cd878c30fbb6087111911c0685490ab4bbed9e1bea4aabd365402626124f42fe4396c3f11f3823c4d0c7efad01aca8a59662a35c96d3e2e142a6d448c2cee133560411efd328722f0d8afa7f86cc16c8060445f5e04b1ce0d307d7508aadc09359e7f203401fc43371bbbb146c0d8fdca6391424b99d16a7b196a50dc951f5998bdee0b52d652d1e33cfc84233a5ff8b00d0d71c2f2505f74ad712d6261c84c1974c9e3adeefe890b2cc2302dc071c9b64e42d8970e52027df6abfea1077c0142669f61e333da1dcc1b01b37f3bf6eac064d0751121070f2cf8ebc0e3a23a53e0dcf41cbb6454d4fd886c23f2a4231a0836c6304087fd111c333b209471726b113c0af61722f5aba3ccaae8f6de304627c8db6f507e3e140ac4d4e4e411f00b4bf761de1eeae49d733c4efd0d32a7571586cd2b84312978afe7bac17d5e137518c34ae43dd7dc8c5e4743353f9b0eb46018f316ef4f9d62d0cece2487aed7cb0bf37afcb8579ae10aebe26b1a2979191e27107ef8180d5a01cd18766d7dc2c54a3315af54e2453a760b4c884fd30ff429225faf0cee60b416d73463f382902eaa41344490f2aa8709411aa59f80adf9fe7aa954ea5ea16840ecb774de62ff25cdea1608a067b6f3fa3fa00367e5fb492f43d1e9a4a4d19e517ea31c6be5affed70efacc88a0c58ee9a998d8e6360a2892c1c2e44fa5347f1ba298f77ddc033867c5b34ec6c7deb2513d44c7362a05d4daf9c2ad222486bb8c8170e81f60073f224308d4680a3b0b34a0d4e8363fadbf0908773c83016c49c6c460fc026ed8a305e27154d1b63e490eb609fee5d7f83c0029206d4e4969c262589f0d1daa3f8b118f443bd67e3120c57ddab151c9720bf229ab68084a8f87f44915bee3e888cbc4c644e48df516cacabf9ff13ff78b17b0b58878f8480b2cc2cf47448b8403aca165de31a4dd8b2d8e2b7511ee966d91128db07a473fad904d04cd195808f5923be5814aea3d28f297eaabc4ec7ff90752085f16f8de2a9acee6ad6b8bc22434f5462c8587a00d0aa32229dc913ee8cc53be46663521f470eaf1d099b7cba702b1dc693ef4c2b9d797346303ee080956465a6459a63923bd1e42a7bc7b9f99bfe4c37acebd7d8979b108e170a99ef8c635d3421e7680062a285dedd9503c796ed60ba2c541371c2933a433aaeb99094530346c6f8a75a6894a0a15ae3169b630a1989c3fb36250b4f4a6ac3956832e834d7085b1c97cde9bd2e6f5031f76d28103b66dbf3491db875eb45f54a805d7510fec39edbe3583578fd8bccc2a5aaf0aee025b170875f44f500fa6813cc00c0bbb94b2b144d64741e052b68d8286fe046a0d775792917849d18901d9f6cd56b160f1625a607aefbc48a72ec02811da19cf9d033611a36540f2da2cf8364090368b76ff2d30ddbd440cde292038522775374529716501cc5e03aad0ba8f5a30181aa5033e72e52aa9613b6b14906a3e1430d0d7ea3653763155a1913c320899390e1b9de0f87683bf6d49a9eb69bc7b03ffdcd7d943cd89526121fbfd6c000493f451de8fb782cdac95d78d104236215bcabdf79652ca24532209780806090418805754ea45068c2a4626861d020cc07b54ea45068c2a4666e6fe70152e10203ee3bee03137000e736170d215808d0e0c0178c16b4a003004e0851f3aa9171930aa18999999cbc355b82eb8e0336e0f8fb9380e73739c747dac5ac8711a35ea8b0c18558ccc0c8dbdb602f8c8c1e9c1a3851718558ccc0ccd8c997be32a5c16ba2297efe0d37a167cc6cde1315787c35c9693ee0ebf61e34faa1a1cb5a406e770e0e0322c1d39bcc2dcb8b1498bc3ada1eeb8cc2c1d39ee0d0e948983691b23edccb434d4cea031e70abe028d0f156edcf0aa823626da196969a69d412d8d1ad6da701b2a74dce57a55ba289a19ac1456b8d455b8d46d7435ae11ec6ffc70e1a957522d01608666068d1a7e3bae74399fb7fb17ce53272338944f37a1bcca6c3ba68e3914ca79db41d538e7d3495e555ccc46c3cf73a40bdf244d76bb9acb5c569dba1aadce8096468d685dda3b6d472d006a623c06a663be89996ebdaab61ddb31a7c96c34fcbca675f564b9bb727b57ae5d1baf155a68e331db8e4dc7dc8a66a3e1e7575e7be5d3e1dbace6dd3ad9ab193433326f2fc6c1a86ccd75a5705d2a5c978deb5a5d97cd75ddb8aecbae70915230325e66581bd7616ee7a70b0037dd1ad76e0a5e03f52a5c203838aa06e7ee121c3a8e363750b7812ea9a1e36863af7b4dc15335ae0280cb74d76b0a97b90600ddbddc2baf4fb3a73f3bacdadc5801c74d0e1d2c1ab6c6a96a34622e8e5e513fdd9ad255991cda15dae288f646da1cd3eaa096b563c6676674d467783cf55ac3b4028e9b1c3a583b58505dc661ed8db539acd5612dcbda1dd6b2606d0bd65e3b6f896f288eceaa488fba96c435ab94a9e36460e4d31cd0ea68cb8a7687b42c4cdb02b53c7ac038cca92606c7538759bd6ccf5dcb551dd0b2daee889605695b989607b53d70508ee21b543781b36f2f86ba34aa6b04fb935716b43bdab2106d0bd2f2e8412d4ecec94f265a53b03b586881478f6971727c58b7990d3b2f5657db95bdb2bd32dd92a7ee4b6704fb929fe8977c3aca6b8e6d07d531577216361a7ebe84ba50c68558afa86b977453bda2cedd7a83bb7ca511ec4b1d66f26602f77138d0e6b4f511ad0bd2ea4cfb83da172e1b808d06939b501d75207da26ef279e57bcd81d6870bd1eafc7821001b0d30cc497212e96ab76fecbc30c776591ac15eeb302e38ee15cd718343c70adac32d06ec72482765d1d00c40a20448e99c7352d971624c25b3650f4a6707d98652ea93d279512ae7a4ce1e9c92ceebbae6452915d2ad8b5239af8b5217484c5e98cb38bb0f1ec1a633efa0524a29e34587c81e330312eb20dbcc79514cd239a713232da45bd349ec08c645a5e442e5f47975906f3019639a72ca2540e92345244b493995206794ab9b493020df480d8a6bca8b4e39e5947276cc37d3a5a4d74525dcb20497578c314a87984df160b1e4a5846b0b8763b592578c3146e9f09297a4704b4bc2217460898c12ca083be806d68141310809092750120844e316a00fc4a20bca3b132127658c502ef132c286c2081e0541246ef08fad90b32b02bf9d79b61e484b225496a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a39723464646464646464646464646464646464646464646464646464646464646464646464646cc373126e98e0109151e21a19720d70e63ec08a1066a76d189699115c918993483a901cd220d09090a27616429b1ab09a1216997412cd01319e3d32e4e7a5d43c8322ee60b8473c58a32ca0e070f79e56a0afd2ae660d80ac209b197d207105310e7437a4180ca359f0ca3961fa5152f3da49840694185e084035313a494505eac78e921c5044a0b2a041fbd4f38f8e86d6ac247ef194c3e725b7041946840fa826402bea26e3131e1b6e08228d180f405c90472d25061c7b042f507950898022d26d9a8b06358a1fa834ac4a43336f89871c9548065018fb2aea2d920e392a900cb825e323f5c5028118630d805fbe182428970613125f838a788435c90092dc19c22062c5385117d40b38451b720435c6f314079611e7632829c7c9411c43731e8a38c9761a432a2722dc12338387dced9c94e0a514a29a594524a29a5b493b36bf5112ad77ca9169318150f1a928f9157d13fd982aa5b1a826da2c7dcd20f8c53d2c010acf3e2d14b1e601d191ebdd401d64979f49210ac83f2e8250eb08ec9a39782609d93472f01c13a9c472f21619d92472ffdc03a9b47d75a31e78658f28175481ebdb401d6b11ebd748475aa472f69a0d44389072fb55847f388c5147f32a297801a0655a7721f585e8ccfb830fe32e3291a9741e328138d97609cbb46b887f1d335a27a181977528447f558e66d6f8874c6d58aba47f41819ff6944b0852f151de62ea1e96664749a0ff826be745a177c13539dd603be898eea342ef8269e3a6d077c134d9dc603be895ca76dc137d14b9d96856fa20ca9d3b2e09be83caadf3a4d0bbe89b6d3b0e09be8b5d3b0f04d74ed6a447c133dbb9a157c131dbbda157c13fdba9a0ef826d28fb99a166c135d75b52ed826d28eb52e98cb125d238a3970f220143b8d88238dd788624ea9a511955a1a91469dd2156d1869875ae2db4badd9a1a27fa5164cb564b4160395d1a645531110338bc6c3044223223941e3a154452382126554aa1273505d5248be4b559ee8db65a2fbcbab42c209dfa82ea52a28235510f40f651491f00de4785d14179411c74e65248340d39adfcdab9ec15326d049806df99637fef6018c045bd1b79f80d83e70cab215d1f8539698038324da34cc290b4cab08a635ddfd8369cd23b30551c0419f475abd9add4432b304104cfe83419205a645bb1921bee9d96af14d6348f8e622c2805a12e6084cab0b0efa07d37a2c02f50a3a4d2b06b6cc1c4ee1e4a9b50591b1c1290df4d56e18dda057ba6557e6bc49672e7e50c43a953d02b18ec6de52c0a3980409d6b9d86f502d01e3050c086444f102c6cb0a525a48a85a226349afcbf0af19195f61bce86040f0adf927238a6fc7b41730fa6505584a8b6fbfa4949fcbee6ba2a12a44598ab6b44ead53ebd46ab54e9bf47adaa00f6d198a3f6606e705078252142430482ba8fe9996f8e69734ac8cd78c2ae7c5b74be8311b7ac881e0db5b96a2803f49609056d041cda4851a4a0f75156804bb74ab91345027815e0512c12cb0086e4101a1805040282014100ae8db4f45a7a253d1a9e8545474123a099d844e42272178e16b34afc586c4cea016210655487cbbc9a79a974a5dfc1c4a1bfcc1996c989da9fbfa4bde515e73eeaed68ebb1f7c3afdd35cf3d9e55072ead52b855f9a5a3733596d00cdb7ee13a2f9e69a93b6feed6bff76913c5e5b4ff783af914cdde5248dfaa5512791b42d88e6395092671aed4e235dd817a5393f0cfcb66d5eb94e5b8add73014abb2aff5d2e3721f1eb06851a0afca16d5f0ffdc7d3f13ff81548cbcd8652a77594bffcea7ecd69f77594bf9ca77f736df3ea54f3129643b99c9457a7b7b45df9a5bb39dc6c98bff9ac57b74d0712b99b79eaa2dc5eebf1f2c328bde49bd67dd84b4ddb7ae37c2391485d7212d72412a9b4917aeb66daadcf98237df3e9f69e8ce03e1e8911b14f72eb1f7cf29867a82b89d8dfdcfadc82903c8752f7c1df3c73b9d9609fe42c59da7c4a52f76198c76d07abf37bbbf4fbc3c05727e08fd9f1c82ff9c7233f1ef9249f2e7332019743f5cc37ff88d8277576db9cb719aa67fe11b15f3b36e9cb40f5b80999f25b48fd4cc26ddba2fce6f049e368be39146a1cfecddb09f803fee6bded6c1d731de5e9c5b85ee8f35e5ac72653cfea7307aba46e46067e9207e127753325cf81e4d44b24a74e2af9e53fbfddcffea6dd8fe7352fdd4ffee69b66bb0f3ef9e9f5febc7689d8cfba7602eae8952c758c0aa6d3340e8f7c1e097be05acad8d95eb58c3e70ec9fac810d8015423138592f7fec81fb64addde5f25e18e63e5f7fb228984be9bc5507d2f0665e33bf5cdeec92de42bd92ed04d421fdf27ab3cca5acddf7f3d5e5ed2859cd5cbbf0e1a651cb1be6d9c51c72996399038917b828f5ec76945e658e61f4caa4e6d3e5e5d764e60c3bce39bb8f876621e7fce8e595ded9e530ddc6d7b6f1d3c6db780968cefa8f89e0d4bf4929f5e9c11cd3baac3be2c38d9739129487fdfc256fea3fd433eb7caceb93dde7c3b35b029da1f0d1db04dbdbbf52eabbbb1ee8aa68937acc3f9587ddd6111f6ebc7624280f1ef1a13dcc91c0e0b56717090c1ed6e364487b8f0264883611f351a56384ed10280341e51f1c5f01eaf2d1fb610a08eef36fbef20197b75fb519007eec689070d0a57fa9ef1ac6d87db3e32c7cf8379a8b2b3d64799ae72b3dcce021a9b25e5ed0b499e46fd55be8233d9d3a0cfbd86a9b18c4e3a7cfe8a0c621e1903e26f15628417f9290d4ea832053823053610a00dc420f1f1d6a699bd82dd611bd8873adfbfc33875ba2b45bb4c0a2220c72699b18dfe45f4de79cfc5bfdd0fcbb3f9d35ff00f0d39d75aa4faf5a75534dadd5a45552fc6af2e8dda4a5cde83e9275dd9245f0f041392045117ef0d565b69dda3127bb344e7fa99f341f392dd33ccb3453f7f96bb04b0c4d845d6058da85a40b8b1a476ad9723f225cf422235d46422d9d23bd28738a48721911841c18e1a7a34acedbce06e4b38f8b8a4cce8a392f1e310ceb300cd531db90fcd4b1344d730dc3660781ea6af5d33f968f929b2e748df5d1df08fd0fc8671d0ffdcb51db0ee4220d0b1660f1574af5b105bc629c0204c105bce2375df74e8710abb5d61a35c0f1f36f4b4e3e2f8f29fba3f3f0f102eba7cb8026930b64b0f385941753c7e0fdb80283a19677ea2ae6401a8915a0b0ae058ff828735a490799bacf016ff266c239e41f2cd611dd624e72ed56ec3693568205f52adae995d3a4632c7c74d36d92ef9064643a688345c02276be2041dd80306ccefaf1ccd75cfabc1f0f7dcd4b58d62dd8cad285c8a8ca96a1a2d610922a405ffd63a221afb22262d53a4bda0e3d7f5d3bc4bfbcf2ccbf5cbb1d563299ae6081e90499d203bee1cf4cefc3210198352f8f17da267a077510048240acd332c9118d233f2a3154b4c44fc7e477ab71ea47ef239dc3a36d264edd910209dbf0cf1d298698f5598dc3bf6d1f87d886834cb901dbf07ff3a7d3ed211004627d8cde42f5e83bf847d59958cec92396a457d16b6c31d0a7628328cf537e20f41f8bd538d5375804dfc48baa4b4e8e3a29e91ed1bb7610a8087844db44afb107ae5b9107eeeba0a2235a41ac239530e91cc8a5ce0b8d7a1584e458f7b11ed3ba387f3ad639279f9a5bafda855d209716a7f9078d1e1af52abae640fa74bb882439acb6893e6111d37b0c8e05e4861afd051a1a070be2e183003274f008edf24f875cc9e7854062446f31b8af5951e038dac08fd325d638a6ea9365ba2723b8ea999bbcdaeabed1f0f3a6daf108838b9d44db762676a56b9f8f25272e8d63fd93457c746cdbb13478e45f4e639b4100638c31c65f4631878718fb53078b2822494325d5bb8f67ceee23c27acc65d67dfd986b5eabcfcb5578c8c78b7f2c267ab9cc2b968faf27ff309433e7a4fc53cd10c216419fff4cddca5c2ff579a5633bf8fce5d9dd01fe855d7e19d65f2eb3cdf0c2022c58f05707b3402e9600832c471002079698e28537318c6ba152e33851d906c7591afb0aa1c46c4be9bd7a7965d52b685947c6ee2120f615c2e8f0258ca4ee224a8634df399ebd9dc07d9662dc389b63d6fac7e168fea5240dc9bf9a974e9d6403dd5e895ddab6232f7be1d7abcae89ddddc6688b12204267ecad4653b07fb1b998c3db79e5354ae25db3845a6f46c079fd65f9d8d0eeb63da67d3528cc6d94d8f3fb367875c703f189c5a2a574671daaac4b25a75e8aad28b437753caa1d79c9c077b95435f358e8c43c7d138341c3aab718aec00c7f819ce837d0d8737fcfb316538f4863f647008a1738dc3915033ac43b3028e0a0e9d008d63c36f70e837c41c1b1cba0bfce386c3fb5db7922ebf8ee7ee1204b8bd4b0ee0381ca0e2808357bb914a9ce984c20187cb1e020e20c44db7008eba3378e9d2e0a46b009fb746a5a10042bcd670b16843dd0034cc50002135aba741b2b6642d67adc9da93b5286b53d6be585be3dbbba4069fb9964ba9686eb49b58478d1935dc1abc965e644c9fbc636e9dace1b267badd85fb56dce5727c2dcdc9cd699af334276aced49c2f73ca98f3e671c87099756c167b4e31aff015b333d773156e90203ee30ec0632e011ce6ee38e9c670e37702438d451b08a4dee0004d869904689eb9d6294002d16609175dae808b1e55c0c52e2ae1ba0b449bd6a1ebe129403ce109cf278ae139894a38e83f5e3c0107bbd49094a0ffd88887986012c68c10f9f1bf9f97ddb495c62578e00452c800e88a4b3cc188c3e6104964a10507b004800956087999ff78ad0be87002ce723b3140783f152701495d5e22aabf9c24c50842f07a32f78155ae5081e764fa8f57b59bddee23a27ae9b4c26d0793ceba5a373344d3b2dbcd0cc9fcc7fbf180448dbacf0ef0bd4bbbba0bf4aa9d27096e4a971d06754007d2746688e6d56bd70395a02205483c4d1ae1840a02fd008b91975d6984131e6f41e263bdca6e069278da6526f24194214fbb3340f06a3fa106423009450461a89398e244cbd3ee0c27dd830efc30c4720212402db0a009a29921996bae753db3c996202feb7ebc9a611746273633d290cdab4efa0ffe92daf5f878401a32018229429e7667aef02a0661f06188c8cb6eddc2ab9099219ca4ef478716535a9965d8eae28996c74a58c203431efb0c26bc1948789986a580632d5b54918507a6a20a0f7461f94082632d8f821c28c109c6f0b2bb6308af4eab5d5f0571ace53f7fed72d126a280632dcf5afee332259e0f0a0a3ca678f2010f0e418824bcace38c052022a06a6734264984bc520b86803c521843439e766b0fbcda36bb7c8321292dafe7f281e7936a81a779767b30120ca1a569ee73bda00a263c8da360e209208fb1d0c08b20af8a2c4300f23e90440e843cedb29e7858940082249e766980f0aab51be041908785c7cec11f6d621dfeed8798a343059421dee2b107b1efe719193ed869376e7105172a435e76b71f74a88032e46597bd4ab2daede9221100793e9b12bcecf6f015cf8771e0653e0650508f4f05c368c8cbdc87adb0e2022feb9ccc40ff79d3911900ad9a64f1b22eeb813cc060c8ebf1d1a1c5949697b90f8f2552bcac8b39c0c2064924f1b26b8fbc8ab2daed812d2d429e4f8be171f7f85217852f5c49dfcf11826062c8cb6ebde255ae9039362c955ebeec3e23403537e8978d6d1a420821841072dde43ebb2a06cb6a1ae7e59452ca181b87a95849f29ffdee4e418da48172a485480d16a7179c0209469b609a5b4ad20b1ab5a84daf8ae3482507562a9c7309c63107ebb8b6918e750e7bd2b9ce919ebca20323bc7c59f3d2088effe3727828765c2a7b9eb2040aff711716d3950e7429310fa03c31020f47b4c04a142c2879c2500e3c40840e94683f70369eaf74c08aa7cf573a30851256b8b0f26c8507555878b6c203113c44d238995f0e85986d8dfe9ad0ff46e70d2302073338ef1129bb6fb65cc0cdc0a219a8e5fba97f1c46909e5e5d100914a257975f0e9d7059763fecb75cded338f22f872d78c4a743bf5707ee14c4358c974772975f4e38cc3fecafeb72052ca071a45fee00f803fee512681cf89713691ce897ebd0385c3f5be141112f3d3ad619cd2358eadf0b569ebd8fb13cfbf441808945055cdef48c4505ade7e67e42d103237e3a8d370e53a9f2819f5ed3393bbce9383a477a354871a509308cf0c00a80f0669115536400042b5c1891c09bddd704c7ffc91cd8b20d6da0c7245ccc33152a5dbe3e43b10325fe935c0c206c082184b06574f9b1e381df0ea180cfb24810fe1d32f09f910fa27a8e29d5c48e4ddcabd87535bd628790ca14430f39092112956b3ed87ab9721f2cd6e279b7fb70799d0018dd87cb8bfee3c16da71570830c408624d105749080039ccc08612d0f9db53cec6cafa4d7485271f1ce1c80bd1f8fca8ebb87d7b0d51db5e81863e49b76d8d04e80632a236812c685f2f9ca962e49949cf80870a467af2fc856208f50a50b7daa57dcc95e4d8c6fda6528c06dff59eb19eb70076a70e3c56a1ad6a042e72120261102290121dcf85abe698f37029ce5c1083e025ce52b4044ff6130f266035bd370cb15a0249fa2e921ba3c5319c111ffe9d0b8eb0ad096672a26f8e23fd5374de71c7172516f57d5740ee74b38afd9bd1c6e41b84bd336ed9773d709850047bb1921d42fff5458e69cfd68375a3fcdbaaf3ff3e8dafd7c5efb2ce0f39993dc5667d629f9d47620b976ddde1c489d11f8d44b4f9330799dd7b618b28ec2d67f9ac7195c78adcbfc82f2585b4869763ff859841bf38d74cd3aa91ba2ca6309e0f8ca10591ef352dcaa6757f37a85e87ce654f38ffa052496ee0c9695ea6b9fe69ad3aaddcd99757220a2f35947019dcf3ceb8a64ce83fd75518a39954eedfda6cbeab5de4ff32bebf8ab675a777599f36704fe0781341673b8568bb3a4c6c4a85cf3c9a28fcc17eb21f310248a36d1210f713cf4409a2846a7dde743761f6be52a0d1241a28f1e8b624efbf0d140acd54a28da4455033df7dd87fd0bdc8fd7310beb4087ad183aa7c709ec882011acc23a977f97b7be5bb007b63c9eeb673b6c7d5f3fbb1c76badb81c7f87623d777f7fdfcf002c79eec3eae0224e6c096179d50c5c72718808829d10044dc80a7f8e81facf21088e7221c7182953142d00422aa78cec59ce8ac9813dd47cc892e8bae2b2ae4986d448912258a95af505cd14473eceb970d9f8bef2d48f79857e8c5d7d6f2350ae1abdcf21f7dc9031445e08275308f1dfd19e61b919f39768b1e8a2ba27c3bad57474d6a9a363588c990be1f258f79bc0cc5153ef8ebd68f3aacb5cb817af51c48d5619016d2a44d062006a05ebb2f3addc1d8ca18583e63e666a6576e34fc3c410d40312011485f17ca8e3b180453276a8d1be08682e2111d4a37c04dcf5c7a6b9aa66914ca4b9f515e761f112bab3c47b809b93e63f6392bb3cbe696cedd7781e748e58990962fbe969899f9da76a8531bb097fdb3c3f552f6ddb2e5e5366f109e891dfff4befc58e344a17b42e13f29a5ec01c63df88f9939f2bd4564eb77e8e1618c6f8f4738e8fc4de975ca2dc8ecb018bad5ddfa7879e447e6c1c397dec5db925dba0d9c83e41660a1858b8c0578028bfc7e828857fcdd6153f6e512c6be441190c032d2864c7f32b8a1a07e18c3f698622ef5315c5d8bd1dddd31768cb1bbfb78e44561e390315a2927892f28add05891b2a2ecb4eec9fbf3952ea2f88192673d5f29e2f239707ca58b2749defec935ffe22bc7c0f1152fbafceaf98a17440f04245f9f1425ca57128651c77cdb48ce9d8322751fed3e48da6ca8af6d5b900837d7f835e72876dbd9e8953bdae5f692fce4cdc2b6eea3b506338bccb4d0e2944516335860512314b158b9b5b78d583887b03ba2c9039a40e0852b7e04e00a175cf08123000108400052881021422014042000010860e21ca6e218c00006304088a8ca109408d8084460dbe16d8894a08fc036448a12d8e174518c90e889e0e8a0478e0e5a68810556d709b0acd0b1c38a9b1b1c37a016c622db06ba63b1d290e621c4c1a2c00d229b15886cd850a1c6a8088b4515a8a90200295471afd3e82410098615b17410a241c58c1a54cccc70980ca66a22040546698e7507d51598982b2f2fa95303c19603ba632bcbd722365ae1be0e92122425480a932a2f07fbf6691b228549e33079d2380d7d8814a15ef1cb1ba7e060674df076935ea1b6ce0127e54937440a935ef1f3d7d21029418dd35282864811ea1c6e269dd34252944869d22bfe4f7292d29e43a43c91222445899420d236444a9094235a488a925ef10369bbcdd05f37f938444a50afba49e304350e3f165931c75da5e2b88e31c621062ca9cc10e208d2afe2a49cd3078fca7fd129312c36cc5ed861e768d8ee4c49ea96828b55b8cfc6218e672a0a8eb53a82e38e0af7798c824bb1431d70dcf160e180c371032e46c1d5d880e35e69a50cbb68694a929445e6a414c3b24cd3583b75c56caddcb83885db61ad616c2c65bbfb46ce6e24e6380e6adc4e7fe50cbb348d6a53464d6b482aed008a308e78f204097311145cfb10077b7277c71dd62bfe796c0738c6abe2131cfdcf9f53bdea6a7ad54560d111685cd52ba803ca1c7dc3ec3862645f61dea036578c31461f2cb0a165588aa379975292a053b170c4183f6b4dab1a966952332f988c8c9814a762669918991966a661d5a9060d93d7b0ccccec920606a5eeae481d848112550accac4266035b598e31c6688359d8bae192053e525c8c296ec8076be537ad23478c91a563db616716694608eba3d38f3af4cac7085250c1c6ca06b6c210329d50a917ee980b518cb1a36c39bdc6ec19cc16dfb0cf8e1d430d09e5086a77773733737bfcfe1a90c4a74b0d20921825d2a0327777b74bd92d39bee11ab014394687ddb3fb62d73b087ae87c3f2e6a672ec2a132e340ff66777777430dd438e3a4133690033e7f86b0578dcdc933a32841ae55b073660cfbeee85254e8842b358448b8540a25619c73ce39e79c334266292177df9c5e634aaaa4521a1a6337a57f734e0cc2863ae6e418539152ae578c33a59cd74ea7734e9f9d9c702a71f947bd2d335f2804865d981093af4983a0f4422450879441401dd22914623a14822ff6eb46e98452cae22476c1abfb28a558f761fedc4be6d8865d5e8ec9d9ab4e32a55d179fa851ca6e4e0947d944cd2886b14bd62ada7013424d34b1921b76456d62f4ba289c32b625cd6ddb615226a594918b31b294524e4c66180d4cc565d681348fe8449c414d4a09ce6a755535bb6d16f260e1586117e552a6694af5ea04c523aa697aa50407bd09dc01073dd52b76d88483b6ab914529a594d1ed84cc1c779a6d326b131c8c24be5693418a632aa5949226062f286161f14914432f28e194c4264f7844264cb16124c94e4a29a5942c9b95300e061c744a8249657c6927d5f28d4b70d0a3904a4a09046b6fea4f281377e24a24df64a4381b23032b849cb596c9a8321929eca2e156e3aab763575e8f9d0b38e830bec0a62083859ab270c33a1d7e467629a5e4287164ecfe218bc0e7e9e731c6782979fa999923c6239f27339491e5d2afeb7a7a5decf333bba60d500650077bbcf009d4217d4e8632803ae494377339b32c5ef162396d3b980e4fe5f4cbbf397dc60b8bee327a7401073d3ae1a07f92339da4ec20aaa652adbdbc64ed5266322e988eaa148de1b12f999959ca0fdb7658029f8a1d93a12c709287ea70bf1411959489334dc3d02109382dc3bec2492facb2dd48258e4d0d4b2569a4b3b6e1873a239f7e2d113dc637ecfdddf30dc3508bc4d2d77fbbff81d0abf079dbb98193542cd54fbfca49532f325fabdd2ce9f495b37de55753ea67667c8dafa89389e34aa46d3b35641910c217200b769433369c1de5a472f2880d2f9e240921f6ed34dc5f5fe935bf1d9b89e17a6eef5675d645723c77b00855e1bb2bc2336720a6a25f8467fe0cfcec7fbe25f4f84c20138e7b155b10bbc884abd33217b17fedb05bb57df6734e21fdad3dfd9ed2b90b8347afbc618c2ce070c49f8f33b8ecfa1ad55c5ab02c99152d2ac10d41c8c2b1aa5604dc4c11caf593e7e9778e89493e7747745004dca9c87c224c003d4f7faa8322e0b86dceaec7c7ebf23cfd35cfd70ed3354dd2943192480d39cb2c33f7e921845cf3cc973f4faf1ba20d3b843ce08a6fd87f7ee600ecd1e918a06e92add60c84b7bfe368f3ab97afb06ddf6ffefd2a2749f20d7b7661d0f674edb56d88d4b621b06f7c6df6edaff56ef02bc9f6fd4ab6ef077f4648161ef9ddcd086987ff753723e483dd67bf2d11fbdcab867d25ecee3eecf9e76b246255935e9f7c2c63ffd267e4a16c1b48804088c00359804199b8228a31a200946508472eb822055e8f4f1df29cb4fbc8304ae039813d8cf0d85985c67a159bd48e4c388630888e537a11166da237f650764744f5b29d025cf439c038458bc0074010c2f3c18c5002af7801172ebc1e9f79a50b124a7aa4e0024ce2c8104bf07c280f4478ed83e33584abca31a787ad84a18222e93d3d3ed17dba28074d78d27d1a093008a267c7105e8fcfb5014f764eda7dae0d78b17302dd868ab114c09427803ced967c60711123280843b46080032a47786104196c814850b10328618811c5d015af5e5ba638c982d9ecb2154640d1ca584f3c1f54965e428b1fb278bc032954dc8ee2030f803cede6f8c2abf504475250e469b7460aaf5a9b5da8a4075b84362e4bb668916c76d90645404290a7dd992ebc5ab2d9ed265cbe08f2b45bb303af725570c14190c966b7472546932c5ee0f9c02ca86030c0f07c805ed6f53014da1649e0d2f2b45ba2e2555415217c3194b2d965296a3025c8d32e13618a575f64b0c42002086249120223e098228b4210c4f4a18b2164a1500441d0e20438a65c5486a0040cac4a12a02421a3c204133d68465088a855b0a0e0488d175e155766108597f94f8d179ee63f1ef6fdbcec6184077b788a2bbe10f27c260aa0c03dec61042b732d0a195c1103cf471e1de165533891822b3c9f5902202fe39eb8a8c42c1785450c19a219003000000173154020281410894583e160928579a40f14800d7992485e52349347034990a3380ee218460820c01060802188d0d08c5401964c002d4c17d2bb544f46cfd03da53d32f584ec598a1eebde56f8ed270749132f6648a8d29035bd16c66bec1147e0c93273e147107524f6d3d0b0c9b15c6881a4e304db1c302b24f009461ea8c1c9f493b0e3239a34fefbda471c7ce1e1815323a75b582867485f2491f4edebfec75a5857579b4ed0890cbdeb78d1736ab9b119a7df402ad7ebde8ff00bf68850ac95620d33ff05b41c9a59a16c158b3631448dbe25540419e63a3f13562885e5ff9420846dd191a43ce6a762bbe984ce0ae51f849fcebceb20db59c8424a89bd8258bb89a56f76b888327dee8722cfadced625e9407c2801a5ea01f654d44daf3f96e18b93a0aa6595d4485705d32349433b30e896171863142baeb4b70badf10f5522602c005c4ffd920c24d0e3684bf10747ee9e520cd6bd432a156b88599c0cf9f5079ccc33f26d027ec4004a421e7b4088db5fb44e5add768cf3fba0487a297ca1a2ba16b66c97ee82163a651ffe0d88323d79fee10a5456a184b4c0b8610bf862c84c35d5712002b743cf08e57da4a0deda209a23ccc625b1cf7575bcf06cad98bb3f83c27f2e5f0136f4a63ff21b10e57af346478a04f114e78b9c2218d12226a5d6fa0badb1b14bec5c4f611cb32e6c992aa4e5f4fcd13ab9dc9db4bbe51ae7f74195f5d093871702324b5d3e240613bb488eda377d18a970e18c401f5d2f070b10e45e96db99f1fe7da00dd24b85146071f7439aa02ec9a53dbc4c7fd9602c8634e6039403e4df7c3895722146a3882c435e99944796cd368e647e28d0eb0bff4a96fdbd5e681d7e745ca47bb3dce68cfb77a0caf4e4f5c33e0147bae5ab2660b2ccaabff9790265506504a217cbe31a270aa54f062c0e704785dfb5b78047450cdb62b6cb1c8267a7e1ac63c79208db38b52947a7b0453d931d3b4412fb5f10d1c735a72ffd4dc685fc363da953a447eed332c2c25a7094d1f2603a54eba6454f0270eb32df83d5adf239a8b8417a405d536da93e1f1b74b3c8c23f1f3e88ff938a64f563728987e27e8a9eeb40821854aaaef73007577e99818555fab8810f96e18539b45440ae5e83f9b848d1383c80fd873fd6ea9a3a1b5a6bf7ca02a9650bb8441afbb63ee912f83afadc3b73378d5e1042d046fd9904d707f1e2086c3a382c3d7c254074513f7c851b585c2c6e8cc415cc906c737bfcebe6d99e778e436bfa966df6660378225087b452bac477c0a2795d94654962113016d032e9553fd54a99e915a3ebd3cffcd14435b212c668194565ee579f92248583d018731dfd0f4654543e8dfeb4676afea6830087c4218e657323037401d604689d347e6ba742e226a961a2977cfb2829b155e48e26d36c2797bf9923711479b7854f0e04fc9a0c28898e4ece4b837fd42ee98c23346dc21062e5f3d20b785f0140a85afac2222fc801d4a7cdebae5031c2c8e8c42c2be342699e68c39e287cc1516d475c361a0061368010fc957a2a92fb43a7f562463fbeff7ed104aee38b1a8dc21e3f224b52b162eb46c0bc309632a01b30c76b58b0192cbac8e661d8307b2245e6ba6c362b81369ae670c9400c1db60a4982e6f999e5157305b217299b9b31c98ecc2bffa0529fc9f3e9527bdfe5ad71f244180bfd184cb36adee11e55c174cd8ee8bbed3631585288e0647996f6a8d3f36e5e72facd144e0ca6258e0d63eea40667ad316afaf0e772211bbe9eea361e7bbb3dd726c8be3a9326efb7d2ba8db6eae433703c2c30418534633b9a9c0eb185048dcab8b6c24a781683140180ec2bd7721d6974b2e798a2c48921b593843cd7c52fbbd0516eebabc27c98fd13666cbfaeada7b3dc4ead0fbf52d1a79bb4ed2c0fe9a71326210817369b5192dcfb9480d9b382df7a687ebff1b2cb84e56a88c7e859d47131a09f96ee043ce2061135078ccf93a55e2488b1c2b5fbd380e52ce73e9760069373d5d8c3d2ae43b6405a27d5bdb83a948cbfa43bb084a16f74b14375c54e63afc1e7f1a8914e8438f09c27ff0c9fdb9c74559904474ce32e94755e9365b98009511e9bc5692b5254965c1d17a4575988d38f05d10ae0d5fc345202d6bd6b894eb6c1e113034e74e9e0e886408c6a1443046d982b971a2812c0de06a6b62f332d3b89d1f315a96a59b0892f208015b7985e86c70f7764a538d18780f797ff4103742ab7b29c5bce93e219fdcbf7f7cfe4b9823b68c672fca403bafa4f220baaf52f09d759687fa7584cbd2bcd4a046919f4ccb2a8fa2f8f46f5162eb3283a296920f1bf5ca6d42f87c861dd743d23449b94e2ef2214d938e9196eff1191aed0f2f44c50fd116b22b60e00606237afe3386d00a6d20b948752608cdb5df80a29b1dc992165e3581552ea87f2965dd5c49f2fef03a1b2ba179d6c869ea51e0d92221c7f560a755c92b1e18ece3bab7d747c882b69a90a5a1a4d81d1fa8eb0174f03abbabd2a60fe342871c242152e4e3a1d399440f15b3d86a2361f4c58c122ee3924f1adeb01734e64ae6248e74d3c2871d82df39eac707020b99213616b38c990fc7ba0bff7af97edbabf4ca5582e378b0844fe2788fd3b6691e924b3fe3cd4af4600d4a4debd4d604e34136fabedfc122d3c2333de2585379699b5d40bd3aec08830ab46a07c34044f2e01012589524115651d04323073cd5fa06cb9428a51906c606a8c8d720d8f623e00d7c09498da21fb3795534b14139f6b832ff249608f681a096bf08d6775177a04172f7d13dfe9cc4fa799a91a12209497daabd63c2b1a627bf1050ceec0a4d78722ffd3894b03c20f140b50670766f944ae50a7dc90f6078db8375180fd119d0f4e0c6598a5e87d8591ede265592ab5ed233bfd8815028b60f673d0164a717646876a1d412e6819bda2d5b2e897f6e1b748068ddba65075d1faddd1fc2ef3590fb9299fc36efaf7ed03610647fef0c5be0532c8dc7988d53db5bba3f3b54cc72a70abfe0876f37386aa518d109ecb51a7a725ac3b36daeea8f925011a38b7ece60af38c41d177f35568a914590d6887e7e6d1d2181c518065f43ae44cac314405fedc771201c3cd2aa1f75ce8ff01000673ee7c0daa65915b62471e1e8700db4abd51120cae99f03ac4450a87f999b6ff61f68feddc6694b11adc80beec14e5e78e610986020e0d28b0bd6f8466d23bd3d93d7d01fd1a042e11fa82428784f7277515c12fbffa22ff773df968f6f13646dbaef3a7513fdbcf245048a5ec1af3c1f51fbf5b4d5a7d40b1e7b060d3c046723e9f67eb7be4686030d9c0eeb5addf65d626ad26044a25b738fbaa90913248ca308b7605918ea159a35bd1b6a451744f51da7c0cdb1cb754677b00026beba99ca72d1e2a9036317c4f321b48f061a997c8e9b413309c7430487cd8fd7858c6115596bb1e34e1bdad7dbf7364f5e5e7e4d58b67e014404abf7ebe211a5f1890fe0f5dc08295e1e4018e974a92b30c11ec0076db9d26a240c74806682cfc8aab35657ee4971c3b2c87fda34839792a1a3eb55656c781afee68434376fcc38dafea3af2d3ddbe1b455f04b77dc609b7a9ec4c67989306f47557fcf5410e02c2be31e26529fa9092ae3fa88718b19121cd695f9dafb3d7146179379439c6bdb659d8791c6020522810f12c5416384ac658f561a06e8d6f998662f17f4b28c9a9c8344ce0b2444e263abf41589ef1609258289cdb27cc1a743a85510e8be01b90643c9e9c68b71f0a02b694145784a05e89457b1cb4b1a60177db6d136dded40a5274bd9897bedfbe22dc736b16a586df010b2663ca5ef84199b5c99d5e732b753a3e5faadd8abfc1798a496e6694e9404f7a571d83f6a5e6245e6e75784ae9e2253aa3ea6f008d669aa9a1618443fe13cc9a7fc663f652a03817978b52064ce46316711002c88cfd0202006b10f3f6b4f71921a1edfeca1aeab81bc98c8aeeb9cf67bcada1e310f4616fa2df4add63e8d85a8268fe68049ed585ac5b6af39a3204e3f4a108e8ece61eeb5b5b2d98df46a34e823bb97b39f601a850362b61cb602fc80a9ceb2ab919665316de2550ef67eabdd4b374f043c2fa6a4ae16a3891f7ba1545070ba276c20282a75cb7b048cde52c30466d84a847f013b778cb8f11d59eafceb25e1d8dcf9cdf5be5b7eb29ec38836b3b7ad45c6940d408347fd02e44f2f37ca806596519d774fde23d73081855d344192dc0d4b69705ed3c33869720b0d9172d1eead465413019d8ab9e3490139124bbf91e2922fdac82923244d6fe470c3057c1f96b8bc847c0fee1f67a11d875e16c50a635602c721416d184b0310defa5292d2417d3f035e886d2a5d9c748dc862a0afda5e46e12ef0aed29ab12c2abc52b1fa537df578b86b52aaf999285ae6b1117d0919738ebec81cfdbdb3461f4c47688fe921d5a4627a5b760636003b17274b531fa42cb71aeb1ea5630c42b8932b8510974dc28eac633512c21bfab5314e30f1b70d1e8cbbbe079302add1eed65a4b2030e62bf0dec24ed191bdafc68778dbe80267e002ec1a42546ad4e8806674e312e4f9fd4d170d1975f5615db12c76d47cef6c7639741b7f476765ba84494070504bd3b437be21bf516cffa52a2addb2a24cae1b7fadf70f3d74acb4d95478e671bba57593583927003ca6013175a1e30a25f128296666fb7215906298986ea60a794d904ad964834be3b7503eb21bea27e3df4ed1ab29d120e1ba490a51b32fc83edd4d55d543b303a93cd3077748b0ad0ff7ce0f923ba64103dfbe621a5189cf0a24189519f622c62bfc859af545b002e488dc6f75e4512c635a3a9f736ff15e889468840d1212c2417583769ffb856962d73163802baa681fe441d7ed205a205e1c962277a886c2aac7ee489637a6e24b77b28ac30186d42e3d5795564cb194e14300fbba7f889874ecda9531a7bad2c4e49a350250f73ba58c965b7ecc65a0078bae20056343ab3166b4519e5e0cda6ffeaeb5e25782c2baea4dedd44a1db6f4d65910d39b250d1f3e594ba040da47c9270bf1b6b0d891b411cceb48b2a4640bd5e341b83e3877ffac1875dc2f3bc6333e0d56e64f320ade7591bc2d45e4335f128a1a8040bd674f41e7e62523a2720bc24d615e3e8485d496287885f8a2004c792cd12a19915bc55a2f62d5927c51144520ed83d105c9fb2f5196a6dd50bf7f44646dad2d0bf7d8fdedee0491e6546a6e4edb41da5aea049898fc27d649a099638ded242d7e8c5c639e706cbee3428c6127176fa2a99fb8c1644afbba8169502252c40940eb29a433f8b1c23f3652b45e9e622edf0368d1fa6ae9da44550c6a2eb8e4269300a5dd92762588a54a4e6ebc620b1d535cbf304a61a2608b33fa767a5e4ee3cb064594d6b3f2d44614403a140b61f22c05c60222fb0cd1e33bac13749767a5be6d5e381985400d2b6fd1eae2727b827c669929edfba0179c9a02c87c8adf80a3c3ed6d594f03109b329a4ac8b210904bd75ec077a8d2cbcc61931490a57f33d9616d3a65f41157596858c15cd58f40ff607d142b2a84c8441ada57049b62b289459c56203292ceda2741f0810f308d5f350e0c12ae52f70e9202d5c12643b1e92b14d21366fdba8767bea171f5bbec296f060a2faa6113628abe1ee07bf5666dc7df46d2eb4244e48d0faeb810577dff6e67a5736ffe1635aeb2225821e2c162fa97f6ec27a3556337e8461b55ce431b33cb143fe1a2dc6201fe1b723ec3d22e78cf5fa244a3b4e6eb3d1a3c5c17e9e73f7d05466e3d4edfa68ed48e9f66275fbdfab429afd3d15c58e4a7d3bc9d82ef305e9b227132dc94a639b71ae8c6c32c8b57897a0c706e2a50bc6242d1d9d8191c0b0b9288ef92e8c83c3841c76da99e14951c1baf4a9523a197de202d20646977651a710c7b902deb19d49fa1134fe157ca1ac4b3b04cc5202a6879c091ef987f5919df7e480af466d04866647e66c8ae99ac765f775c88f2816407ff4c8387f4600016f43ea3f1be53bc2b349977460c862fa0b6d0550f077364a60cb9f3c810f76f163362ecd0ec6887f09f1d14faa86065383760ad95f8099860431592ad4d34913bf4ea27e02584cb9b57972cc82aecd45aa4ca2bc1f40cc4b62263dc888d9299a985fb6fe42a9b45d582a48d2ca48d13e4396f0efec37968245b90348f1cc4c80b32a574d66bf21be6a8b3ef829f98198f1a6465415886fa05fb56188305b199757d7bed7f009707346b6c35053cac9bfcc677bf9968aa06fc94f64877444532e0efd443c2357c610de2990490b3438c556251cf83e2d651eb368fd61b63827ccb67d05f3f4894ccbbe1665ac5fec0e179476809583cc43aadef18a7bbb630e7c23a985222e77600ad54af83790aca42750f80c1cff067502db0496875796800fb4203ef3359a03948dd931e42550846b05b1a7b8a6c9e1aadebde5b45bb5c92a06187f692d739a065f61bd71793946ad32bd7174c98202ea7876404e369d7fac85d881c62c2375cdc2c637f484ac4ea8b341137e05ed01e8880c339354fdc82f7e87e2fcb80587dee3e79854460f1568674d6cee225ff598b76d93d6872ad7c65a6f955b44f24a2475cdc495b85916fcb1c9a0e5d683af49beab7f5c278bb5459750c1a0857e83157272fb16282f65c1f45547f811e1beb61d0c5e86eacf29b2de59b3eb44eb70e9790f65f29c2a1be890cfa026cf595b9e73449f9219ea680f92c2f3c5b42209df68741c27b0a67266ee9220240037ae45d3436b5324f4c414295fab598ac5ba35ef98c8fcfea3e40bf591ef5679556b330a3e500178a0f1559dcca9dca8a023bf251681b42a5306b676996bad07bd7a840f2979e6de5efa5cab0ad8ebabf47a35c69c6f931ea83430e23426b9b81efee074b443aaca3a279ff6ce0f8ea40e6d91053487f7501448a18ff287021f3877e5c220b8b13df0e1d3e56928648f22184af163c4d111aeb7897d429fe921a1806bd930c35645a262a2bc773d94c6dfa45464dd9aaf2f1ab686f2c4b5c5afb1fa25aa85dcc8659d327a140d2ca1babb3d744e45c3cb463cceaf48965c7771552fa42eed09905ec854a933c96c4e464406018cff2291f3e6d68246f9330b166dd8bdbc0c366b9efcd698dcb73b5fcef754c115443cb223d06291a0fa8d44205d234050325c31cd94095c018392541d0cbac3c8b83e91d7b964a0f09b59485f260bec1b85c6384a923af4b8d2749c404d9e35416b7df8cd5883e91de5cb8442e734b58d5a177ed8dbf71cff179b3bb109f6a17ebe0734c98d144db15ff4a5407260ca26afe95d7a63c13be099b2630ead8a928745b425b71894f25407eee51dbbbeff2b6da80f643515f72c6264548fe8c528bc4c0deacc5a2bffcd51a1ed00c4b6d2d44651fd6bdac8cf77ed747cc3c82f36331ef733cb09f87ad22c15691b0a063d7a4cc86f4351191263ec16c5fbe122baaed47940efb45a7674ddc00c8f9100c37822158daab77af24edac03c7b44947b3516c4d4750455834b16568b1c096893fe68e24cbd249851925887481044cbd4c418cfa2497721880236defb8791ce759102bdaa3eace4002948ae842f8cc4efb06e6023a2c1540e1e94d49240d34bf2817642209472f848a2720525a30dfdb745e8933279ada141e22176ac7230da9bde360312baca62ab15d92ecb07fce14eb7cc243a139253e6415e86c2d5fd4a7fc22d5a6f41ce12250f6ffa43509b5d3a04771f16244f856eadf26d3464f64a7b3587212050b99511c7419d2c873d04b4a9feda1e4264d2b8350a4ead215bf91fcf8f0b85b07d0e1a34958b38e242f9e381fe999e584520f6b3522fbc273778fc3ddf994c4be8b845c9a5fd39905323f06edb7b307ad0171c22974ac79c3eab1eab227fe30bd00d3105656fcdda865f150142f90540907d65813cf08612bff9a1e93aaf8984a275d648afdd9254f2f2bc9e7d7d4a9402019118f28c32191ba9f21b44ce3b3c12729d069adf88eb4886123a71f3aa478e911c02172e8d30a2fa601c2ed02cfc19c911a58a2dd78393fc71253663d93d0c1a1c995c7882ace05735407baa5a8f4a12f547586c826b95eb274988462a791701f5b020bc0c0d2248d034f7c6ddd823eec4a026895295c1024178f2987b9dcd1cbfe91cd8924fee462c3349a0a772b805356dce30b02de9cce804833ce603a720f59b823195200142600274115b0549e05f74443f2b125422471669b42b6eb064a4d6c4f167a32623789fc3e5951680197475eb43be6788c075f6b1ca4ab43ce62b7414f247839ca093baa889b00c4614682ff24db5052f3d5ee6f194f9467edc32966a5351f0e10ffaab230af2ccf047d17ab34b6dfb81e3f3acb224ae7ceb3b63df4bd586a0043fb20d9c11b9d6ca084acb877a65784aae71357022695c65302be5da4bfbb81b21d81596ed79a57be74b5e4831490a20b6da2cf38dc0e2d13832f59f5a49ccfc1fac643877644029976deca69e6dd27b63591373d25f159c47ab27ce1958c8274dc696ced1f68953a9a9b0aeae217c70bd1731f2ceb41e2d825b858618d90e2e89bfaf4330c578fef003544c87f9ef8dc009b245a2e4030110dadf5c079f7e992def0402556061a58c128d96e042d0e87c993da560103645d26daa75e1a99cc6769f47c0d3a7fe48e2181d9c70aa9086adc8e7b8b67a1c48aa31a80db1f064838d93217691156b82297f12dd743bf378d99b4c8b8fa4f83d41f94c2119375f8f7f456b4c1abbc9ec0db9d889f63e3d9ac708e2a514e132c5e482a275614516d7a290413fedc74ba8e14ee36b67ebad38b18ca940216c8684a593ddd229f5c8d39b041a4296fe40993f9052865a66bfd3c6e26f54e97864985fcb3ffe0fbfc9a5a3a3513f4051f705d512c5e95ae551fd3cd4d283049529a2d5454e835c053ed4b5e226a3de655c20e2ea90c2f446133ce3e9ba19b3b9774da9b484170fda7397fc791a8d9e799afd09094698bbe9c090c0e516dc2c1010f00d8befd155ecbd774417ae1979fb9b1c228c39c531674a10c8a727bfa33558e135ae3f7c730749a620b5bfec5b9682f59b4cc4f5627f4b17c7424daeedde98f688260508af49d19bb97e8dafd46333f758623c5bbff4be121a7bbf0c50bd7266730a59c458878789e8380edc00e357e79bed31418c2004f0cdc5fadb41d745a86e89f6a680e026e090f94588693bc8af2783f2179ae44ecde3a3348222875c1b0162659cb484ae6745b9f0c9983a5bc1f03bada53e9d6eb5cb3e8210887eb33a846a1ecdc1cda3104b7b937b23fcd9697e33fc7f4f44c32709aeda400ee3dcc1a0b7c1ab393e62839b3fcc32d91d6c039e73c6d38dcf10991b4a55193f1fa69bf5cc1c255cb166408710f04ffcf228011453c12a9730142be085f01fed751bf7ba9959078b55e1904b4eb5a82302d4853ea416ed13729a6aafc81002b07eb8ff2ab224087641b7435ad16998b4868132a4e527282da505214b7f18f5f94ffc44d92c54e55681ace276364c47e07bb7896106953f4859c8f3f7a06afc7a77611addd280452aacc8ddb6cae647c8965f017edde527545562f5a2a14a8906960b5123798c466d973dde8e9ccef313ea0e29e1f8d90ed90017b3f41635026664739f7aa111e85f71f57b0893fd5b53ff06e0b02940c2ea24468130ec96536ba00124bcb03f793c81142618d8714a0d45381d48351806eee66fa7f5439906e4a22b4b949342ce33c021ea0bf4f50e24320c0f332665aed552a5d231cf8dfe58d3cd2f0b8bfdcb1d6e68239fc7a38c1e18e69b64942454cee124f79721e0f239dce4b6e974bc0f18f312fb0fe1cb6231b438e93459f5d37afdc63ef7d799283a8a8d0afe090432fc4f56e1ff77e93a483b83bb9908a2a02e800e7ca2ae92bed28fe041987cb5b628ff8016c38321b09559bcd11bcd8c3cfb01c419d8e2a4ff012fed5e3346fec627255737105fcebc31be40cdc0c6d87a5c2e119da95a414d19e55d087c46d38950f1071f91bba5ec2f301ab603f699b3f7e5994a62b55feccf20e47b3de9830dfdc5963a398c835cbdacd29ff9056f1cbea8afd908087dc83861b173e40232ae54f4e91b8e917139ae6f238f869ff47d510cd288879035ad8b6d071f0a0084083ce6c277e61b7ec043a2be4d02c9d64a69af64e362c57e9cf99735fdef82ab13f73993eb51ee274f4aeaa71add604019fa5854086a0b8559b0f7f571c0bd4bfd7d17e83215655f064f992d297ba4f33b5180fdc4ea0a4f3f22f7f889c60acaf5aeb5a59307c4004f9c3b1cc210bf619377b0838aa32aff619dceb16834919c01ed4f12ba10eaa11ef5ccd4c4fcf4dd5b5ed2f14ee4f48821471535bf0b100750aac2f6fa1a923cbb1d2072ba3874c2ca6dc2b9ac13a8d3c8354c41ffdd2eb658584c3bddb39b391aa31d25e2dde24172326d86ab2500fff8701bce0eb719b18f55100eacdde3dabd6be6a29e9e881aa1021c268a7dc7179a8066f84db34d4fb3a68c1f1915871d7cce3fe2be9e5529111fb0bc411c45e309f9c985ad807b4ad9de7bfd86b0660be7561ed8e92fa6f7fa136945430573481f792ec0c497703eae671815ca523a72f0ed6c0202dfbeb8db4b1cb24410389208a879eb0b097404b93938ecd8e2eb045d4963340bdf2192a902f4b77ebad98476b9bf9da5788827b4c2873f9bbcc1e36292fbb2c32ed9ad01f8eef32f1b9ddae623d0ab61e0d296cde5f01261ee7804cac0a35c179d7a0a323e4226834a1b0887376c77f0efd78e32b6a9084f34c7e527df1d62f432158c248ec6e6252005bff2c3b824ae9ec24a78f42eddab878b4651c50ca6eed5ceb9ea3e622e2c224f16c3dac79ec7567a60f63559c4291402f9dd54eaed68cd6d5112a2c0d717d6c11d0f4969d85645a2d2d9b4ee219bec7184124a394ab5d3615a80a278c7d2b84d198e8ec1a991fc9a90a9bf73970f8c9aa4d33a9d7cd04bb1216785a22c75cd592ab6c851ecae743fb24774b73a3b69ab84a3b91e42674171961f7721cc1b7b231259f1ac1664eb45ca1901fac0c505c0e12ce1049c3f0d8a0a8616384366b2ff2aa84432398440fff17a6013379386eb9395c4ecb75cee9b2e27620d1827bf2f573cf46ab98884417e65ad382fb659dc03dfed6f7962c597c8ddadb0e23b246566473b619cf6968b70ee82373cccbd7298333b8e109ddda9457f23ce46d5a6c5a6130d042e13de467326e75b5b81bdd5426c0ba73c43f73ca38a6dd5c3e6b528d5ee01fccda08b0a6e75e86fafc20f20d9c0f01efea767e15af7ba1f38e0eb84f087b1a886d58170d6221927bc00315ef95324ec574e754d626df44674d3304f4411823ba4af9c1bdec24577e6c6a7c603a7ee24e368d7550d26d38bfb90cc5230e056d3a21718aad7283b0015f16733e813526c55370f3603f633664d9254056c199992c83ab1d7f29c620c7a64e632c4ab9846c9b83f990800f66f1ba04e13165b7b944aa8877182f9ecf934536da5ca61bd818a8397cea7758811d6dd4ab274c2c2210a3b08d64a32873c082ecda3ae82c6ba147321a2e8056b21eccfc7511bcca8e4ef1b8603a5b0a1653f4c47c4a0ee999aa537d417195e414e0a4018aca321c141efc7cf4c3f85985338b48de3a759efa52c1a7fe38ed850ca3b791e39a8af32c9ea2e5111f267894186620f2023e63ff08468b46aa17c713fe07f5ef367ad744187f702c01142ff59c3209186807ce3c7e21abc489b0f8f3ed335089f7c2f0f72a9974aa93b591326507aad61c0c61969713cef0a5082200059365dbc9f020cf6327ff0240cb31c14f530dddc82d65abc5df875505f0eef8dd17b024d9297e1ebf3b9310a8b23c86344a11241dfec1da8076372c2442f3714cf198a427eb11352704415cad99f02f352c060a26b08b44b99fffe8def18385f3cedf3299545182f7f42d11e4ade339e76ad8350e61b6874de35ce81f2654f5e671a64561d4a9e7c296ec9d438531b99f4838d4656ec4393d49f0a3b31a24513882c7ad9def71b03dc438b1cb0ad5f9aa3123591212c55500cc85e78a0003c95d573e001c9662b9b8d18681575e3c736405f3eb788938a19c4c6cef569560a26be1fa2e4279011ee25963441a43ae0d0b7b8924ab4457fe45a743c6ae7d49fc06fdf5135ac0a6d9dd5797efc4bcd788c6b3a7d292a55413ce34cad7a990f594370b20591fd10f1e87cbc5d808b1db14c5f4fa5b035f6d5194a94687c3b21d9302b34483020f7453f201404e5135ce28a1fd82cf29d24b40a72a5a782bad814901532def034bacd7f97948a6243bb1727ea829790b8c751da2f7c80db76d105a8fd5f5057467ee33b27179078d6531848a211128eb01f462926668c2463524e6be7c1aec5cd05c5391c68b69de9a36e59f96839b1a3937512d6e16ba0b1d6c73a3050ad99ee8cabacd852aae937970aae624c387d4aa78f781d9b36288015d3bca9c0448f69f9af586fd66de013e3df2bf45253e3ea6fb08d10156e178f12278641c7c285cbb6286d4c10b20705fd6f5a3f11a5b94a3b5b403873aca381add236a16a94b9ff0d8b5b446ad226ff0c540394b6b69e64f9a359b2cff13353c2563b13c252097461e7bacf35be6b26a7c7a454a4b316e04e628d8803f82b21421310339bc8bbbbeafd1d625cee2cbf7b38f88dc6ee1109a197554a05076b173437aa8cf8ee4ab473699996e07236ffd9ca604a490c82a244e751f1010aaeab2a0cb1aa2c7879981147fa846097a7c3d16b9258cc070013912600d58fc8b07882ccd59cf5ed32919bcc240470c34d8b6938452a34dfc193ee81c44fdbb3e7ca1a101c18ebc12852b7b9741ae440d46abe99cea4f1d15f09a0ab19dbe54110fe481ccc00764c0ec40675c0fdfc057dba8c2db509d55ce518e136c42b7fd50f7236ee7be7587827143e084b61c2800bcdf04a08ca86758c0dc5a898f7bc80868e07c405322e77c764094781624a505b90a6aa91e897543c100ced8791b6bb13a4c21c10a0f96be94ebc34d220da14e47237a7f44c02bd69a5b63850fb8bed09589e22d5471594a0b3d7248a5558fb9b3233af52d78ca3d81b61e56e982c1f2f403c76db8d152ecc2e02647bf5a51a7afe25b93a181b94010892a0e2382a9d75e4e2e80383b0047b97677bf5368388b9ac2303a66924d7c1e9bb7b0ccbc819acda77a01e622fdc9cf508e6c098347ca10186d18b8daca162c88d1a63b0be094f54ae4e3d87f7dd90e456a0829e18182c2bc3490c32c5a92f90a1526914541bac6c964db9a90c68024625f4e2fcf086a11457d3e4b49a88dac0545ef1144ab71f512bc232a326f9f2f766856138051c1e87c32078e871dafc7a0f4b65bd1eb204e0aa8b5851a0502ccf30715782f1c734b65ceedf3d46afcc0f73d58263cf85f76a7ee5b083d8325e1aa249f4e8e19f0e2180af9c61dac15ebcd1f5026e47134f93af4d607b83383ffc43ea81051fb8a5f08635e68c438975c625ace557a7c37d3285799990b81615cab4f16be81824594ae3c7ab22e631cf236d6f584182a43e26491e0ffcd96ca75b3924e2c225067d2080d861191bdc7ca901a3992d3d33d74dd2729dcac28c3f4ba5a173878fb484d540b100e0eb1abef0430df852ef68f9b3f5fa86b6eaf4c240ea0cbaf2cfd6a52842a3ae63f36cec1e7bc4306ffe4ea1c5675b19b134678518292bd6a4fb4180cb7cee77e6ce3d3ba55af65c69fc4e9e7904021324296d974a08d7416393139c479b6aa64bc0d012ffed5db95ab3d60327efff65320ddcc7ecad01c81c08db84ecc7244f3f279c1880cfd32813fe2dffce52195cec08774fcfd183b18f6d347c105ff7c80f97350cd894e2766a4af07d0238ea867ae4dc00d7ea9edaaf311a1b4f54bd0d36417e4f9a9cb85f8316eb300283184c1f9d8c7f6a1f2765f612b6f6d5d2d85311819882b289d42b2f513b7501f9b72e96d008019ac12943d71f3b49aac980256c0fa897b5af8e3f3440117024540a36c0b2233a8c7a838645ec62976d88518c15a51e0e54cc38d5a0bc8197ceb72004beb220a83c294372f39948fb0a20e18dd888dd5bf5b84c315a6523ff0c12cab299bb3cef83106dd67332b45c912a6780d0c0ea1351e018098a2a987a744d4c6370d40f42459d86765476c07fc1897d727e1a4d1b986f59584c6c10eea240f5ab763d5b79c4e12301e16c61c76bc9e5a01a177752c2d55dcfdfe5f737858a86b0c75bbe0aa2014d91ee3424201a7db2006116b7ab4cb3f1c0b5e81154864c0222738850a7604794393f69769dc7d30df07c9babc6608faccda4c618c9ac472ad7c00594fbd8e3e72b6685ca234d6c91db6da31cc44a891c089c1506ad8d0a815b488635259859b452f5c70f4789007d9020dcad22f58e3c61e4783f38da1b0bb5bbd52e55e020837c2ac7ed0cfc48e1d6e71ce7b8bc639345da1b166816015cb19a7ac3e76e9bc2902d6085e96295a838418de933590492f636248b4bf1a02386def01a096048b8f19e3ed1485554d2655277e49c5ea061b2eb4051ebd9b6f0670e3309a3a2b9218c5bdaa75befa4ab9bc498beafbf98a41704fe96e62cc7bbd8b0156ce0acc81173e36a3e561ca8e76328592cb81c111ed69dcd57e830462b6de37ddd4c71c7004086f684f448bc8df0feaa0ea83d90fb5dcc3b417c7471c3523aec0de7d654a5c01e8911834a385c53a2c0efe0511afb6e81848003ef9bd78a9bc30223afbbb699fbdc49555ab2902220f51a6e59e65306d2c3b36ff39294e9ff52bde937bbc0c995ee3dd4993fdaaf3ed1f34546a03f370afafc195d57b0660244366563e0820209d5caa5835f417d670a85a17e02d9dbf60c148690a4392291d5145bc2a4b13cf1a7a36581790546a078a534c7b02a0ebf614c2934d66156e05d1144fb46fc5ac35ce57ff1bd021088992e694099ac827b8dc716bdd4c479b06ac088d68f752ca55507e15891a5256b4bea6795c423582983cfebc51449401e0c8512cf9f380c1572f672249cf828ccd5b4a7248011d46ab568d2027a0383fbc9b702b6bb87065bc817f04234d712f024ece91630619dfe54c42bb8a3f7c5cefdc21932bf5f98d18e90f7befab52dead4429313a00b81132af7003aa9d17ce61706e05ee69ad46be6982ff8a35f182089b735e5fd8180d5086b267840c7139a046ee0f497bf83bdc858046a0c1df8c3bcf41144d7a7431006b8a2dd7c36d8d173b51f0906284d42192a7d37f7d22ff8c51dce68af591929c3fbc30645da3838e90df05117c8aa75183ee1c3a52df42327515a78c8ea02055f85b7ebeed907dce57cd6611012d7bcbfe0e37e3bf925ddc4e53d0b8464e7117ae9492b8ba298ce0b1868003c6e1fa277358f7f9822c2c8cb60393ec12911f12fe68e8f530b6fda679c4247e8f189993c6d792441b0133e487e0790f7ee841ecb1bda59b228051d25213881b12bd1438c8e5a76811f18a18d03f09aa728141381ca74bd495e57ba934ea193b200c4bb5d7c775de9cba52acb773b0a2607688d76a09e75d9f1e476584ee95e55a8e3c4eef6eb6861cc6213427eed06c6c20d3e263f58097340b3f8ccb4b57b788e9bd127b2f96aeda1af8c6c67c19c717647ffe22ac56877fc5d7f20ea7cc7f9f5eb143905eb43914f2661331041bdc23b13b81daed78b574f9d6f4fd0bf927c26c01ae0f6ed0baf7e34936b0df09d3c8f4056a2f2ab86ae4ce52afe1b5ef80e09ad3572d3cb10f52cfd49aebe90bae3c2113deba714140a785d65be9d1be7c1989003e59e5cefbd026920788cb580109ea3a2ea7116936c51a117d83fdc0e702dcdd5df0ecdb586dcb7ef70c3a4520a394e2d6713f7837cd891aece79ea97a710ac58097a96484f09bebf63540cf6f6a7cd437422cc8b4e4b5beeed0aad26121395760119378c3148f14946ca35d34ba0aa6fe0363e9f27cec582782a3414ada834e32265e9aa77ae8ffa02f09c2af31ac62f0243f882227f490e70257e97ff8f118e31a87785f81c5b8bae4efaa9dbe1c8bf29f9e6975aeaa35089b1d94571536aa1cec3d7969aa13093e7a322586ee8b8265757923b74eac9cec780c1c55044275d39e3e87eede93f490af88d2b179496dc48bf012eb2e0c991b9db81c1d7d20a06385aee70c698d36f3dafe2ef556284200aca68aa0aaa005b9e27687279c8738d16aa19ad3d392624b2011de059eb71b7d013d259f118bdb15de610041f04a68db9fbd76fc5671341ded16a618915dc871fc07af1000d3039fecd4cc048a4aa41268a60887e9d184b708481bf646ba9751429429246e408039092a1496c45b09c91a52268d15cace08143041f61f0331dd363d04368255bc2b8ee635a95ac98db9896566c8564ccb712275dcc92b1468158e79f06dcb08a471f903528a3ecf5240873f68671b2f3985e3043762cd36e88bb02b031c611f679aa85ad05aa12624737035b25ff15912dff998a07c48d8687def1af0c11f00c98601ec60c4009e448d187a71eb62f5588dbf09f2c4ecbd92da0a959d7ed515ef3124f8f7e790e4e1bba592d90cbb6fd5e08759f05fa9aa503df1cb9b67e469b57f65f4c18432ec1e55981cb3583dec10072313d070256ef24b361ee0bb5b8be5c48e20050cb3d34a23314c87fa81885887a18e059ea920efd7abc836232436f9ad5088cb5b5c28d681a1679a6758613f260ad62ebfb008b61b9dc032d270ef47dad26dea8b2bcab0edbbcfa3e03539c55ee20bfffb3621ec1eb3099158199466c50b438187a693051365ee296e3077bdc231e52c08683bb57830b0142b72d9dc6a3a45a5af434f54a1a21f69160a7a9829320eceb1bb285d8a7e922ae08e60b6873a710ffc0e23b4a94ced12f71914cec21812981c6b80959cc60ed404644de18e2e0644fe8f573fbacf219efb09c368d691d68f9178933b009e30906ab7bc1da163c2911c1466e0417bf3fde4c4f76acb8822c4bf8d33ecf4fa6afcd16aa97996f641a15b926ada03bb56984a23083be752ea9f419f1affbd7060e3c24ce9e1c62349294444be1aff9039ec62088b29922eb73c45002bc6a1c752d2ba79040e002445236b9d84fc76eb6520599485f2e2ffc8f3f5deb28a47740ec1c207295ad800f476a5245037909b4c840c9782e526738971f7b5c1b0bda442d020ff12be86f537680f404e1bf505efcf8bbd13bad0b621b15069dfe7dfcf03fed1783bf40a1004672b1d1f65c6c4a8da698ac3baeca7840290f0350b426a3353ee232a36e3f6e46dd0adf8d1c22afec8a7254b3655ce1df1d95cd06a6ba34ea11a756913c2cf34be4a8fd8b4e5fd6722ec21a72bb0f98f16c181780d0b0d243e274254e47ea413872d23f57e1a3207a8750c6e36949439e2b8b614ae9ef9190d4be0c78968bd1b0ed1a9dc3e849ef1b208474b558468deaa3d2a24369d0c508bcb56286767d0809b7aee6dce61ea1941ee2c41840c1b5f1101198a06379a2cf4645fdd984f476519b3c4c28ce132bedf5c9155511aa324d436ef96d50a1d8094bec50bca2f39afaa722d21f89e1f473f52606e80fa8acd06140a2e88d125234013a73d83c4535919c62f561f803e7a0a560c36d0c7fc4a884ac0cea5c764c7764787615c7af15a55e461fa3a8a2e8c1a2ddb5004ff64ff18736f22eaba8015d484257927096df8d3fb0bd70b4d818f30fc92fc422bcf3dfff24668d2a089a64d054a29c5dcc795e9025688430912e79be78aae18839d70c1f3983dea6285140fa25f06b4cd6282e6bcdf1194c38ea78ecf5732ee61765cc43da3b04d08fe38c0c931e14c13fa741f9d758969c4996761c656422b4c54f108b634552bae8810f029d1ea841ea98ab268dde1a94ede48eab9a5a45b213b5c8c920060dee675361262ea1d742728d83dfba6728344f0e785adaab31c14c6b68ea55319c539cae0e5d007c4909d2c87d406481a4dfed2b103d576783ef782b0f24121792cc107654e42bed61252f13541b4ea4ef44c406911ae718eb537ce75c8fd8eeb54ec51a34e04feb1299dd12f47042d53dab55b4fae89f974ebd903baacd78d1b3918f7f6fde934d8c64fd9f418f378d11d63a6054a0d725eba7ce86ee6b01c82ab63eb9743a4e1de2d25fe035741f4e124849f13851c069b2aa3309006f85bf751065d43fe8a41a20bfda2fa4d55f4885d6913a7217fe84c310563150092b09b59c06153d7987fa404e89de7c043c062670eb0539c600a2d4a186e76d16a1639945c49620c4c4215b1f0984ab9afbc9b0b49ad25b80dc4f1d036106022e823e023db7724455791c6ecb89dc8c823e70dd36ba2d3f58125e346cab54b414c1dfa4e06f0e24dc3e5b406ced312619ff341547e820d4c4044c0d223e0c22b43189f7c72e8a9be93cb80c2e42b9c5fac33504d68e8fd100dc59968302512ea2535a1363c1e65df9c36bda2895afbd97f372bae26776942976312f14183c1a6747704fcbc800a93e282a311c9a7452f8db3d9e0ce88c046b8ad50553d29e4ef8633b711ac4f69f24fb7b98ee37e822a9d1119c33914b61f1fb1842b755b77d31b3465051e803976cbdd2e5435cd51822fb53f89e8b6d7289fcf9f721a663d4b78146b0557039238f0013057aac82d20902c9f1b27508853754074cd2e0b0a63528bdddb9012d29aabf1c9f68e32abe2ee5c7a89163ab5eb7050a23824739d0de2472f8538bab89c0c68502fdae470e629342393569383afb864f3efa8ca22a821dcb487d146936e61e80b7fb2e1869b9cd54ab7add25e12d64e5220f65cfef49d27e2e44f8910593f32fe943afeca48854e57afc6f4b6f37da8c5d984b099eed4cf2d2bbb0728b739c60c7c00bccdd240f3265cfb8485fb9615ed611fab2ba074833e2ed5b5c1784b5037cf061326deb9f2b4ed444597142f5d645de5819c15a8c9dcc9e6d6ab0290c115191c7e39bef68287aaaf127712b1b8273171c6353a1bb700a0e219810fd6941b72384a73ef71fe7ff1f1e914efb032e2fdb395a45115cc6f170227d5782557e09eaeee88b1b8d6b07a5b1f97ab6d630fafe4adcd3fb5c7630b93a48efb099396d818574ffbdd01a6866bc92778af5fca4a1fd2950ef14a2878efea245be10881fb2a0cb516ce438e1f011adc4e97476791d8f0617ed9c6d68f669fec1a842d67ac0fed0ed21c1ac305350f175b12711ce0b7c402c74e00c721a8b5f71271a468d0169132c92542952209977169d12ac7ffa7a56e1d1558ca4268ac9b4d5e5d7d893496ef6a750106a1c67576660b739f06d127833013e6660e739d0e908e0180740fcca8aeb192eb4ecf4a806bd60b25a2c96cd29d7ecc5830613231f79330f2f1f7dae456ca4140d1ae9c9367782df88fdb91b1cb6488bb49493e80b11b6a463d2788d0bd480bdbdbca3681623b094039f0f35bfdb581c3a07e4642d5aec683f5393f6ed35b57eece2d61506cb668c6664ac3b8bae08d14b1d8be2eea82f75d81845394820bbc4d8bf9453ee29184496711c5521048d5f6cfe462bc280debdcc5174c5115cca0e0cbb79b0bc81c984fcc7e24e2b953b90abf089a40d235e3362c66054ba9c054f192f0de1f8584d9a2fabb1f9e4729329be341d9b7925c3296e82ca6b487f2ced3f9de336f99ddd6ab3bc29dfe6ea7d7e4493d955b1b19bf2a8e423f56cb98d2bf297fdcbf391c32acc1cac3c6c9625e12336d9e143d74b111ac6f204ae6ac42c83f0a2688536bb13a996956262d4185aceb833593b97bec41578542895ec61e6e0c8968239c303bfcfba73e960ebf72313dbbd914c50f8672faf85454e632e31d1cc574ed0b8d90b0bc0f78b1d2730b768a5e067783914815755398a9cdcaa13ebc3117aba8cbbb0d8783f8e28845d23cfb172c9c7be6348823083e75c542c0208cc5e636015b622f063de724b9d8373bc1604382c9406c4148e60790e512f1c62800e20a34a28970ad1beb11b03bfda1f9363c0fdc18ba78852d5daa0170ae06a15d8edb3335779a980e5e21960e4d0b4edac9e4f1d33dbde03cf501a6c40959eaa2c7bcec186c5ed5cd2db384acbe618bcb03613995faeb72f865d909409efc4c0d1b73505a1e53555e9ef47794c36c559a61406a81ef99af83a9c66bd8a6057a5c8509a240a13d0a2a7ce50732cc9d1920a832b8d3bd5cf18714fbce9788f8d6e00ad181a6689b2d6868693a754fd0faf1a3b1f111cd0703cd33521b79024dad0088d192cdf5bb2190f9776f5e9d94f7f1ada478d7c23227e68e81f5a90cdf7251a05ea249e78e920fbe6cf008fedcdaba3fd375146891c67b9cb64643700570c0a2e539f7a16bcb8800c00ca5422ef96842961af18bc11f0b0c5659c862e72a9ae7af42306ac3a90d5a2a342f9ccec97d591c6fe1546c841f783bc2fba9b91655db93228a968677231a29774105d112226b02c325a33a593913424e8858880f3eae8bb135ae41d192dfa374a57133d98f7d147d2def30ec75b6e461e1c2b186eb824c11e7677cd49059b910c9cd855ec4656d7253b8bba10424b387e540796534223a2d10bacb72f0dea9d40f450a35fb4e80702f14786fa26077dc47b6ef0bae293f167b45a7387b96511ca25319a4542c93323bbcc958fa7d2887071df3b33cccddb79b2bb7ada50bd0e2e5ee0451bcd353c6e2b9b7358808b5e593f091519427a4b7c3979ca507e73ed7159482fb383b4da3de3e1ac45cfe4c1b31a23d2255731d9045ff23ae24a3150c9a939c60172fd0ccabd5acadfa9e6de53cee2159bef9dbe8b41d4aef1464e6ebd52348963578b13ccddac1a1ae48c6612a525756fb026edb89837ee70f4906179779507aee5e7d518c44e7cfb80c8f8e23047217efc146791244eb1836c2b0f51f4720c0e795d20f09c26906792f1af1a286581aba0fddcbadd4121a43e3d0e7e58890dd07f22faeca7775d444cef39de006781b9e350a1934a50c4f5b8ce30983136a725cc7dfe29cdc5fa8e12ec339aa538c0bcd4793a17b4d41cc0e96f437eee91c16700af1de0070fcfe680a470183e10db2d93dfb884e99694bd755cd9d67c750dd7e8ee7a6c1116afaa7d16f23627508f7db7d6212817af8874585c288ebec51bdd2cbad48280892ba1b29777516a6e0151986807a4ea2a2102e1f2062a1ae274d662408cf05290bd6942401111fba0e33ba507d0e8ed5bfff625ca8d87cefc0e6f94f9a9335cf7b0d364924479d22b27629d62cb0f2c1ad06c2f0e3a711d3b0d483fcbafc1514bc550847f721a3298ff5e777673587a9e392a024e5e2220ecf450a0084e98f9149a5803a880ea0484c290de5104a9e622246d9e7b40501f6d566cf627b64d11f6ebd938656a2dc49827af4caec7e7bab76d76c6f847f0d9807b44cd3147de0c50afa7ac7e433ac502318082ceea4f3e6530688ae4b4013d6687ecd5be93db137236c01e38ad3610cd35b401f38a9b2cb340bfeb33f41fb0886b0371dc0d84166d200e767189eac4c418daee11a40fae4807b86d20ffc30c8f23d3adad181eab4f6970cbfe6d807f7f5f447f20297f111712128870030941c4ff9c36ab411754a0cc775726d96635426a27633542ca955531ab81f484e884ffffcd5cb31ad1a5f3a0addeb6398756a35864e911768074adb11ab72c720f19a4921fa55a8dfc11790bb193d8f14fbd20f538abc1b7421ad30d263301a0573caf17029ae7f1cf9b154d207b070319ae06555a70bbd4851b97b48f8e5cf7ce24c488ba212e58a8afc036370d4d28e715a8b4eb2bc87907757cd30ec073f81550804796537440700dae8d1b131958a12b30a231cd1463be02fe93d1450016f0e75d84fe774feef608163cf7e0b0a04f2413f285ccd96dd719e461b3acda6e3c0a61c1e4e6bac9ffb460415c6a67d6ce8561013fafac0e906441029ad7111044e34ebb650584e3c502fae615105d0ec30274a6cd5178484046c202a27b801f493a0bb624823779432c884f7016e038a35830c7e66620130b8c1cdd851967b44d9a04ffe55f065fb82c7c697e1cf02fb10d945802e0fc68229e6e3982864622e8ed165b6dd10be6da7fe2d0d03619c158c2cb89689d2c032781abc9bda42db13fda0896cc52411145801ec7df8bcf3c6c6092b0a4882bb48b18b1bd17111d6ed4c750fa7101b4e224bca0c4a94f868a38a34ebe5ce3f892669650e1e861c07de4ecb0d53d21581185c54e30e29e8f1d3e8085254ef1ff0b1977a948e07c8515712af92d18770d7e1ddeb4917fdb1eda6d2759e2362c6264cd293928aa7b9bae468b98d7a63bef08211608b32cd7c2e947b32e600b02b898f3a2ed6031af7d9610e487f01642ca4c4a1ceb63b4a402fd372a90774851a8e211824348300111ffc321507df42ef144a91f3a049e104c889030c122fc142250953ee1c1add95894421111c10454321160cb3ff927675233545b84a8f04e0ab7224096175c1120445fdff6d7db75aebd189930423869bf242b8c6045e2e472d6c85c54095ac608da3b2c3227b546f8c335429b05ae0b68539231e11a61e86aa07c009b60014a0bb7e8a3f225cdd5460825976d2b2cb060e5b427687d9c6396ac7f813dd8fea02702162b3b1b9ebffa9e90d7c15e796e88f047c9e06021c02dcf3a024a77fcd74bef13fd97bdcb96779fbd32748b97df7bb04c08faed5fbdf6866bebda33dcd5977ddbbee1aeeef7c765751f6fdf1f5cd5ff5e3819e9a9cef7c6757559f7ef05b7eacd6eed1d6ea1f471e52ceba421e2e6091d823c69b3d2b0054512e777a97fc726a5778d4daa7f8949a257dcf55c68a4f45db1a4f5eed8a4f4b6a51e57bd4b0c12fd7b4c12bd0fa5ef4223adff8e25a51766f75cf52fb149f4ea7cff85464aef189deb1e8feb35cd1dd24d7db2d130e33a91bb4379a398d632e7a740541123d794361d7e86ae0868e4f07482d487760cac1af27623b842d2895a5f981837c9ab8d8f27693a514249e5640dd383be25bb708baa54c948cd0267ef1e2dca71aa3d21cfbfc6da8ef421b3a086274bc28c507b73f2e3365e9654804b6a734f062e69edb435ad9f7347f53ca5044f869cc3f8510504e22c0174c50e0ecd726d35700f194b53ee8bba8cd2332a0f23c8d49e11972243efd8f2814e94932b838fa28b7c7c81829157b21f610c1dda97fe42eac37c4a2c788fa9e05a194af7a82e30c56cdc0392aecfbbea8f80171921bbdf310edc72f04fadbc13ab604ee1cbefa74916453404894e91284f771a5e66b23d3e11ba4ddd81034d84babd0377d80c113c52aeb8b47e070e36c420383d47bbb9e1d924055ed982cb421c16f0cd552fbac69a2583c61e2f26eb7c2b40d6591d2c10bb18e028b4504d63594434c61bce1b5c7974b1ada6e92d81508b36590d3b54c194546c7068930d0e4168d8af74014a19c4b096371110cef604c295435d46d90a0b73f61cfc913c611c4342f31c4f559dfbab9e542f247565b744ba86d4215e6ce7c73b1f038b4ac3b2950ef59516c926a7ce2ff2fedf48b5df674f59cddbd1c4b1a1c301b24086a256b1f62d28cc2701ba33eb3c434f57a338d2bcb38aa15a160ef0ce337136beaeae8af0a5de3bc7ce8b7a3a62f5f1fdc517a6ff8df07522acd8d98261ee341e291691f009905dec6c65d4cb1586962197062ba6f2c368ecee2e948bd50f2e0dec5a4b7a7c755e5c081f17f2953e7c648d689f5b23a5a31e9da4c6f56091252b037917606567bfda98734b920a31bdf23003d1bdc101221ad2426437533efbf81ac9ff23250ab4c93672566ed0f45e8da7ebc054a8e5e75cc981a85020d3363a7cf41b7b19167551ddb2608730c7333e36706460ba20c08e12d204c9870e35ed25fbdc1ac995f49204602b6e0d94190e208c842c9fe0ea7695ba234213c834b36af9352a263de5d1da50b4f70542fa52c133c052038732df3a0a3ab3f0195ea866ecbf98de028103a1e4bb4b998c0c7cf635d158d897ef49446179f1274281d7d7e0919ec156d78b4c5400d14ba2707ac4be14cd2210ff93a85132bc44de20c51b037303012c3ab72d0bf31a9b9174351fa76ad4fa9946be06590ace9ec99ec894ca9ecf96cd9a900ede908d6fe2972cf513137a97e350d17aad149eb0d80cc74c5b458a4ced47147d3a58f822e27e61c92c01a009d4ade87df27d7f857840f9cd7be5f5ae5908b222832509b7bd274c3027a9d432caf37f9bfcdbb3dc796769788df5a2af8c024125e9a4295645a582a4cf8adfec5fb6d184506fe5f6488607a340cc1fa27e8e1e967780bc51086722b96744d0adefda67229683f66857351183102cd64ade5a00d2519e633472893704cf62e2aa7929e67e89104861535b955df7bd02ab36bf2dff82746698b8f9e544d26880ff977b40d8fe13805dfd14b8a5efc1e6cd6ec3cb81640707dd875b3b4677d0511009b9e6180e4f0be06f9d17c145de575c208659e2d4a7e209102ad0a1e310166183d359849e6d31a4ab0e941de234535d0a6b6af4538c27a65e67d331afe47f51a2f9b670b833cd44edc28998cd83816a2cd55da17f7727bd3d94aef4ea7ce47efb218af6b6a30173f6f8e510436bf233f4c2c0c31b82a8b9e14b5792f34a0d1d62500de3b83cc98b5ae5f176ef682728c9de86e04ee0d19d94564fbdefa0e882b89feb8319e81ba5a06685fe98ad62e02cc54410276ec54fc1216aeef673f3c9ef4735147865b1a83d1a437d2e230f0fb5c535e08ea63a68580f14a0b735a6c0c3af0f806488181d681fde70eb1dc1c539f960eadea185a4a2818e54183e8eb5ba2463a9b98a97c843d4123a3eeb84506fbcbba9c1ed2f799eb7853206e1dd20e4f0a3a227f335e586ad041f36833cc69927a48b5f5638b1969ce526619a7d08a65537895e4e4771a68085e5d93a1d0cdb8daba195de774ddce051e1ce080c08066a2f52424f5308e2f217b7976a736ed88312d35431310c48c8245118c16ef3a818967dcfd3e377be15ad8aee6d0ad274c53a4ad832a380b0458e1eed3f779cf048c8a770a581b6b8148a6b7d6139556cbf286b03d2038f2988b3bb7772abd3a9779dc7ee11906db206f07815931db144ca54b23328764a97a17c4293cc2fc5a5d3a2b47b112a51827440d211c1410977e700ace3eb05342e295bf3fc4bc4770722d9df7a58fffc72fa4326a03291ea9d7da193b2da904feb3eaf81eb1d7fd979476f5fb67a27fd90b0a8b0c4c9342d4f78636d65fc126e2899052d2fce18836fa916ba1063aa0d9c9846683550531df246ee770a130639815eb00ded61c56660a47699095bea142ed02ef05c0148e8c3b0b1a5298c1547c73496d4e9da9a2fd9cdc5775c7dd9b340251902167ae130e3f9b85e90de87f1948e46e58791b06a9dcd77f90c75d643c50be9d4229bc75dd7d7677a3af49d1e6771a5efbd827f178b498c8c8a4ebd2fc2c97972d39529b33c5e5b656ac22e945a820c5041908b8b7e5310cf07b7a8e53fe9f7e77f9e4c0e56b5a663bb96756c4d21e04026c9797faec926b9e2134709f21f0bcf0e049ab42c04253fb7a0242960a83059607c1902e96bf1461a626197e24154e872c7caef3d9065f36934b56c37d0a67ad5e188226116d1a6082d4daab8175648e48d9ba2f19a422dc92e071adaf484a6836bb959756a0600ad338d8ddd9a8b4646836eebe2d54228a7cb0b1357c02bd26992a8049b5b2e6909daf1c024fb1d949e7478b09cc351711f5bf15379dad5553e8ea5a3d6d6fa0a40c7eb94e8500c7db8acedb49223991cc2d85489ef525e5f5ad9f6997b807a8bac4cbe7c42ce309455ee4ec72089c54529250dd51daa289d064b0249901e995029cda1d55735e90d2062a486f3a132ec8eb2e3c1c33edae1eec10e7fbc5db002f436c0c86860397b3a8025af6cb80f68d87d9ca9e32103cd27c6256d10de47021511a89912e063ec94a07c85913dc5cb2d3325cd3fb03a441f4f396b3f001968014c6304ba7bbbb15c30523a4e07971f6bcf7449858f4f9fd945a7649ab6a8e28996548b34adc20dfb7d99b665daf52bce2a9e3e82e75ecbaafe8608848160188639624e58baf007ec0dc35abef443ca39c4ca5ef8879db5c13ea271d326bbdb9632a52403120b120bfa0a3a7061441535891a0e8ab26869020534e03603beaebbdce54fb6d77d792ae6fb3798a764646278dcdda3b0560b70a246f1cf2e00042d5a10e9e0832c9880e107320882145a868e68e2384a3d25b6a4ae237928943a1a85b59a390aa838be149ed5b67a0e6eac8dc90facafb6f249e177e1567e291c4d97065b4e9e6ef22ce0b647ea463f3f5a6a8132bf11c984ee72b95a748515a8b7d8b167cecde6ac0b47d3ccb772db72fb3daa830e093968d51bcb07cdf5f71650f22e6cdfb1575702f48e3db3e9512f7fbd1ca250efe8aead747f72fbb7224fcd305200054c064a860002cae4c10db0c0d2c5115818e1846a02810b172421420b2094a840d5bfd13c75c4c78621b83c61c4506d89aa59a867b767a98d7439dc4a2eec4857e5ddcaaddcca6d4bcc46d462cf62663deb192cb65aad562baf4846330d38de46a54fc69bd9b6ca26bfc2da38ccea08e9813cc9fb2f2c62e42b62044af747acc90aa9cefcfd54b50d7bff794f0a8dc0fcf7de17296284f4a5870204e63ff079073bfeb5e10742fad27f361cc2bc375bc627167d9f245655e3672f5812abba99f139daa7c50d841768b159896555f70df319cd8f2e24e34797ddfe1f1d76fb478f75323898a766b7bf61b12d74253f3eec2c67d222d1cfef87693381beda48df81df7320dfa99adfc8bb01a76aaa3cf72781cda54fece5f63be8a4f381b27d17fa24b150dce76747d57d12ab22a5b45013c1a7fea6d249dc7e8fcad70d138a016dde8224fc0c38c2cb80a69701d2781ab0e6e9c74f412730ef307f03fe5855e9678033b06ac6d7007d92eca84aff6355303f3f18ea7398a36db61268b385be64e6b440bf814ffddec66d603fc78425b90e770b7d45c58e2dfb74d0f979374a93df845b33c0a77e1feea9e110d6e9ffe119e0140b21171fa7c17d66161762260f3f0b3c847bb8cbd4e9672ef8d45f63daccd1b284a5b56a063bb64c8b85c19c6ece34d5374a8890ed2b03a60977be891e60f6f06f3077e3de1814027387703b6a455aacfc11416287f0ed533f09e6a91f0957c312ea531ad5b0c9e3df9168b0be1a0939cb5d2df314ccf7f7cc53df8f3e9b15be2f3d0b30ef3d83de957925b6afe29679aa6116e6ab96a78ee0e0a505839f2376d0a189aadf5d9ea24012188c8105084045f4a0eaf755bfb34a3f619e85ee614223df931e26340205c8f7a43702f35d0906e68fa83e20dd7fcfff911e262cf23de98bc0cc6f0433a77fa78b78073beedc064b3e397a74c73984db4ffafe114c1eb79604763b75f33cefd351fffb6cf8e705d2bd67034ed5680253e533c29b9e0470aa3c07b982363f48a46faa46d0f1c3678623a48ca7f919e05479b4c63c09a0fd11c0993781324f0394f13520cdcf0059e5316f493f3c528ccc0c8d0c1949067884e4b1653ffc44bd3616fa559fb529abe974dd0372b8bb77a00f8782431463dceb84f4f3ab8025a1705f0228ece0f8361c64fdf96db63f72b707b4c8f1a81d972a91592342c4a7488b341ad398066b94afba9a9a4f07fd9aa75162902f4fe61cc6df3d0683c56c7018885b4d5a2489d8d8d8e0d0ef9ef4be83e330512c98a8ca4f7c601e7c75539d04f3ddb3f03d293442faefbbd0081420a4d0e647d78536f4bd9faaef8d74a10d0d6d56207de959f0befb5268040a10efbb37527ad24f15cc4f55c35afc4067b9aebf3c3655a5ce577eea8ef5941659093beff4259e030ffe83afb8c540ecf295af7ce54d9c89af7cc5414c2b3af58a694ca375ab5dfd0a031086610068df389540545a398dfef4e9a87ffa76f4cd71af231ad33c1c1d8b1246d5afd8bebfa798554c143773c7966d2d638e5a61475f51cecacc299d8892456c6d50a76677111371c9435c65a77befc22926946a77e96cd1a7dc865177eaee519ce5ab9d92adbff3e9f0dff976f4e537a1c4e0fc1a46b3f3169d6460308d0261a2ea7f340f12e3b6140f01a50d64fe1c2df3146b99a7fe2f6e7f974622a36561475fd11c3673a678ac45a6d1b8745b689e6a5927a3b5ccad34aaca1d67adc8531d11af79cd6bb5135c28bc6e50c7b5ee044009661c19b506a070b57ef24eab724cb1616438c5f1a9dfddbd04cbf916762281858603092ce5546d5fbd3a1ad2a26a8401222e76c0418b2d3248c2012982c4c8c10f5f96d0b285134f4ed0c317308aaa50f50d48908011430b7a50c31692138b52c55f1f48110314a9b6afe1f453d0171e8d9a415f68b7ff01a193211ff0940b6567996581848bc9112f3852c41716b47270032690f099dffff0d408828801104d5a3ef85084aabf054f4d1fbc584535c0e20822d0a0faa28b1548f1b2030b682852f5f3f0d4036a90d5a01586161f74a1ca6247ae7ea07ea0c5feb9d3a8c9b56d537f8b4c7a346fde189d7bc475ef26ecdcb80ec8e1240778ab3d4a3d4a49f3bab7664efba9590b3b768c04837f21a993d2c884286e7b69b16b958e400b802df0007fb8121e53a2512be4e89a8333169bb198cc7946bb56a93b75178acd1c39e60326d31a3576ec6e69d4e82d6f4d1eff99c3759ef83f6e86bd898d1e906316cda10854b9b1183373d76a1ceb5aaaf4a658c762a590963dd46da59ba8b7fc7c90773c89c8a9fed25dcf2bd51f0580e2c219a73c76a20af0853b5e58a7bf72d556faf5e90997861c7acb0776ec580eef58a3b804f97a152b8d82dd0e126ad4d0aca3301777eb443628e6eede40f7879c395d0a3bd657baec59cd53ad051760ec9815536c21ef224fd25cd41c8b1d672c168bc5c6aec24e833ae6296f79ab63ec2d6fcd580ea122d2532df489bf36ce58118bb69a2547e95404fa87a080422c16f398fb0bd4e2b1fd61fe392965d041a6cc51f875997e3a789b7374a457dbf6397ab4f58ae9db6095b0cee4b68175f869f7aa45eefb15e4a7608fb89a808118f16a543f69d4fc799b691a05c48817d37e9fef6362e1cd1b63c76def943aa54c67f59a4e15e3d0b04eff28803b5f44d91679b689bc53024771f09603059257e2bf214e9e0ee6e79b502f4cb1ff345137a69862ff081345bf8711525f888c4e3151ec03c7791ce7711c7b3d41983b5a2d2c5f8eb356668655d37e208d5db123a5345ae4291297220ca10aad798a8b582d4fcd180c15299c58c93cd531929c959ef22886a408a2796aca6a51ad0979aa270741c464354f7511a6acc86de62919190b884855ff565617b9b13c355553557f3c3555ddba2395d162b0976b6bd5d56dbfc2561086222409837f21bd824e4a4f0269232d6f29a4b30db8facaed3a07bb0da45b5aa4cd405afa30513d8329b6abc51950227a658adf0aadfa422a035be5a51db9a8d6ed69a51bad5cc49140b0bb5dabd56af11f4001260bb4d0627ff419ecba4abfda35739a69b02395753f934701b7df7b605d68b19bbf98a7feae8b89196184a22c2c95b56cd62e57bbdad5420927ccc02f4da055a508f0d828ddba12e98bc0914f0757e728068811b5d6a5e18fca3c9f7a6954e5d13131313225885fa314f3f5f0cbfde8fdfa28440106d7e1610e191c3870d06c5b48f3d5efee8c5238ee5ce69bc32b380631ddfaa3f79c866bd16ff5b0041898e6d351c39d16878cf0724b60abe6d0112b4a65ed02678b9ea2e1b147033ba0d69399d36c99cba7838b2a48a398798a4a41b5b83d521edcfe52702cec3869320edd8ae59a0eb7c28eece57691a736e0a7fea2a2a2a2a2222aa332aeb45a6b32fd8bb421cfdd256ca084cb8cf3a069f2cc2becaac569b956ae957269b1df9b51d9cc01afb0b3c60031c20b0f6cbbee44a208b33b3f1dfcb915b6c6003962d2182047b0dae583bf5c6e578b738ab5b988632ebab4d6a8d2f75319957d09e45beaeecda7027fb9f5a9cc8ab8854e4a43324f753795dd9eb9d38924e41ddb75fbab3873f8bb92d42ae4c0633ba78dc79a14cb81b717abce60e7e770e62d4dac8d3b47e0b087c7d6320d1eb72c8739bc8d654f7027b2e38c19693af31c02d4c9b1befc290a4f83ad652522b126cfa472fb69cc569697fc693369f18aebb3d949d80b7fe15a8b3fa4d7fcd45f4362e674122d851394ecba2a3119e18e1de3f7b0b2eaeac2805a744742ce1c865d58a39c2449925c808ccae814b40b59a3bc466934897b19f22f5eb8d6ae56d23e5ef394cfdc67359fd5ea129f79aade507bb85507067c29ac664ead250f794a8b3277ac0bf852f01850e2d87cae74f9794a8b1cab3f320c566968280d4777907b1a9f8e2e9c96740a83d05cee4f9f0f1372a892a5e33876841d19e63f3ed65d89a3e0f6bb9299d32479e3a493a49335e75aad56ab592b1d3b8101b456256aac2a51a7b835d6a82a9ba20b2a13d689523dc85b2b4fddfc4d68476b6134b762e6380fd8b910d2e2768e59125be38e7e4b6cfdefa7837fd6e21095d5c08e5446678d9a43237068e335d6e9bfb936649e9a4147447142288a214c49a2cae992e4862b548a98c10f2a1a5160c1850a9c5062053950f5b790a742209bc10f0f52ccb00a43d5eff4c753b5a84f3e8daaa5d7ea964a69dd809a3f1d140576dcc82b04c9eddfc899536bade596e9e486a8bca925d758b057d7bc56abd1308d40824d8d1b1c1b0f92ba9a229b6e58179389ee05260f077901136511183880c14501aa9032e50b952e5852bcde41f1abc60eaac68b85e0176aa7c424ffd4e09e5963fab4a802bf874cf20f9317984417f0aa023fa51ba0ec1ccc343d0ca704b04d8e7fd8875f7664a012ac0841040e53f4e005142a1e53bc64e1e14a14162851f5f3ab9f61409dba409f7a0b3f31718f0bacf30101704a043c4b531317810b538b0bc3776425fc1b68d1c32017a031d89177fab6d0a8b1c6ed4af2e79e2d649275fe8ae003ce24f770acd3cf2d1701a738061cd95d068263879bc480c9e51e6ec1d4611b5ea00405486c939695b0125e758aa3f06b0c3beef08b5ffce2174f61252356c22f56a2e436cbc4209cbd6e269c211ff22a7c08c7d46257983b5575eb1811eed9421ad6692a9cc2a9348dc3298f017b6bd6440c64564c1d76e28a564d318834958e390b79b1f48e2ce495a845a11833d172017db16393edd3a871d62e3f91c943bf9f0433d5a4f70f92dbbdaaa159b01a6ad239ced2b834eed875b63cc5f3c47d03debc1c955e866e65568a43b75f3ccd940321fa4bf497c8852498b5578c3b361247622c269b9534dce34d5827069cf22c7c6a6f4273db6eb56e5bad11d811b564f12662b8e0f653a605f5a97ba8ab3095db4874ac0896e35e2303bdbcd86ac3efafd58f8e8d7d02eb308fd2220b9113050ec94c71132c44b29090a7b8b09198a7fe2ec60aa18e89e288c8a84664fe68d4c8ddfe12fac45f65c670702788b3a8b669076fde18bd970d2ac93da49096ac63034e552a6825ab0c95e497773d631d2a38b565e95907733b0a762b0bf7340f53a77de041f7aa97cc9c55a9a76f5b1ae5dff173efe57012e8a57048bdee7917ebcfbd8b6119ac77d463c6f56dd5321ff34564626442283426b481e2fd54ad7ae5a99e4d18181af372feee4777eda83270f41e69f361f7361f06838b28f52aab64a575fba9a44702c245597eea27a56a8b7fa4312a4506841457ecd61a3889797f4aa9bbcbf5631ba7acf64ac64ba7399117d576bc66ceeb02b1f1ac511ecc9db457947a4f7fdcb9b43f1f130be97665d72befd5fc76f84bc1bd90672e4c30ec8abeb84c9e5ecd1c2b66aa97c4c4f4aa67254a7bd533bcc452f2f6f276818ca85750e394ddd9bb8e597e721eac3f03b55afef57ac84f5caea25a497b8d95dc984c611849fa94349e6509a424fd6954291cf996be396e2bdd7b81bf94b2a5e7e72f05f6c143eae5f7de2382a5e46dd229cbb8b658e629af34f00a2462b95dae605bd528aeccdeb4cced8bb975f12e76f45ef784115aaae8028320ec801404272a30228b1334b1454963ef48db6a5b012db987147ac93ab5e5a71570aac6a0f392d672e64411833abbdc53af4c9d7e29f8d45fb16881e33576a7650a2ec89455944e2a7ac061095509280cb650c598b6bbd52a47ba463fae9d4a56a04e0075eeee72eca25a492b8b6a25adb62a10cf1a45da7cf34ddc64fcbd67effdbdf77ec8fb1db643af6d346c3eecbebd9a86896230a6b8f9348a8b9a969b11a42d8a97cd06df0a550604a54f2e0d826e2b4aab8cd2288df22810b38769acd35488dbef402d56579fbaf23075faeb8bfedcf661e63017b741afb6664ecb5a24ddb1b654984497b45a79d7bdfbcc8ccc8f3f6e901dff7478adc9c33cccd4b6b52ccc10ac1128406a5ee68dcc7ccc1ba97919d7628d40e9de48cccfbc1199aff9981ea4af59ff23d7bbb036fc2cc4fcccb320f3359fa367cccf3c90989ff92e0422f335b346792452cd4c7793debbb094c32e87eff0ac451ee200f4cc1987152db948001d0e2b266d259db2c661a2b88b29f633adc592b3e01e1e629db6c254f0a9bf1548af9923f364b33ad62db257ae5e992e0e3b93f881c98589794d1e20e694729dc48094655e6bb129d74088d14f6ead51fdc30c824f24ab9a082fa9971e929c9fba07fb7d3fd3d01b6d3462798a722d8ee44c2fb723d7cf0e3fc3fc5c7fa28d464b1bec46db886aabc6fc34043bd2b235729692de0b47d19f740925dcb851a3060d4d2994ed9420def8712bc7addc581e868579eec76d15137ac15e31a1176c66442b398e9d96efa99ae7f75ea0e243933ba9f8a0a4007752e9a107777b798aca3c554337babdb66da6fa4be1f34183ec781cb8fd08c1c7a7f4fe363eb60bf3afd5e6d362fb167f71b90edbbcc47c84464ac411932e2c0c0b312fc3c2ccd7849b9223a48779f157ab553806d9b9a4e7b6950aa487796fa63402eb1d417064fc4b0307b25b7afa5e09a511386fa9c4eef5aa5d5d58195708f55e06c9a67bbe5e29ac804c38fd643f26ac00cbb4a85f2194f3203b1766caed4dc9f6b3f9d81aa67e71b1632569397390cc1435996262262d61eed82f9b15643ee681c8840d24473b18767c0a6bb1575b6fe527d3b2efc2e5efc28d6848bd1b6d2b298d16d15a779b4c9d9a2de44a64cec49bc4d01890ae5caf99d3ce2b1d362bcc7ccc03997920312f13ae7a153323f3397aa699b4a8ea557f9fc04a7eb5e5ea193b75261b15f332323134aa6ce698487070d628666a9bf6876e836d47c05c0edd357346947e29747f6ff4a3af3f2d569f162ba902db28d568796a7bd516e947983bd6d6ed265b0463a25a8629f6375176d07527f8d48d7224e60ce68edbcba58801a7cac329c5b497721603bacb4f31a199139b3c4d7814b7494f6d4b72b8f52705b73fdc5633876cf1c76d556ff8a930734b93344993e4835fe0eef93db481c0ec429996fd4bfa71487d11949e4bdf7d3a4a21fda13e41b02325470a33b9ba3bbad0759299b79feab3adb6d8b1ca6a1452eee4a1325a13572e45a2ca6475c89b9d3a0bddb167ff26d32ad579c9585ce6a76d54af1acb445519a65836aa922dca8455e6ce84759ce89b40ab62de5d5b39423222b7dacc19c5c0a7fe8de6a7fe1ead5c704726586d45e05f4ec14542366aab324cd426736d386c5e6e3f6aeb32c52fc2349c80891aa84a507b60a28262e3831ee103a440356eb51192394735dca6a3d5fdc2f21d7b46573e2d92b5c54af887653c74fb698bae286b2b47e4f5229ba37be629ba649efa690e94071afd6166bafdd155096c550360b02f75adbc0bcb77a4af1e93e892c22101f83e306f1122dbeb75c7edb55d99a77e2ddb17adad8b6b2bd9bd7beab4a3744515600202bed27b269476ee9ff0a540bff4a477bd9e00c18e5b79b5d89897f9efb672248541762efdee05faa5277d3d28e8fd8feb3b362bc4bccc5720e665c20ad0f8e934c8ce2d3d83e3df2eacb33ae485a51173c2a7c3fb133e9b1566bee62b40f333de031fd0a207e6e02dece8ae08b4d83caaaccaea10cdcfbce72e8f32a3a6c69fcc00a7ca5d9eaab22adb8efc10c30a685102ddcde8e3f73a674d9e7953d925c4cbe3195309459cca2e16825f3837b016598889e229a6c83861259d9c3da99d57f313ab3bef819b2aaef50ae645534c9483d139cc9cd5cca9028cd6814fbd648a5335eb2106038f41a7b4484754d6a7a6334aadd82d2d1675b93299561e33e93dff793beeeb43a0d6f0057e2eb4193dd771a311e74ef29c06d12c2ce96d40c063f4def3982008818deb8523ff28b4a93fafb7fda8ce298e9e04d2298e7e7e29d00b4391bb8dc05a7a8ad22a8d0b69152ad4221696ca827860bb5abd57ac966a0dc7203494c6f8290c8acb3c522a6ebf409fa853940ace62cb7c85340af7d01675324ffd439dd4d299ab98392de3b85797aeabd568a5d4fd85c2e520985789f990100eb38c432a73998ef1cd7324b73ae6a94a4a60f4679cbff9980cb1ae0b2b11b562852a49b2ccc1f0229635ca672d7a51a3e8cbc198282a8467b1c243595021268a1631c5a6b01629cd4f453071fb29a5c9682c65e6b0139c44fd528075123be38e0cebe1d9d407c01f51e18036f56ffe88ca8b21d9f8ffedbf1fd54aafb063dbd4bff1137c9c6721fc1ba191fff07142235080fc876fc4c6833f559c8d07bf85fff0e7a742f7f581e0fccd6fe00a387f131681320a81dc3c00c222ff375fc4c6e37ccf1a65e3a3fee847e104f907fc8152df66f4ff475436a35008101bff611122acdb3f81a9629235f2cf4f55f823b36c8450ba9f2af0c759b3014ed5873d0bc116f2ef870d1b21e77125fe7a78ef5ea3334b492ff473380e385537e05479a3120e3855a34d0ffafcf4abca521e5821dbed24387513d6959ffaebeac3951437e1ca09da5a991c9c749efc6bac687e4aa4ccca58e47a432dd6588dc96aac0ba9ab45a06a85e5d92b89bdb9b47916cef8c53370f6783700a88f73f37e135a50049c08ecad610d3ba8e4d519c679d36b736de4f675ece6717eac65adae565eabb153e7c799b11b654ad0b3d7bdf7f336cdad455e586b2b58ef200a7a1e8a4e514b5bbb09cb1414699161a698e627b8fdfef5b0e9e1d5f7429b1ede7b21d38ed098e179dc937e8aa477b0870b77b4c0e59c0b9ddc903e273d2becc8340ad6dac467b5864e3a162c45948d62d815d6e3ee980c1a2c8d5b4300c46071fee6e7f7c307e76fc87933c949b2700010fafce050e1d8f8243baa7ff0c33009a7c261197c30c4155ec460498e06003855213855eca0e992b499a365ce99801fc2edb7518570fbbd15669686fee3d36215b6daf4a8ded73f728ba69716bd467da8b0a3cf68a33c7472f3ab9b15499d7ad40150bab56c9173b721099fda6f1ae5edc33afd9e7703b6ca6fb8cbcc61229820861ad7d8c6fbfe4a422dab7b4fff411a4dd81b15f8cb75af5df51b0e14801af7f09320658a1c5810c60d4b9450cd20d0a03cc1162e78420a9718ebd05c6f001587baa272a9d75ab43cd41a35696ebf7b6914cd3d1284106630830eb6b001891778c0894f0d6ce042fb420695d24aa98dcfd1d40637caff06c7cef4d4cd4da5d5f2066c953b39b3dc5a7e8d642fdb8f7ee4daaffc87bbb174997eed420ad6d5f49efa93be03e97b2029a42dfa7be0c8ddd136fa3a023ba8c5396fcf369f5ad62bb574d249275d89fb38b9fd6c4a1a454b67127f5d7512dbddb1ae5693a79358983bd65527b13577acdc7b36250bd62b5ad08095a8fa00ac59664eff0860dd82766b805cdc7e9b19707e865af424cc6b02e79d31af0c386f0c0c7c4b4fc29742df5258cb994373606b7ee6bf1dfc328ff34d8055378fe363e6f9397c81c6cffc84c2969eeec08e14e66a14a36078bee7274d9e6d44ea9e5fc6e4f19edfc64c1e4a734b6f430a5402a96bc8b65aa509c57535aa14be10f34d734d3f7bc68f35ae89e6c71bd7148e4182dc98e79f9f8e19e158e306c7c683210068c2f146006240be161c3905fb341f03f20d47bef64b30c8f8afdb9e47cf3de9bbf7be3ed3076ab169c0f1c88d293d6d79aa965d0a4b8eb63c454e61470a83358a4628847f0877850cb1b744a7704f4d588958a73fcb16b4aaa57251bba85bc0fa8399e3c5cc01c2f6536b2079af4c1d2c33a7dfc69bbe155c7513d6223f95525858518bbca36f15ec6a24668c0609d45505573179bc5114055ac20934012f1bd0160daa311b70aa52a141b455a9502aa702d41f7d36cb1b1c1b3f6e4aa8cc671f525adb08679fe8e3840ceb53ff686f6853b887c6f7b311930704334ff37cc4f7f4c74ec6d71f3989d27b3f7a776498fdee470e8279d28fbc44cc730c0381cc8f1856b7700f8d900e71a338a4b216296d74a790ed8e54c7e54fa66549773a8103977bab169307860d0bfd8fa8be0c1f785fcad25fb7983c30d0b88fd152d2fcccf3c0008223b726a4558415c9cc0129156025c15ac3ccf9a93458b026298175061960fdc02ac306d6510f6e3f97c5a55858b9fdde1577a4322b6ebfaf9c9c79e95648d98c0f30e3ae5765950a2fac2495d15a5b750915ccaa37541f62b8fd5e7b01aecc2da65f31ab97cda7a763e15a56e7dbb508dc0cb73714dcfe4d05379cd2432153c9828d602aaeaabef3270167d9f4a8ef1ff291cb341cbd05dec46b949c3474b2632dd441af1629cd4f4da3b45495512af84465ee5db628f5f97d98a9ba9ac115e2f66f31706a725d32cbad87bbe9b0ddb09960d622d79a49bd325337dfcc7cf270e4aefdc274fdc7dae58ecd04c9a53f5dbc202ff7d01aa64e3b11e80992a0a5fe76d46a9f0e0ec7564133993957a650d5c27f1386375c594c5a6c19ece8b3d565d8cc49aed56a5cfbb7fea486414e360a430e8395608052cd29b1a5341da57777f69751b28ff3374e7214d65fc68fb5645a25b25ce28436f471febfff96de3eea7b1b7ff34070fec30d8445a000b1f138611128a3139c6781bd18c2095265a4042189f3386109c03782f336be081420ff36c22238ff210e3ff517f99f4e9fb06c831d9d044328f56dfccd8f385660239c7eba9161d9a9fac7f971cea0b09546f4f6a90eacff56bd7413c1fca86e91b1bd8c9f1fe9fbfeade5299c3731b9f447c9dd5cb71da4d43d2f1ce99338fbe92085d37237b8ef6f7ca4cf8294e651489cffd1570ebb31022674233e1b1e3e183e0886e104ff71a896c9801fee0773013f22106c7c3fa54d146c8495e627b0d66eff28ac443afcb91c58fe2df4578b4e9d083782fa14d66927dd7fbc06279d74d249274937eed552e85b4bd6e9a7542ac93a5cf30398e021a054974c1e117037708f08ec9d3c9cbaf9ae4d50b5b4f1c12ff8f3cfede77dd6a7de5838b3d2faacad704f169c3aa2ea77b251ec3f9ee5dad9822e1c39184ceb50265d0d71fbd955c58e5cab798a55ff383efac53afd30f8183f6c3087a30db8c7439b1eef638c71bd6dc03a952806fbfdf001ffe649ac7af31c69b79f0b8661596928c0f9ffd149362bdc3cceb770f338610b38ffa1e8a7272cffc8b54e62bb6583cdd19e8c6f472d3b081b4800061f638c8ec23a6de3792a6de674c581e30007186b79ba23d7c03bd612bc23d76ac9b5cab5155a6523aca5139375eca7a3964d30ac6af9e2a77659d5b269378f8373c343d65715a22a41c67c48aeb97316870c0594a8901d2b135646a5a18ec01ce7bdc536d8daa4511d98b25bb9873b35bdea7df53a3065d7a64778e4ceeb797daf9c16dcc34e58e6f712c7d54b431a6b5361f400891b0c01c44594a9c513404004e1d3822148d38a67f5c6ff8cad741293a76a31735215cb3cf59b4c3377ac65ccf39a75a8d8b1962dd6e9c701e9f4d34867b79b708dbb70add5a891ca2e50a3464a5bdce3334fe17c3fd7706e9c003db15aeb149706ddae2bafd515cf9424d67b4a5ff566a55b59c28e5c8760022bbc1c1186155602e0050cba4051840d5438d17531180db2d40b19d6a20fd4a521cfdc0ee4680a5225acc725b64fc1baa5c5144a9a841d2b127664587d55d2034da2b8887b9de0c3c483939964ce4562fce4a246995a1463a27cf068b1299b1a85c266ba5dab7d4c54e7c30bd6f9c042a1514d048bfd0dc414fbbfb05bfe1e8e9c00eed828b066d5bdba779366353979daf3a80c2cb7f2d63ccd26cf0cbaa9ef1b083193140bc4f41f88bae5419de26af3d46ff2d2cec5b77055ec90156e8a99e2b82b2c96db5ddcdebad6c87de586b8ef7eaccf0db538e338d28fb5cab659a36ad89adef296c95bf3e43d37e38664f5b9124cfdaa2cec9f0e680993b63ae3865a6c598b5dabb3eece257ee083cbdd19f4440beee8ad92cac06e3f36f9eaa2eece6fc78ed9e228eca199777dd5a8fade9a3929b7d2a719b4b3e32d6f81de1ac15bdef2d9cb8e9cac66925999b7ec899dacc3cfd5809c5fa4d36eb5d87cc72627eb70d8555a6c97e22da0cb62b1582c160e1c5e9d88e6639998b8a7fe0ce2aacc8666b34f562ed7ea5edd6138d84be6a7799b1b6d3cde6b0afe83cbb05eddab4f2aab9193bdb62f8529e3649c8c93f1a0fc5bd1e53a6bb1d65a7fee1622d8cc91426b60b95aebc759eb9e75fa8978ca7fe080b8ed605f6e75d9a29516592c9692468df6ae1ac54c1ae5cfaf9ef16b674adb2a43d78aa760fefb7e0b038a5dcb8354515ee5e5a0290d6b584fe99403759094d9e33c4c1d1fba8857b3ac531a4099b911b6891db91a579b3cec40d69f3eb17c9fb8b8daedf7d60cecc8bd3859a378a851b35a6560e91d1bc6bd3cc545c159c1bdb82bdc8bd3c2bd38d9943ef5077155ec105bb9f5d5a2b7bce52d7f523f206f7d2d7e79caad35997ed65e2f06ba266ec28423e2b5eec99d311a00f9935ac3eee5758f2ed879a8a9d4b6405a6c8ff39ee2a0f4dea8e64eea3896775dc71161e647b5516df27419d5463cf3343f554638567d9a91634d60aa6a7e7e361cfea00981cc8450c61b849b18a275f96b424ee81b85513f00b367c465eaf4a76a7eb4343f6306d865f902fff7f547f1d62f8878f9b92946d8ef39588efec079fd63df98796c423cb8a78764787199c630dc1410a6653c0c076b18193b72b4f7bde71f7365d9571ca8512514780cdfef334cd408c5b90e3fba0a6eff1cdd878b55c7f23e1a263ae9e771e5e4b95296de915a3a52878d3f9099a7f9cfc6435358040a109aa7f1d3431bfea9a211161915e10411a48a26e4b889215aa17847fba358bee06f930233efe8517a0ebfc5cea607e9f977909e3be6df312f87a27dfb2450c810eefad7ef18643e4666c4b162c2919f70b11a9013f2381c4c9d2386b032c2160a633fb891773fec234eb6b96f31341c16ae6c122cc23ff45622b3d691dc147f0e166b94871c37678dc83cf57b186b710a07e362eddb178123d73b653b9f169dae2c17e360306e4aa3f83d057674e7124aece0d63b99a8f2ba485cddab83959d0caea4a10bacd3917eea92d084a424e1625c1353a79f08ee09f17a32dc05d3982b5d0aacde9183d54a84933253ddd16e9e1e62682d38d59974b037906e028e0b1b0422dc7eb6954735ca6a714d280eecd8a517320a8a53ad1ae1000720b4546393f5c6e471f132d8dc5c5b65973fc85e35abeb56bb56d79a3cdedd5ddbb34ac4465f4635ee8a6b611ad801752f3fd1991d5c432eda2233aa8dbed0b087b68e155d99391cf7c5d4e1b400e37e3bcaed8e5c49a7d8b16b8db8cc1ce677a1451eac237add60dc13ddeddd6a1c71b9b01197c9e3f5fb7f346aebdf2a0a4155623175a33f58e0c13d341c71611758e054063230e2e282968682e6091e54dc1fdce5e4e9c9339798298ee5d3a54c0762f63b9299e2ca2b338725a5d7c069b15f4c5377b9fddc433422ec570ec60979aa86e360a32ef3d4486e3ff7034512515ab7017726214416d7e3aa7b75e7ee38aad51a2584bb475e3ad55d8ea35a39849a772d35e5ccd33c0b353f83059a9f098b400132f335a6195f33139a9aab710e71e0d8d911c56e1ac44d69918b15c1c15a6c1f36ae874e6686ba45f2a9df047316c78d2a0dc27a0b57bc3173f81d0c225e9b5b419b4b36518b1c76d35a1cc2f68f4f47765dd7310f5dd775a4b22c2abb64b1843a5937ebca8e263383837130512439d80c7ba5b4634d651be252d355f4900b232edcc3e3c7e8e9bbb81c664eeb723f4c6e061c6be6f4833097e338224a841dbb56c79e9a51f36307e3588d12e22f640877b92b5387c332739a748a23ea53731ccdcf7823335ff346687ec68f4d96940299f1346fa4e667dec88ca7f9b1c99a9f7916663c4d1128406a7ec64f3fb140f333c222a322c400225535210e3ff90d5cc454530585eb7bcce10bdb57126476b0418e4cb9db8f41ba77f005fefaf5bd51adb4920661f982db27d3b29dabc5ee805aecc2ae25c58e68d782d9b1c93e010d13b591353437fa74d41f75d51dc61fe6a98e8779eaff010821295debc795307bf5278daad2dddd24e9b2e21c9befcd80ad128348d79fa95d7696cec2a9a6c234f68299498ef27ab27225feb3d5bad5ead3b42e6ff7fa21eb5a1dd962f1a99fc5b1581cacdb5a7a033b76ad5a8b05d4a89173356a9ce5ab51e3a8d63d79b6c1f6531b58ff912b278f94c953278f95c9b335317b3a927588600e8bd9d32d51a5535d943ef573300ed6b5b82e68e6741d0f60f722b9a703a2eba123b71f4735ce07d60e13c5bd60a23e168c634161f9c7bdc74d8e1d66c497936124c6448d3151dda8e64c4635afe1768d3b5a9fec5a1fe8515a7c3d9939ab16aded5a5dab6b3da1405dabe5b2a1c9b311f93799aced2da8b96cd9424474799cb5b632346bb3366bdfb703e68e6a33877f1cd5eef7a960e37e644996db6f29f03d72ebef20f5207d0dbf6b68d3a3fbfa3bbaafe18d9933fa23777eb5fe8e79eb281c838897d6e7fec8fd9ec7b85fe864e6416e0a07f35497ce71cdc1bc8495bad5dd1c176b949069085b0a5b00d2228d623bd2535cc9a7fe2d1c392e2c39a44158ae649140ae8b12cb446d3151dbf7577260d0d241de2acb53ad85f585ab0bab19a82c3be6ba5526bdd64c2c2e5021ecd8250c660f0fd621819a49f792d9d343acd35fbfbf63e014c7b3f1708d211cb82c7c326d5c3d9c29a6a5d80b17b8878e6a7e622f3835fa12722937b0db6add6a652632847b465c46354f712c1f7de114e70276b65e6f232e2e608db88cb8702e60161861dc7e1a841dddb17b887b7eb00e9785539d844f5cc9837b7c86d95c7a680077ec295486ee3373aefce09efe62ea24692d4a202cfdb123619c121cac7e3f57c44471dfcf1d31511d27e360958375cf4d67871975a4a71ac93cf5d7c02db9dd1dd964c9ae1681b8357338ce5a93a9cbb2dc3e1f5df6969926ead2534dce905d324906d1ef5a5dcb53b3bb6b75d7eaaed55dab13da8975ad98ac6b753c49db9198662834a8af28324a49db887b86796c0a1907ad56b49242bc487625b8c6550672f3aac83d3290f555859021e0ce2700a7a8ccab2cd6a93a74ed41b5b8e3335762a2ea14b32eb981759854569f5aed7d94ca629446bd88c128516354c864501f688b97d8ca2fafc6625e3e858c65b2283499937e6a752e036ee5adbae2c15932219adcee814f9ec3145f3258f9aac43034b8a746c6f7573026cfc765f26ca330260f47eabc4adf76a904ea98f15ecd4cea8692eee57a2dc67e89866725b0fa9063e9ed9320b3832d3dcd8fd696defe58c9d2bf60bffe34ca86cd71d696f3d46fc351e663de9301eb4f254bd6a75131e1b4f69b246d38fe2d855cd2806ca5079c021b73e7124914dded4e269cd0a236794a4f7f6e304ffd3825906f1562a668cb5544099cb7be3c4583e6a969d06b46f8e58e348888273b4ce983268f10194b4c1e211fa53279846c9489c92364f434df4f85268f10ee677c3f6d62f20821bde9fba913934748f7237c3f7d62f208f19e84efa755268f90fa36df4f73307984d0aff1fd9e17a73ba8940dd357450e324523000000000143140020280c0806440262a1589514bd3714000d7f9452745a1d89d324c7611043c620648c01020000220020826d0390a878fca82b7b6b0cd8f02ab0653d9000185a0f217b24e6a86306f688a612542e5ac2d836451b51f6a3584cc2e64fb4990b5507bde2072deeceb6099005b122b7bde3a59412300b6871694e16fd4a422f5721b8dd28e9c3567d5c2a1e7dfd713565ef600dd725a36e68cc0c578e5f701043c4d3ceef51a29945895f339502d82733a30401f3f4e4ca7e18aa89828017ccf79904640afa61f6424d1372ffb837275dae343b67a52571afe65e46f7cae47192c748a4370d195b8808f7619fcf84a265be2f9d9ec2a515e439f351b17adbc2bb9dd2367c79c134a060845c3cd32a0da405e788852c42f17db58f37bdc01604d8d808e3b6a45506bfa75c95cb6ca54bcaa7425485ce94da77117db2badf9ec2a20b47e4b640b2d73438926d28a3a312e6b1d05a7773d482c5512ce5b4e4c8c2198609d32f730d68225dfa20ee1129088aa0bd8881f70378322835fee7d900853d0e6fe384403035c5855ae62a0900687e0e542a266503cd5fc0a30cec4228043a65f3f5642a9bf9100785ab39e075e7e536b283a3748f08f6901b09f001c8e63d0d4424e909e0f93fc508a27502131bd4cbb9c2286920c336326e1d38bebd8e75e602b2db77129918f34dc9cfc45f9dcefc2ce6209534488f8c2875197681c926be762feaa94a18440de4e226c0d93e15656f1a423850363c0654dfd7cc5ed4e33ac56922c60435ed604995a073f4b9d9bae53a6e674e45a46c9d377b7c75ba5126cd803372dea4b642d9a399e834edbdd178ba75cfffa2282321df0eae93a033303f33325b614e772bc516651a7623686680b9d903c8bd6af2c8ecc52371c392bb5e8b69f82bda1a637ff79ca66070f7b7793115f6a16d39d68356f5d15daa1d0f5e5c1bd799e80277139f0cb562f12e3244bd451e7a7eef6a2d08c74409538bc17fdbf3bb2527cbe628b2348e2408f1b5934e0fac395cadbbfb10059a39a2d05e8104bbcb025a6d4039b74d65790cf1a5f46b87dbbfa0bae0c2bc4d64983a4c7888395eeb030654feeb59096144742f931c96b27c047f8f5587f20c8db745413d0b00c6d7d96e1e55854a6575dc2bab894cb39c918ae2af681a65a443e2e1db9f6e9d8da172a368df9bf17d70e1b518e5a5bc2c09d02e06ff0a5fd5820bf0ab819e1c192bc4903049d2e4a1b108499f12e417c8d74affe762dc4ad7b933219ce9cb47e44760f99a0f31056452762df96bc60f4f6890731b1e5305464a5838b14a6c06857f08ef6e6093c413fee7e9c1fb879a82704c9c18bfedfce8e12948018d274872ca24173e3e1f6876d4e23fc4a7398d88b79f1c74c806316254e5d4c16c2f7b456017215bc409930d4504135b46340ad25b7902ec49c7ebf378f7e00c313f9130c95b317af111a70d2212ac7f4dac906056bd1fb9fbccf275cf28dac4e565867dee4138a901a51e74d6515abc687d03dfc15849e6418f2703f02e5981ca7203165902204875128a0bb99d4aa726e99fea16b1fed807e091175fc514db51d48767fdab67ac46b5a5bbfdafdb054e95bfd7b6e20bb9cf7fc41d87e46c9b62075ddf8cec9e6b7f8b9dfce6c1c4703311c9af072a25057ac1090a0aa8540a0ec50d059c49826eab36da703699c99c468e576eb6aabb2b04432e1b9c8019a9fa1ee5a83fffaa48d9f766151f00d1c24f176f4efd737609a8fecd2e0113e4b35885169f60919345c296c8175cc69ae25d13a83c950fa8f9eb811d2c0c501b523446bba3fa6865b30b3bdb34ca78d09a5676a5bffc3fad7ae0a8cdcf70a86126308f45026c4e7e41117a05f90be00f65301af2fb6cb79a01df335580101b9d260c8c705d5623593203fe3e631e757c7b4b27d65b368e22965d7bbde0d7d2cf6aa232bf8166065b1c69f3718c75b8288bb1bb7c7d3792fe73e131c812af09051fd8ccfe65eaf3b51dde8d2b1fca063f929c1bc35c71069787173200954a22a862cbc59bfcf0145b9a4fdef7de92c1350d679c686e26e18a8130a7ea70533df4be7ea5c05aec1d48b0351860de6411802a63c8a7daa60e794b985159df7927911d4e0a804bdb9d0a09efd9feeda8c89f56ae69b933e2de9c584c01b17aa11deea54ec7fe392a984129b6bd53742b21a18603d3e1ab53b06f361ea8b18b5475e248b4b5b73576f6f31245c724c4dcc44e6bc97ff9bc1eaedb4a55d2f575c83ab880c2fadd2b81e177a6dd9950748d33c15b4cd7a61d93041c28a9daae76a526461cfe6b38014850f2cd4076855a2699a533f95ab4d45ad823366fc5e307b1c61b4f39e67c867a8407839740132439a207e22fcda7f1ef7fe39370e6e4811159bf5f449d4c091d5553456546bb1eb71299bf68b3d461a3b7d795ff105b57acd372f58e63b655e12bce3fc9aba76e4f640e4e54efedfe82a14172582000f84d960090653d8e870f210bbaa504e26a4da45a3325a96704a0838062d67163c43d457721f50c69049e9b9e57aa4bbe85fc05b2dc7b179a8336bdba187b690e6bd773bc699b277a889b446ca30885aecb1e7d77959241cf5ffbebc0410f18f7dcb2615f6476b35c135c8329bc14fd4346da46ec1db7863c34b36bc2b40a231f55e11938719a8b8958d8437b5382832fb82049df40c97a9711bf8af6f3bb13503ad9d212a32d015fb2770034e6512b3a7ba694bd69fcc3541a9ea97ab8f1f7af1ec37bc4277a556463a44af49e066ad990a6281d33a85cf9ca0109356b676bd2d6f27c21803478fbc225904f31588a7c6b3d832f43ef85e5c79500adaa3c990f891a5ba57cb09c22414ce3d0dd3de07b1fc59a4cf15d7aa0b317a64f6fd5251081eed9de73472c029cae4171d46c7d89427f6d68153608de66f469871e962d2dc04d0a5ea0126d96519752a30338b9ac5e2af8b1cfb4a0b1faa0c00db8533bd1511cd4600b36d2882ef7ce8f8b92df5e867c5c82184f68325d8aa205d8471ae70eb7b431c76a46ebda7f366037976bf6d35c41025ab9a1a5d89e36c08a1264b4bff6e43dcf0642665eac7d4fda535218e81fca5b98b93e42264ff9477d9af9f7083f42812eb0fa4cbb83d48053d838f79d68cead5959468365367658fc41fca39610b9e3521c10030a5be0f5e5ae113eb9f4215a7b209a7383a643dc29c256537cf15ae85b20b27f778410c5fe8140d815fb37b036b870da19d4949733fa62c7b189e90bbfe4d2433ec1185350ab07973bc73766c1d0cbc200c94222d0299837b1455649188d2b17ad5628fdebdaec7f44e9f2e19d55e8a73ae34a22ff54929966c23f47efb802c197defb6e12470d2156673c4d1b2eab3c346c91af4668c041a97aa76bbd626b339031fad532f90b5b75287193085cf35eb67acde05b701e4e86e6649dfeee71953cebb7c3b480530c15778c428f66b83266977e1e2f8834d44f039d1eccb23fc9f5e7795948ade354666568d690f5bdbea25a7972a41f6d907c47782a1b643a75bde5e938b02f42b85f8a81d0e9992f6ab87cd3258bd756a87e83842b04183b67cf18aa72f3391bf882ce6a2bfd707d46ae49c1a779cd8c97e67142f118f64d52b8a5cf78085f21debf536f1842d5757f474adedb1a0ec378e885dd731faeb4ca386d37f7658a83935fb9bb0d60bc4d93da660f4ab64d23270e918ec6ff0a8153d419ded3d975ed6e15cb158d8b87eef75965e715509b00e64f2762b8ee0309562c3426a589a1b8010279804b35b47941a364692f7629463da04b78bdf1f772751f0b83ca0e528d9be58680de708a495b3df08bc3c6cf4218ce32d3b0c940da8a13c81c9c91200b682c0b1624a3daf0a876cf4ea1f143f30ac107924cb9fb8133485e5a41501be6a464d53d293c6bb9109a2e05a9b444772f7b2e3dc03eaea2dce065cdcf410301e2fba9f92affeca3399fd30e8d97fff8ecdc862217083ed772cba7cdeef538a37158a2dc5068c8ca3f2d6540bcba35d728792fbd4d437870d9f773ab5db4ec62c945f5671e398621c65ffad2c649ccacf75b6878d4c1da30c63142da10df78f3d4aed4959dc0ffb72d875b6494a556a1a4a3af84653fb1d7e38271aaf7cc25edc912f0083cc1b96deab4ccfaa4d67a1aeb9f7d59a381b76dad88606059b372cdccf4083000e54babeb7cc3604a62532d1e007f78d7d0a9c1fc59637721f40dadc45f781c4f8cfd54be5a5b123624bd1ec92af5fd4bda28c4413a076b74a581163fb5e5efaea9a048339e78b4bba46d29e5d9e5285c7f00fbf4a7ff3bbeea16da2a56b8e3e4ec9a831e3f5845fae7865718cabaa575aaad9b956289e93e0949deb541425c2a4a5ba6eb9036c2a5cee0e58b91038152d0bfd846e3d158a81b12e087ddd1ebc2211ef0ae5709d8e3d74d03008346e7e65675411dedfce9e3edff6d791a12c5fb3b86058f0dd5417f14a0c535f898394d947a1ac739705c2cee8c8ffd370c6539751d47a735be9da772ff9daba0924a961215ea8df42e11e26c9f6e69bf63dfa5cd5b5eda25b2affaaee33dfe4120e3aa324d92b9c0967039e15f93775c1243faf62dfdb596589aec4da503f217a48702b8cb8a1de908f05d25201619693f9361feef7b11565bff83dbbd634849181e740d2366ac83944dea097915ec8b5bf6993a9ff102d8efb73bb1b7e63e803737bb5ef7f25d72cfd2a4b0c063d4e21c153b3e2fd22f7d0d9c397479ffd2436dd6b4ab6e87e66e51a7b16076dcf4b436747dcdd04279f6a9c7c30e6b350fffecead0ea55a9c6afe5cd0b32212d1c258416c9108dfc34ef7222a44189720a805850acebf245bff62c0f1a4ce166c6a80e6653796bf9eccd094d0e90520e47c8391a95278c1f49529e522031d72d1a4e51ef4efb0ae8fa36481cfe348af34861a3bd804bbf0f843ab8306fb5bfce12ee69d490692bfab13150f5d05d4f646847b562d9ca70cf8b213794cbe249f0fcc6b06530a4497a8196acfdf1aab0267268c4906746a8253cea74091f24b954b40ff1b318858a23b07e9e1cad5cd3046b25fb7c3ebda20f0069d06b431d22c2c8eb993b766768f3951dc9c64e4885f36236b801ba354eabac19568472826139bfb9d71745d7a9c915f3db6afefcb4b6c5834e336600afef810b91ab9384e1d1535d4a14c61395e5dc9f13912f61136858c50d2b50c07956a3f3a33fecce55c3639fc89ecd78c36acfec62a7cb6301895af10511c8a4ba169c92c5cbc9ca9f698d760855bd22a2b81557d566aa34b34c142bfb93040706afdf1b180312b047f85a620a31af11e3db1a3b0efd381802015fc53c23043d8839fa8bcaa7d8ccec727a405d7de5c9d6875c1b02672d39ce1155ef8948ffaf585a031e03d5604e9918d28d47fc3fd1b54c3beb44bc81e135a40f431eb1853dda46db02e8b915403856cf52d24393cd48b8503a684ad7e45b4b67882d0930bd835a2afff4b1072a9c245e9c3d6bdc71f8d05aa73a96bbd6b827e3267e834c220f1d6f9f8b7041f6ea0d11992d17fa3e4e9d0703853bd8781746e112b40ac5798a13e59fb169bef51adc60c9fa9f97daa2ffebf1c2eeabf8800412235351755c8850fc4182b8483b25a9b6575acb0c5f845bda36fff71422ff45559de789efa2af3350de3b5f4004faa40041b76454d032f5928fc899902516f9c2ab548f0c4cc50a32bd039d14f6378298af363507a54a779d22a512407232980686e3f1aa44c4dd252e9351a1336f682664990fdcf2489a2253f9245763af3c8adfa7acb4fa39e20fc17f134a2dd97b5bb7c7b32137b14dc6f1b705495a0aa33d2c85458fc633df4ed34284e09b4254962e4dd0c3cb25509ced0e848f4e718b69771ceec90652826ff72312543c7dd0e77a689d71cc331ef216aebc7d79c485474cfd577e8e51af657fd864489ecc7491212d7a797a27d63c21da34cff4bceb3ddf6255ad1cad9e554d57ba1363aeaddf74b6444c1d5d048eb13bc717cc28ef859673627a32a5c3699a92c167f73f4752cf25fba8e0b865bddb6127f1b6a3a9794ad3e507195009fe5d140a0f7e5f8b22786f63eb15c520da9d7f902e330c431ff9abae01f2f6edea6519d120898dab828c17f186c4a9a41c67f575f40bd4071834d1d3f8f76e4168bcc441cbcca1d719e8dbc5ac3c401db04854af0fc297beb4a103d001f89c181ab31970a66314fb281663c8d9d4052f47c0e52c40cd8003c42c715435fb054bc01696a4fd256cfcaa4c26b827f83c1f4245fb98a93fc962ce6d3aedbaa937913e4dd090bb3a1c56177faf3ad563b7b6f01289a45a1b4625b65997cd4139e4d7a00a8e2916c238c51e745aa25810fb05f381e1f9255c036d397df317551b519c03206eb0d39c0b1f9af095005c061c040b81efa3c31e133e0c766524ef7dfd048c7b717144d0f68a08ad3db595cc0ce55a7a4d9ac0931739022af3e95fedc21bbbae438d64e33b2ec7ce0f1aac2d0ef0ed2e3b30c1167a18dd46d35220271e92718858e76dd9e5211267f757302e41ed3f6963badfce790f4a623a728639a322114fa7206c44ba7e2f0360c06a0d60069af0a1cbe18461e1dd7ded7086a108d1e0120d12365c2ba0ed3fa84b03d87368edaa9cf78ab0d1d09c2081215d97804320c603cd414d88e94f60284373c18681be1242f2f9ec05509275d531547ec66a3cce91a51ff6f7509b4fa9c3a38cbdf6033e732d26afbd6384cc751576fe248cdb7060be8f015bbe668461a9a80f1f1f9fc34a120641fd57c4e791a7ba8b4c1cf45ab2bb29a6e8c71cbcdeaa0b37471ab744d295f98b6df455e95b5ea03b8229611e2f58c4ebccb4b3b03422a81a65cc054b63638356fb5a4ebd3a9132b2179c51bce5fe56250f6907342993a4451634f1b1f0c932d64048900be9d5ff3d9e622da90f363ba0d9ac78ae0c6cfb86c171c0bc4b18e08d766e998b8f9f32a0b319ac1b0d9099bb8056044b1d9f460f7b1d5600034784eae6ec59d72b1af07f55b72ade2d32c008502f9c1171c09d2940869819bed4d9d89b959425d18294ce36a589d1e4a3312d79a40d4cee04df1826ca365af3d37b6272237ed527823086c2c2219a65a7d9da649d048bf5998524305ef330251598aa9085e001905846f550e750ac8f3f01cb7b7536a807ed32a5d69fce7292e53c589d2aa9ceb6c3ab43176aae88707b7bbab095d3b125ccd0502e982d282cc149db147ad269aebb39cea587e9e1ee2b40e609993720d18e7be95c0edb421c247ae1257c3cd2f7586f08a504dd5fdc35e30d4b16a2758d7c9775ddc88907d51ef7db71175bcffbe39298c1ed5fe96049d602c426d6b281785a7ac9a0f4173ccbff82c43a43ece680667e34ca3ad6423a7bc0f553414fa6ba756a7d3e629975d8c62db2787497b97a753be0bf62c86741d2fe4d22613d59adcd9e78ef76aba175eee65e1447dcffb4688acc3c03effd1669c3295a9318cb0082477576b92dc0fcd895948a572dd6f15dff14a371074eeba2f1587ce61e26ae91df652647af6178758c0b335d59ac35aaf5ee4bd0b524a69048b8d71c3f24f568faa3bd82b2572ac078d32bf1d34acc058494fe58e05355ab3af1ae391f9980d812c11c63076c976ab746f1c0056266ebab88f7061218675b51dabbbf99dfa501a226ecb4a0e966071000187af12a21121ed9eddf61ebfd4230c9383be091a83e80d6df6b832cfc1e226c26a0f9204a929556b0846fe46edd0be4e43be1ff32b5a50a728e65b40e4af5111006b1e10b75cd34827efce8ec41866c1d36e74799d97e2bf14821b09135badb3637ce12057054fdee72871697b08d95cd53908df20f3c78884c08f1ccc6f9c8dce241658ace725d3c4c109b51f6b983acd43a71d076ed6da9e8cdd24054ab93a494231e20f72360cbfe5d0be03e2847022a072691962a01e3092bb0ceba9e6f92e0f7f839c59ba20c96f0fa1ce940ab3a2f09e128cb8be34c5b454374ea80e82c868cc2e0fbd3e7595abc34e14e0badb1d813b4764172bf15578b34bc47ff802e82f792c89c3c9ac43405286fc1b18bce704c8360957c63741c2ad887ea248a9c780bd91d962bfd54e0e55dc8b7e576edc30dd185eb50a159786aea939ff92ddbd7774dc0339e600393ecf934d55b7b543faf15c8d0c72c4963b5b0b1d1ffb0d8362726b279786b9b12947b38261b5a2630d7b691303bc4cc39bc329ddfcee7117e2895d7fba44c8e7885d4ca77f3d207d331494961860dab9495f3682d64715ca56fdf97e94cb0bfb4540df4b65dc8ac2119823d4bfaaf24aa68bdbfe0f34dc96b562600d17f8cac4064edf63d72cf7e0418a4e2c8f24b70d13a2c8b06ce47ec7c32f0a23e8c3d4685ab8e37bc7b364a4da81009561254ce6039621897dbe47dfa1245826409077e9d3698edabe7bb64f92504523dc5ab4e589584301e489e441d1344493a361f19f99edd2460c6613db744505041a2a6eed3153c5fee36f2fd6f69ff3f21408cffe537cda8f7a987ad6e83f7f2074c30a7223e1109dfe0d861abf7ce2da0ecd3b99c5a114c76d56af92ba063cda422a13cbce5e0bb7fef3f271bfa54c090dec3bb64fd8336fc81633500b2ba5b9003490a799af3b943d202bff6390b02dd9c78817287abc5c200bec5aa45b8c69b244210848ff8e44ce004313dca826b9982e066f8ec62743da0b4070a02183812ec1d913b09b5bcb26f39845781013008c28e6bd19890a61d9a59e00c11be3be0f904fbc40f4a47f7abd981cb7f7b06f954822820b906882066b6d422bc6fd82b2bf4fcbd583952a1b2184f277e29a0f7b3db617f6f4e21544b66f5ab5bdf52586469af5d6754debeca56e9ebc203755e0a66f16d5b72fa076d3052340c7f81dadf775d0aded17946bb1f351ab9faf4bdba7b2d2a578e469289514367342b47b4ca9cad87c1102da38ed143fb76216f687c5943fb8433342fc6d0ebe60bad14f0ac2dd429f9b8956274ce722bbace8d5def39d6bd2ea6a435e70114deaa767631ce043d07c5b3a6d7b18b216a4b27b4890b7880605ea1b0ae0274e2c49102ddddf8b0b17dfddbc7b94d01f2c750b6d182bc0cf7af885530cc8359d75c14d5f5a26f928f38c33d267e92f971993465a9e1295b4e8041f2de8502915c39c0ab0505aae7f29c1ace0c33fc7589f332b8af63739388b265effeb9fc9e923444f30fa7b61c3d6db19a52cc1c8e91adf89139df744007a87ffb7080668f5d68c644053db8da53e7290c5df6c55dab35b191d3d65095591a0c120acea90a59242155bfd25040ddf3573084a7dfd7ae0781e3184b4d86ff7e9e74405bcb49196464536d5a6105b14f66a9177523d8cca12aaf729f2a9ebae1a1a27698928a0d3228157edf5cd74256ad7080c272c44136c95c0fb47bed9bd30161aad04062e0690c89612f78d8d754a08b708f172d3da05ab4cfe8e6c2a991a636b140917dab58ce36b677576638012d22bfe196ac71fda4a00934665d18fc29a6effd13993147e5257dc242f0611d3d30517b91470a02a08293ffdb14676387aff2dfe3c224b7f2dc31717ff4ca26defc5bd428d2d8d66179b0d5304e6077c8c42900c56c1605e987f2e981613a960cc43c4652ecd74781d4e18e6aca36b8927f778ecae2fd94473551518b1f68d268e3aead2d7a9c2dda702050083db912d7428180bf5960b9d65972bcfaba093cde11354319722b8fc636bdddd79e145d08c264c44014c6339317387144187cef46d64d7dd75742cfc280e166b329dec2b5fbe1f3092ac5057bcf7654c08f6464521407ffdfe26d8f3384aaecfbd66056c5928bd9ff5139a576294905b34cb2eff4b4dfc64b5b2d4642c14e437cdfc7a32fcda52426474a9f1302a5638dedb153d5b92e2146c950410ed9775c2b7851fb5c4c374d9bc10c9c96e61c6be07cee0b0cbac194ad3f8cae03715a8c0a3ed11032c63839c02993611f05c252283e1e765f8e89b6f5a2d28ba4d26bd8f196662df4887868831be7a89d1491bccc6d9c5d0d8f94468f088a3da6c4dfb69d726bf3018ad56aa9aa02dae1839508764bcb415c061b70438436718dc5a1f732e3277e045b0cb7b6655560103f2b0056b948d64ccedbb896800c42c424dae98fa7764f412164ed351344d96acfeaac628053b31a6d17594d5689fb3c398caada6e036dc4f6d36ec8c38f6f3ba0d8369a11bb1f3924ab574b83b07d1446c6d9ef309c0b16e542066db6e38eb288465a169cf89a9735de5e26f2fe62fa78214104e2e3bd2d904aaac37e2aa1483bff0d31df7a59af6e5b89679df65d795abb0ee2ee4ad9559cbb921067f1ffced02e4ac629f056d301b2bd6ce3076ec3d9ff237077b7d595dbcbe13eb46515001358ccf1ee34368372f4301a86c2546b9adb1c2342349a725b76444502a19d3acc5873d75613ad0cde5ab4ebb04dd751e6d9e122e2852ee991d87fba03dcd0e80d6b16a31608e3d4ac9106b50617a8dd14fa65f2702443dd33f470c1987c5f87f53f771ec156455173bf7ada7620b114e79ed49e76d7faf2132199ed1275d960323470bbbfcbdc47f25510880fd7b2aebbf1fdbbe4a5660413f31c3c1b4066fec843d2e91819802822b0b5860b91782402ced7edf57ce52208efccde9c8cf058611995df19c65c39eb07e9527a731103073c959f43919ee4d8ecd4c46acee30e16b4c461e21f3a25775519e315fc487b40da4c2ae8064476aa4c003991dafa41021790882a3d0b4537c8c8c4bdacae02e9feb5183af373984b7dd69efc43731fd83853651eac27b22124b7619848980a094bd133e87f160f79becc0c635c7d6156c3b66833eeda31c65a8f5e851a5c7f829991ddf7865fbc882af0d62e026a6f0b3a16068d52fe6510529759370a4869e358f31431c5b7686d5656d9c1480d8e7e1f658334d39c0bc3c8a07da9a848a21abcdfc63360866e7600b09eb7d265237503aa751b449c0e236216568c692c51a2426a0826c5ba3a7bcae47b50a167cbf9e862ce9f97e9187d1e110ee7897975bd89d2cedee6b599e023e6a79c9632d5a8a8104aceda4da19012f59c1c46a10f2d1faaa1a2c641f551f50e619a389e264342d0d13694b37398e8a0b1fc0c0b8520c8c236422f353309f6d9eaa062f56443116a75f535a4c9fd3d392bbcb5382d611da0d439ff91121ee195b3dc15348eb424c5f66e7a7b5a9ed11341ab4268302ee99ab7bec930cff605137266c28e76c88f1f0aaaa3ac54e3d5b43bcf83f8bea5cc9573ecf1b08a671cc3c107ca3032b9828f0f1346511648f0baf1d09e3c705fce045aff0ba367d9f76a13ad651cb1a969c13e51c0b3606e4b62c9273df2c3b8427c21a58740d53224467d85c4964786b0ee221ed37f56263c5ae38c04e5a53ccab2c4acb9a7977af2e15c5e669ad857f49ef6710accfbc26b190bf0f54ceb349a040d46cb83e557cf30dceb99a2d5cc08a13139457288d0a1d911e1d80c7ec0e49f574e3e20549511d838e83b60cb8caaf7ca2e2507e4f7552de9c8eebee6bf0e3e2c5e562c11ac07f3fbb8e0b18798abb9f49dcd528c1620b2558182f049c0e823e12f772009388132c7d652d553c1cf880468f8586cbabead96a06011dab146a62b9ff5b4544ea8c2d4f20304be6a383e8255c3d55bedd8c563651d4ecaa4c5f7e1bbf4af8c067c46d4146e9f2b56dd19e8256895d443e6b231b1613f3c4158ca81e49edd327c248b18162ee177768914abc912dc10bfa06b6a5074aa1c83e31d0a4f5c950b5495dced31c2f97d5bf1c2f3f00725cdb596198d3f1375b0e3b00cd080038263db9c0b2435d356e0434d93feb56eaf5e0b1ea374f1e22817ca5a3cb2b7bf0480009b87510565794b41394101e1683d910d1b47f901f9607b1f2779ac7582a50bfaa21bf60e8838d08f6923fc2ae15ec2eb8f9d6518bd08e40996298736b238b0fd7cbbe92c8af04350f56f0e8716c34e91f0d3ee19b109e821e746447875c40a4d2fb2ec890e00c487a5a47c35b3c9481919c2993d38616590f13288e890248204024ae86564387bda820dbc7d0be2e1d5df29f05623b052a7d1e229584852b6e6b7fac420f027c5edf6774e28e157b2b743fde1a69a3795c3d023dac37d25923a6d18e061031ab30133158b0d3d2cdc7c6e7152cb52f450c1eef67608e39809ac4e39f7e991492c59226daf5d9a0636a09843c3b4f4fd9e31f65106679dab1f947196a0b75c693cc54e7fd504a27c993980e941291727e64556f6702f2c64bedb8b51e5574a6d518f455da24d6adbda0eaf65d6710eb710037c40e69931f7787b8ed257daaadaa0da279a7edcbf232969c20a85733a28a19e73ca2f89309f514c6a26464249bc173be3051094632766593c5eba89126ddcf88842f5003e9837b84116720910eb72c63fc0cc8d008d4eed34d37d8da8aa900eaa7b09bf9dfc19012880bdbe1d1951e8fb66aba81bcdc2e462c10355125af42ffafffcc5440a4f40b289894803886fdf49eea25b77f95631d67da93096ef0ae72e0987e3ed04941c4971b2bf2e470774ad8e18df4ff8262bdc662efc64a8dbc79747f8eb3ce0d4f985c957a341b776033540f90e902c1716c0ce84b3bdfe658c57695989a341c99d538953b93a03bdd2c2ae22e042cdf11800d4c4b11d1ac81b4e1090693e9fcd0dc44e83b39f90350d2549647d5714788b6f728c764773c3f0041214d01f335038f5219131b303d5642d38028e1ad9930daee4929c954da95f9a318ea87943c38b751c4c02d5e487a79bd07f19ca9586240bbc616d103bd64fbef9585da778f96a74a245d96b20780e7018bdd81fcdc8e4855d83d3f7b334c7ba88c52bd3db28b4e53e56e2615de440d7c34e8dfca153438371cb9e3512abb1def04e9644aeebc90d1b903590e8839f18e66fb2063c9ff7e7b8874421c9dcd22d12c2fd44e89ebf9bd2ff885c645f46156167ab1a6b9b1df0e3471706711e9e06d62bb091957f61d50f59fef35033f5befa09f74c87686e8b2210314cb74b06623d32fe34f935378e8bc4cb0d061263cef69e6e1b0adcb1ccf67d20ba4619a342dd1ae8e2f8426cd4ad23f53d27f4e128a7f6ea96074e82a823a54761ef9761079538cffdbb4598ef0ae380e1cad0a344ce05d44ef7063b1e33c54d912b3007c72431157fec3bf6684a9776d182c6c6279cdcc3f194704812309c60a6652e39acec1890ea7e5360b84fe13143dd1b1d8c0d46a37145b77b021ee6f4034fa10dcfcda937a418e4e40c6efc77a4fb41e02101f13aa3751f0f5a0bfaec91e193f85ee6ab5b04dd6964a8453cc9ee16c375947c0a8bc3ebc2f51c7e61d050af1973f748abcd5d9459b9f3835a0047265c245d232061b8ec7ccf35f3399c6b49b44b77f64b29f8ea26ddcdb70f615f0a457b7f8414997ba37ecd045976b811ce56fa647f1b1ace40f53187b4272c00c35b36fec0e180a4079d4a7e41ffa6ac190178936c8527e3a6142ec242ce829ed2fec8ce0e9acb958dabdd75ba9424b9b86e90e6363489d9f80776546ebbfef7d059a9c3c87acbe82c81ce9c9ed46a7e9142ce1f69edc5b0a2752602d5e9520463f8e0b434a41098c1e5ac463fafe52d3840e7ebede48a91d2a3bcdae71df9328619006d662c8b7b707f12da520cdc3b559d3c85ac400cb4b15fe9e346a9307dd160b2a47827d6f210fd12bdcca2d4aee821bc1a38b0189be7afb3ebcdd1717a895f28b9faa9260826e60d2afe876e7cdbddf067f589c27ff9ed34d17f7f23a351e9f8a6bef273657fd22988955d301a37e555a2df38c831c2bad254153437c4ce0517d447227ae44e3d1c74a87756acf6249d156ebbf3c2bd3dce821e1659b21cbe82018aa138ccbb8a86c9e333fe423408743d434eb58186669a6e1504175b51eb830eb7fef7dbc145aaff16e02065d3ffd86aa083d17f616420345d23535fb03e9fe3b120a6e85f090de1274812cd303d5cd6c6abd5bf062d64db45d922115063884a68f807391d793eb3f05261e557358d0581b0c0edd4b3fd66e727eb87c43f246e75243e184e899bc042a818a0fcffe04f9e49ec6ca015d56ed5a4ee69f1f39e4d1cb87bf93fd9984f0d251812f923aa4fd8149ca01b6ffc60019f526bf1bee1c5e85e289e006b4bb039296a17e6df892f5ed44c9a6df13bb43165670b2f9f7c382fc56561e7de759e9865d3a1f7e7b401d6abd953778b85092bd01a679405efc1efb3eccfcc44c846651dfa1aaf9855c7d2cdff1bc22565f06d420c41b2ed0c45c6ba0b7a5d668867860f2a5c2cf06600b23a6bfa720ab2217ddc68602a903511cb5906754a7d7628ba1a32bacc74da9ebda78547f12f8690f1da9c66a4663cc7c44df6a5c0a88e6ea8ab9d9cf2400713f6be0626c4d2836d059399183386c6c48142fbda4bd152f044a21f8fec017cb618c8f93c7ab937937841722cd63399cd2983dd2946fc5fe9ec197253b1f37504359429e18d228b5db0f96d52cef9d7ce2df17c690f9eadcfbc7e45d35a2dcee7e9761f8ce3a290b9474d36efa772bf530c73fc95aa8c17e3d0b352421c922ed224a2a6920e75d542c533ac2d6c1ee671a6e0dd0577130ea5e3e7dbbbc1db32f10146b8ee08d96e26fb6ef91c551da6ec965c55ffc24f66aada2a6cfc3d62988258bdf5e89cc1204149eb1f098214aa6c2e9a44361c59efed0eb7ae6845297464e48bc43483d454325b71a864fac21a21badd9eba5c02eaa479690c1056cc9c2bfabe7ee04d56339828b2c91ff8f10e891f38cc40dd5aa8d9ccb53bdd5afe5f4201742802da600f8d39c95ce0f7ce8ce4ff2f02e4145a99432797168357671e013314a890b82de60b3d112b9d1fa9c39b6da7bf2e701b15b256aca9f41493e7ef4145e7f894a460a1d612487fb6a03fd3c248d8280e335577509db089926dc349c6812953aa8ee4da17ba88782e02e4cc9a2f963a276d43c33e21ab9aa02f95032dc4fbe3f1469d1854f3ee15de5f1d2ebcb2f9c234a20cd809767b7200d02906f5cde78ff5215b18e18029a86cabf50a990dce76832be5fc539a46050b8eb428be66bf99756f215dbfca95f8a5de9717a7689819d8c0644fc47dda24c29e9fd565498d438cd9e5a7d857164563dc406739221acb77a2d9f07e7455d0a1b798db70df7f585688c742f6e97c9ec5d09d23fd9d722e726fbe7a2761ae7edfecdfd783f7399ae647ab98c1094bcff16d93809e1c91dd600d005739c8633c7bafbca469b13061156ad0771a09a3ff3ddc45205026532b10515351e0dbae52c83def031bdfce1a140b3da25e844e827fbfa48b1dfe3f4da5f2bc827e924227a9383341ddf3b7eda5b876a627465242602e760b4c20eac41727a920d5462b98e2a961cc318cad5aa51981a686b19eb7d5364cbd35e8194401a385c0266b5c0a216c32a6d5f902a73cfc3154306b71447e59cdef66115d2eb29aa5c85d6793dedeea401ed75660c501251b2364da40bd6e648b475f7a8100f745d056c1684879b965ec8e18c96bf7a0b2277dda53a9053c7401255d9a01dacf69a494a2adbcf775083680dda40d279ef3aafa34600f90758634d88366173532e1b7198e77b95ebaa2efb2f536805fca5615c69d05b0e558844b266ab8cb6d8c57f1904096655a4b33a8b6018063725bcb5e236b6b0588203b18a517c0c97a80c36550776d9faf1074cc838dce8485d1f656a062a362f23ed65eb25c0d31e1087d1781173e8d0b6c8888fc660b780ff106d6a498ea11e0c7ba2cd307dcaab2b44d0c60935bc4c50062379d815449eaf4488131c87264378ebb3e47646a89ee96b2ea2bbe592b6db74759205a76c2c68348a0707b83a6000d069876471ec003a5befb6b30c17db3349e2679c772c71cbe2f55f625bc8ef6dae852add7a792574a42018466626cebb33cfb3bb6caaef26fb48f0df9d0dcadca602b7c2994677c2d60609f2cd734d0f9bb9648324d09d8e25cd400f7de323862ddca15e6f7c45e983fbc3ab4e53aa1663de5400ca1f2cf25e8bbad01ea96f1adbdbd63444f1b121de7a8affa3a9b9b4311dc71378c798e463c61d12096b6a37fb7555be1d1946c48ce5bbe6dfc5612382b7bfad2ecf40d79966197764ebeae836800f8801609ec1ad12274a83eb67b4ffa982c81fcf23f8e4bb90d02ac2285367436912cdc219540093013dfe13b7cc7ef46587baf50e93f027b4e9d25e4374d523f14df64fdb53b3a09d66b957220924dc35c608d05f62a99cf89e700a22c24c76dcdd1ce5c3654a0aace27bcd4603e8846ef65a5338a68e10bda87de0c43fa9d30c436fd29c0bfc0e3fe6227b3b45034fdab38abee8775a5a1eedcb4fcedbff80b00d5f0a08280b498c9795bea3c068bf685ab857839f202da50cc97d3f641a51b06906b7da088682169d5f1812ad4b9ffcf2d2739d4d3a3b55a9d010859da4565ce9a5bd2386862263b6621d08905b460911965d09ddaf909385c87aa9b9bd34ad692352c6ca89edc4a13f43cfca9c520cb3a27b440aef04ea5241bf843c0c1ae952baf6037ac9a506d52788bff134b2ea2d6789929286a861d2c77c61344a3b84dbabc71d5dec8878710de8b1ac2af687110a013d03e26511cd4391390f6ab7f63e5f878884642c7bcfb2cc4f26d471044b238dd7f2cac980ac2b4c8b4176f6cc5c7ff910dc677ffd7459213e57a3c72f8ef2e9301eb8e37aed3af115cd16279bdfc9ae09ff8cf00091b456513cadc76e496adaa8577b167b5ab0d4f82d1b16a61c0108e90572afb1a95034391d96f96d3385e4626163ab63399de73703f26d9acae8ed54bd676a1c0279c4c57032134895db348405b9066a77d283ff0c2989e2db11b93102d38df39037de937c321cab407a72392137b3e04f9eef67f75fad531537bdfbdf5287dcb439ef5d917b16e5c94eb1b2e4196308d7aead8ad47dab231ae22240e11467cb47c6ea8df4be4f5fbfc70909178af74ea13ac6afccbd9e9481a1f8469a922b3c293beb39eb35ca7789f3c555b9cad872478a44945632fdabe18bb4149552143dba7a99fe4848b86ad6e1b0e891aeef6a9789f5fdee0f969027b80d0961555ee1955e2759e27e60dec71d4c48e9ed093a54a403caa529eabea543d59fef32b912687e7ecc88a78f22cec82cd85aaa99089b97adf350ad9baa5cf68577c4e5c68a36ebc40a0e5aef4b84dbf74138e3b376313046a06108f00622c1ba23f60099e0dac26150358ecfe19f7488a83e87ade027170bffcdaa24e72106c237c460e5f5d4127851a78696e36a660b79e879238a0ae416d6072297813b6e9f000370bf7a0cd35851028e8dbcc56f7da9ae80ee962387fc9bd18ecb7e3c883a55ecd7301c84bcd78799e3f4bcb0375f11be5b95c0b156159a1114302c46b1bffb00e45c0c6539c0e99cdf3cc3a5923b9a0df5db6e33fb912c6b20a0430d2ae7a53e8de4d037f8934c7385121eca6a93cf7bd23cc23b1f3465088029641faa6149560047ae64ba13971a796ea8ffd4002d1430ad4c1eec2f3cc9163f90877983fc0903bc221fe83d8cc38689f4eae760d3478576b7bcfb547f1ced853761e39f11bd11e9add0381a3bd6a3ea33053aa546d5f97ac0e7b2c32f478757555e6e624c0f70fce68e2dad77535bcae23fc3f762a90cf5700e56d1d635a89ad05de026f8c207b03c981fc63fda06e152085350e022cb8c42e868ca493204e020b8c724aef8ceb38bbcc96660cde181cf61861c5eaa2c143b0bcf7245f5568caad4d36a33674f19f06134d178f60cd3f09e5ac4bfb9056f88da9d5abc106552899c2763488fc1adb63ec8eb90359951d6c7912ed269b8113a1642e44904c01914eab8dc14cc04570822a84f29121418a26f5ab1306c9690706a8a75314f66ce9493022e770c11d21bf41b0f8e8b3902a2f71d8fc6438f9517af41316db982015c7d1e219a507d1e5011c17929b490239883d5c6e514a3ef1e58ed23a7fa2bdc3914568669307cfb47c3012e8019e16a6b4820bf8bd8b56a8eb8b500914540eb6054f68e2b52d1eaf1e71b7158d3c62810ad72b1183d202f8ec82e587ed981f9c2386d33a3a665bbac49f8962da749df751965add5bb28e25d5915fad5ff22402cd07d73bc5ee108bdbe05236721baa8337b7617cd4858f616490fa0db16b8c6ca1c9b9ebecb186b07b067478d521779e2d2f26491eaf9a33d9eb9e4f888ae3189c2cd3c11744b36ff91e613fe1ceff5dd10b421b8ccdf4bb079811ad26715bbaa0b7693194c6ca611c4c464ba3f58220ac9eaa8f8f2802a20cbcb415884b212d685dbdfba7d1745bc8263681752bf1319e4866b1dc6d9f59a3191eb89114dcbb81c7c7e789d6945dacb136e58d6cbc32eec9ce59dad6e78f2d3d4a160d165eae2bb3257c9d7646c0cc1cd30c50f2e1c4d41e20129e5dd93cfc3c536006e86ede04eaa6db2e24cb5bfc24634c8cd9b9dbd3a06f9d8ff9f08b2f428bd89b7b0d8080582de713103d110246c86e84ffa922b27ed9ead21b95087d45caea68f257fa982f10fe63c693772277ec09478803b9efe77a64865b448cd26e9427a5d4457b4537792ff2c6e07254fa4ae0b661db7f073b0097f4af43183c5964a1dfd7a76d26418484acab77da1377f56a53dd8c96a7ee82166070030721dc9eb7000ff47cdfda023c88051e196082c6314706a344314e59fa9ed5cf3661ba9f689c2ceb3738962b4d529e8d5e20ba148f97126055bdf6ce3f717db1d3d789f33588cabb349057833af6f91ccd85ae25a8f6c63430524a4c385a78124ec721d2474fe0097f1eb42821f3c9037b11115f3ca07c9f1f860e13e4ba5f0aac9a21cc0c6661580728eb44b2bc0357906ed5e3a1ad564831cf436be1283c8007182bf8b418abe743767a34fb782194be5de3ea4de736ad990f4fa5991e50efe181c480d08d7de955807d9025889888fd869e57f150bd9b5142ce518cdc410bb6c62acec3c35baf7472de195ca475be0873784326dc63fbbe7b8398230f9e107d189449b4aadefd3577010125ecc21b6ad78702ef84c2662d452c476fadccd6fb44dd399fa81ac844578376063aa366ed9497f000954c0deebe0641197709192a042ff698b243153af854b17a22fd500a82bb201488917e41b71bca16c9682af7607acce1a75a72a68c97caf40becd42fff2611a2778a95a3200c5fa39478fcf364d17334dca63d5ae760d81dcfa0f5c999f0e2484be1558b2703a32c629a386009e251a95af4cdc3a6eb02d35c2dd03668a85e8a5797025220c9bcf3c031efc2d7e4ee9ca03da7c1a5a6b3079243686c612245595f5ead531c3c3516aa8d6061c709c30aadbde023432de4ec2b76b313ba934b394781142e2bb915aeda9fc1c0dc1c8f3223446fb89817e60c0c267776e7301f4a0a0fa51e03ee391d50c54e2e43cd0efa82e17a960b19944d61b376c706fbe4b50b92358eee99cb6a82b0641567930810aa4842ef7a7e438f5e3ba294778f67198f42419fbe8c26151372f25ab00f85b670f080f5919d3b503f21484ca3363dc4bbcbee8b5ec7b3a9850c805f280f011e8f29dc1e68f4b002c24b3b3eb0bb38a3c8bff890465d240a4c001a015cd0412ac28fcb642f4784763056e4c8f5ba5bc8bda34e2c7fd9fa58cff62202d37e0ffcd71c5123227f13e27b6a03d7a74bb0ea96024fe042cdb8aeea33d7f540170bf44c7f50be10b01718da5a8a2b1389bfdea0d26bc51c08d8179488af16e1b36147340181581b63e9a50a267cca4a8b8fb8d0358fea1ba5bfc721deb51223c48c0312bb8b3f836eb7411d660296104d20b827a501a741c48a67ad8d50862f0e62958fa3757d2e79131b98f92e4ce123868a442e8d8c76f6ee0e6f127fa584d2e99630248b102257429e38272e463601a30602f62576405b97e9d8d9d99201435bba6a237b6baca52f1d26637100a5eb847db4f55b0c0767ebcdd727c0b1c9a2f5808a51dc92ce6b053a829f27482ce6c9f2ce969c8cb748a632628565d75ec6c15e816c5e8e27065ef3687342dd52bc8e9bd283ecbc738f7ffe96731b5b57d0c59fd364ef6dfcef89e85013821556d47cd0600f4ade602378d0dfdaa687a0ab3690f1884ef788c6d6f00cec61e5666674010dabb756f007fcf1f7abbc072766a17aad87e9b327da9db33deb40472c4ee41b8e89e557ba7e068b0e378e78a0154051adce22946220b927b9442be6e7ae93a398dce63c71c4f9543913bc53d2ed38d76d61da9c05fd7ad9c1bd828092afd68a0ddc1ce60994af18e543d3a1dfa8534a0ff54cb00fa9bccab58702e580173be834564e99f45d64b6690d15d76b6c184e3772415f21a5de32d970ae88de984d2107931688985348ca2000d51187ec6dac60407142f8db6a1b521a67b157602a16243acc20f1203099c88b0e2232580df70f7a1629fc385b353d76268c600ed326981e5b05c29e21084a9109f72c3c4120aa6821f7e395adf27c2fd829b62c50348fed84ddb62c757c275fd9a8f488957a5bdd47975f32f6626a5033b26fb74e8090faf216080ac18ed5e40b2c6c8a60d05c0d4b94693bc9c2c424014b274585dbde043bf2d2191ee640b7e206648213245dfdfa12b80f716cf8a78d148ccd4fe486677c7c58cd9b7ecd12b4987b614596d6e5a901685b8a5462db9983bf1108d536cffcb7212475e981459d38b61cd1734fed42b45e5e924392e3c8b6071b172271851e393421ab2916db94de067455c0382be39f02764d9e333ff8ef5074e0729dd2735f2ebb34e68aaf8b5d1d20319d08a4cef63f2ac51681d4f4a64902742a20c5c87b367ebe2ee6426a4b4ccc88d0d2ac8e701540b89c8ed97d5fefb4d97e0c5e93f69773a7c0152701146fcf7e64680496aa0210c24d58c2d1895f244a78b58239576e18f8036356af0cd2c36d153845d2f59a5f34e588b44502126fc437870f7436f72b15d2a199d6be336fef548bc528ca7d9a659094f3e4f3f8a2201b966b2af12e28cb4519fb733cb566b5b563d1a543b8c322ca0752567bc925272b9c829aa8f9bd206a5d15a6c3e18c807a38a2740812e06563fd7d2ebb5fefbf5a8f358fa2456301f93daeb52bdd3d59342cce5da9c1294173d23a1bf8cda80d96cd7f73d40e06e23b6f6dc97db28237635203747a31817910e940ea93e3d522d88052b14bb60e91ecd1921ad362e04165fbe4f716f49e46a7727456b45a8772f9724b728ceafc7d90c8daa49fe3ec9fc938eefcec9b8df7627eaebbbb95feb248f94a18cb6c8f1cef3744488b3fc1e15182f21ee0825bcf5f2ea3d15b180d75375811cde4f894f84b877f48ec83d87d8c884c8352f788e5ccf0003d96b3f07be5327687a9189efc5105c42f8912510c4c7d73feecc57ba7640bd9336b6294eff149c829fb44635c1e74e31d691032708c5c7d91b8be287022002ee86485dcc148c5eeb69f5fee6225f1a4c706df8719f02961031db4eea799ed96787a849a8fdc54320ae86c733d01f41179ccc15388cb3f08c8a407a0bae3a9919206b4b14a64644dfd2dd3dbec8a7fa0c3a0a62b02f6bdc9e5e5e9af68ee6451688846a15961d83de24d55c5a489f3e06c849fe88bf3991ea592bf491aa39584f4317ccffc5edfa415a2b3fd74d9e9a31ab220bf5528ca402519086a72098ec37c701f15b59e869d4f75fda7d89da761e853795d07aa513631bfdf7c44b021a4f1e78f0211fea64470d64e5e1e778249789bac9fb3fa951e19f3cc70a16571cd8092adbff17e562b5447e1767cfd22bede0ff8d3cd60c477559a3a7e0015408540591b268b6f36b9e3f3c76a51cb5be143f4b577829e229b05dac5ad71a2d2051ea8301fdbb4be2a9ee30bcfb7d0004949851e3a8afecb5d5cc01c70b8700c80ff183bb425fa523cae5c3a1aec8ceda0cdac4973f37d158da8435547f6d6d7bac2be30d6b479d1d4dbf807227865a3e45f02f9caeb051c42604addb6b40f877cc795f6b5b4ea2c3f49e9246d2b08d1c76c4dad7f4197a8a9a94d68aa01fb21aba901e52fdeea723fc8ee43b51b2c2358c5a7d9250044b578503f7bc1dcb0086946eb68a673159a39d6ea5654d74ed032932cd26156225cebba0966cc385bb594ac4271ba9003df73811ee2fde1af1725b2b542cffdcc31e3b4abff7fccd10698661c8c204147de04eacd3c3bcc8f3b8de5f612b98c92f0225a97e82d68bc964a54fc134285266c6daba38c2785ba92a0535a1231a6087bc69018872181f6ac92def6cc127e829aaefd99ea7d6ff175cf396184ce7f1d25c2793cbec4dbb55854924101b2ba2553ede9e6fd83ca134f96eaddda123971f559431bce4d2c794107a0ed675cde3768609e8eb72722bb6f81d4ad247d9fcf8a0474eb194951838a97522e25d92def61ef945d3247f3850699a216b5433c5d8920ff28cd6ebd40490629323d0b34bcf75a7acda8de9af8bd85559424fe8d476afe7757853708fb0786ff1c22c262773a599cf9e6ce352e58af51e32f35d1e111c05297e400467028b90736d3dc6f747bdf6359f02db71ae562ca4e7392c336b1e15382805989591d42cd7bbbcc35e286ee684fffa37b8a693747c39334ea3c0df5618f500012e90c5b82ddb732b03188d6a66befbaf788228bcc9bc6ea0380dd984c67314f7bff63420458cd7b3da463195ce6c2dec2e251fb9b796d0e89674ea20e0e63049c607961a65d0edb440aa68004fb804d0b70597b0e1c961793ed0c2cec865635aae534e2c197325cec05b8e71866ec4feb3b0ef4d7c36414997b80dc0db4e45f5ca29c940384a697bf534b235e7f8a4ecf14728d578c185eb294e7687ed667c16b0760439be350d9ee4f5ff9d0856a7e9bc478c1d3129426233c584566c14e38f0d8815018bbaf6d053dd45cbb779d4a73e570189c2dcdab92e69f76bcbea86dc6e92907c6f88001394f09045361bd5e509f5cefe021da32f0137ad42b6aa199bbf9742f47e261a5fa7020e29e470b12058f4331c7bf687e283827fb2a6e4ac115fbd5a95676692db9e4ae8e250c2ab9db1920f413e05c096b299bec7e81fafbff0fa6ab8b56209531545b66746d2792affb87cd42dc80c8a72ba90713af9d80c24b92d47552c71c6fa8262a2f44c0c02e0aad080231a514c6ace5f25e317dfb83aaeb7b654d799993a86d59be5701c945b5ddc78fbe45f6d1d0b57a9bcc4496fedfd38a63e8574f47afa6852f3ef66f7dcefb83c931aca5eac927ba65152089344f2408a1d1be006312031e16af83c90fcd35ab67a6fb8fc0ed1845138865d9941c531f9d7c4d56ec91a5d5760569ac168ccc8fecb292cf46c4a734fe828a1289cc40c5d2600ddac79bdaee83103836f149a5a1e44ec3a061dac42492fee1b661c1289f2afd4eddebb95ce57a4245f1175140489ca8fdab6380facf700d57654859b34f75cff573f37e03fad33685b5686bea4387e883b9df46950878dfc6e53e4254266a05606b792f1f7ea04aca021f2e9cf227da7555f698488a2157c23105ba9b2a25a6819db67a29b51f4c653e49d48f32d71ae29da6f6e4923e12688492c5480531035edd9629aa8051a5ba4c84264c6264add081b8a242a3e09e5183bf65094aa3228dfdffb1166611b574253ace9eae3a73cc66e79a2526d4c6eef833a67da48d7afeccc27e592976048cfe17f5eb3c01cdd89648b6be45094c3c262829128e7833cb2a03a544dee8f18f2538fde0ce20200304e9d0be6c4dff13a94e009f6dadf2fcc0aeb93a68b50e7468dfe5133cdfa9c5f9c279a1e4c2b580228e93858439e5157190d9726577757e9c8c1f7bd0952b91a49dec34deae6e783a407a32ac707406a2fd19b6ecddc3ca53948939e82bbaae1a3e12936a564724afd132a5064df4371d6e72badb85cd75c509740e2f224720f0491b70191c6f06f4830ced4475579a2f0a59fdcd9b6a5608cf1c5828ae89ef447b0791afd1b16368865f3c13b036a87d817691394044a572f7e20a807866be9070e7df82353906d621dcfb177601e639f98c8eee22fd3460e666c2079a68c46a3acc7fec0a6196d7ed972b98c01a8b7c3b9069ec5c0284a0ed17b8633a2268835e9344ef675149693596e0cd3c5fb041d92b7f595cacb4574b2195b33df05a6c897cb577d2ec08f91e527990489cae0e8438897dd98f4c0517556cdd8c16b09e8649e1d323b6d7b1b3761c65919ecded12d1fff38d4093b42ba7f78f2fc9a3e56620c7fb447636930d3cb6ca314c5ad386204d4d13b1d94e6689d610fbc37b6c1822943aa9e93f31e9821bb101487f2e4b29c45b893626b6fd8b5b00aee6819202eece26cfde95623740c815551ca8473032c4fc02995324831adaed0038ebec1ca6f0233ffac6e4e5650b19e984a497e8d7fa73225f1bf5510734f7856197494c57254c4a73ae77adb962878a68b4f8ce94db361c914ee1a72ee2a5beb2cb1d55b2f676db2902682d850198bab57c812763e3dbe9e8bf5f7ded2792531acbc845a113ff45046a985752a832d659a452763ef914c821fd70562443c5f78a0531ef7c415750bd7b7f356a23d7f16fa5e4f4aa614bd4436ca227c506e7965f3c1fb1137d3be60fa9aa8e3efcaecdbb0fcaaa8f8e0da50dd3709abb1c0f739bc4ce03859d248c4a98fe36ebd7a6b93ec28ddad644963ea25ce4e7742fec619c02a63c3f63176d312a3f72cb7e75d49e51fbe9dcc137e51e0c3d822c258f9912d9eac2b35eb58c992ca2a31f54ecbcac308dc16a4d2199299b207fbe729f00ac7a0cd936c03d945ef477ee67f26a69ac4d1227a6235cb50f74247a982d5921d7e3478a2e4f6413bf0ea2a0833caecba4ca14122cc214e8216bceb122ef388375f0176e7dc4316c69fb1ff65f2250884aafdfe8f85132a5dda05c5493b4ac785601faa977cc60c8e5e327207a42827ad033c39862ec3489644533715a2684d97e65636219c30b9c4ba264bb2346d611f903061ec1e2a2ef9067ab46f005883df3cece51e972da772fb56795b6cbec263f8b24987ecd3997a258fe9939e03185aa07e1a15a3e739d591b023818c87485aaf146c6bb885ff138c318df0ec3a720ec5651680723893f65c8b82d5c82e2df2dc1763b1e1376c31cbbadd7d488f0877119ea8312e316af55826476de1c8e80fc08a0d780e27e3b4b0a254408539551290e0edd066cecc880db1706871c7624392f26d4b675f483a5023432117185cc1080e379814a46196260b9d95b51ec96844ced708546a814b1322f7fc2d759409feb404674a891ae81ed5c94acba868d4e6af0a4e2e032c5b149d5b87e558e530889907e401477ffe6ba97110a9df5ecebd70413a86da7fa2dad3c87c270886f6112df50eb01faaa91074fb20a819f76f8705294130b5925cda5e31bc13b0ce12d16c0ebc1d2f92654bfc043e0748a5e141058decc53a2cd0bde618d98b861a84ff0b439e89e4c8e049ff658b2d143689021abd5f9942c404f4d8544c528bdd759c5d6e5b0ac3b9b00298a70e053a031f672086c07368f9244d343e11e640461c712c5e98819110c7ab0915381839c10e690a589a1b5974d94e873a32c43fcac82440d2bffe64b284db365655891cc21fc3ed71883495bed0e484211131acb66a6951024cba75a6baa8e706ed59bdabe9fd439166ad80608a164f57e6e76a2204561276105901e7f7822665922deb420577388ba1b0907378dfb601e58abaa3f3f5351e32a21821c8ef6d89215eabf025885b53799b0c9797b64ea1bf632986dd247c0157ca0149ad4a8c1cf54f1aef738f3ccd8666fcb77e2715af1650d2a8dbfa821f0bda1c699ccab11df848bc2725ffdad4afe904b563e24340b92d4b14c429cec963b11dcdc8ea5e12d8b0da4a514d1870255c1644a4e7c632e6cbe80ce067abc15e0d0f1c827f60bd95acbab6304a5e8f5218546292d490827da8d4417ca734e75d519268e4df092226893b8fdb3e948cd27cd449202fc750e89f5f52ec0f68ba23371866e12f163f4962591065e12fd148f792f333d5272b7a987625a2b32f722a813880a1c7d7bb374578b531130db5b49891276907774cac400cb2b3989984f71ca0d523762656b6ab832a2e827a930ba761506a1d9900701114c52154e1c1dbc6aed5848f721b8658e106c76b8fa83e5928411370480f60be97b4b780355c08c1808f0bd4db5479fd5db91229b2c197471c68e71357f3e30bb33cfb2c1a0de2bb0ae8991e376db13a88df042406e315d4a22020b27c4b0a17b8589a091060f9b066f1e74b6bbbcce0b3bfd50399e8a38d95694c9c93a56d753e861a7a78c61dcfcbc0c9ef731bb8a0bba35bc5fd662828e5b5a979b6e57f5ce5dcdb550099fcd0f03535d1860f08f722413068bbf22dc07c2bc7e0a877a97c551bd26962a858a12bb7ce31a89bbb109624e32e65894736da21ffcc2791be1cd0ee7909a9440c2e2a94f983c3bc9194155a32bcb476d40816b3d0d801013602abe83521940ddb16a59d5237c43ceb81cb22e0e87db10f71b78e048375d091cf623a7a34f0d2d07d0f8e792953ad78b31560aa3c9ce53d15adb07b232d92dc7920f8179280640596a9e0d33970b039d2df6e8a6f10a5f971523cc9da3c8c909e1da330717f63baf38fa2127e8fd457bbb17bcbfe50a1f91489f866dc1b9d5ffb4307a6168201206c822989142a8cb4d2230ac75ec76f94937106926bfed6fa327ffc70689e07b97b3cb38a02d721c113030c392e2661a4e9dfa83496e9f3eccf741dc5dfa6e34bedf8e5170bbb802e4211c0f32c9ff270554512c906154019d5e56d286ac70cbdcd0e8990bf5ab6630bd1aeb666efaac1d29d1e555213d682bcbe7526e485c593249153b2b16843413a0e386b7bc4961b87b04019c6cf76443535b202a93b0e1d403118c7432f7efe260f451f4f6bafb6e6d6cf7598dc7678f453f68573de9405e996ee053a19336249712557a0bad6abd46682a666bea0bbb897f13f87827e07e91c0fb8914f2d82a1dc82a6fc948d62372b13fb0e5d9192b213095f3b1e09d97a647d903a7ef7548fc899898c69b6e29d4e8251b4b95a0454681d15c5d09a572099e48bdb8ebed7bc0f7b7df5ac185d1b61e04a1468eac78b9ee8f7c1a0b74ebf0d3a879251d9acec16ab4e247c69478953668acaf2ddbb9d8bb761417254d2f79a4670c4b0d1c9d1309978bcabbc438011322ea9b4e857e8661e0a13314abad57bff52f9bb58a1584a80f46e45cafb4aa300d3001f96e3e92b716e82e7c71590cbed53b2fd0600d19984d76e9e5101d2acf540336efe4047d2befe8c3e5d08caab6aef025604d72107a53329430214d303b2501703a18dce17a910bacf750e6a6a6a24f34cd62b128edb148ff212463d2f8fe0e77556f048008bddd0b2e5281b492603ce5e78c4f092d807d4b01c092acdef9718ac9d39175f4ca80e822090902ef95ad8c981c24f6cfd12154dcb88b41a0489c263a13eaf9d0a906fa1d1da5166584c9bd5008b036d9715e31473afa86a7b8edcc1ce7d6d19cfbab258596d988170dbec28f5c599d3bff54c473c36f950b2b2477d6b9e6d5d7432816526e9d53ae7f7c9522c2fbf018aac446602e1ff2f8940e32f361cba746ea84f599d96d8d3ae35e1aa244c1e596454c78c326bcf7a2d7d83872a3f7709ecb8daee1499dd64d2e2c1c56b379a28fb844ee53e6250211d20a3a853a1ab41a6e09da5761e1d92269d6ba8411c7634dccad9f040c4184079ac22a6e44a78e59661569a36535289a70f56e1caf1260ad81418815dd06dcf43e5071a93cbed4599b6f9b0ab1cb20004e3baae89091cd41e46141e3f7b8eec7796d3fb752505c2bdf9bfde0537e36eb24b13224bd3b0ffa0944146bec20f5588ff8894c702bff5f1ec34be1c4fc04750e1c17ff9d81268084db67682f720e3e04bc8cb869c2201923520f71dd44b8acbce21ef4b899054a7df40b6823623c0b2a0bd5ea84c6d0ccd6fde2dcdb32e1550c97c83ccecd7b622df3fbbad02b57e36398663b62f90a71abf06cbe3f336993b61c2015fa77cc806b0577dc27ba8f7f5dfc7208b8dec49f7fcfcc3d6206007ba6c6b53190b5f5d6b3dc93011d07f39f97248d2d49f3c20f480a8e70ebaead861aadfd451c5624ec66ba0c47c58d3204dd6d0c8315169121517e85b09505da58202d37c0b747312598424f060ed24bd82d3f8555355b7789aa0387b33a498b058c362146cff3544866c4150f21daeba8b6444c6497b30576f5e2c4741ec4fb1177e496dec37c87ac5c089f649f810322e7c766aceac5d5171b07e6d83369f84f23ab60c5d9af90fc1a8c44928678f3781a6f3c1819f410c14a1ec15d8f796bd8437f6f5f1c305773a5fa50fd060ca28647dd4ce08fbc1c187449513e07e5110be92fe2ca48d5b29ec3d165c1d2b55c0a1d90fe83091a6f136dfa32f56d6b16839458ec1a1dc4e11bb81d86c72464ed5f7bd55eb17610cd82216018c5c0bdae7944cf3b9b7f39a4ef12a95374e549d1e3997976443e80cdcd125416d72002fdca307871246569febf7912c7832666b62c08c67249b1c00280f55fec403fa01b3261ce9b1cb1f1c5bbb36885209704823af89e1a2993ad36439edfeebe2ae2e113ecac4094f1ac55f75e5018e1035343262e9c1ae99764e8c2b7922befd283904bf542a334294dfeae0316decdd445381a99f759127c69cc101c15bec5bf636a2be2db723fc86ca6960834f85e41ac962855b81a03c9424d45a66e184f6c0e04bc81b3440a8d2e438cd34de595ec657a46cb01fe07be7621df82bb2bc002f94bdc2a73a5056ac1d9c82eb7cf3e7aebed0906e2edb881fa833ea504033cc8918db0800e99fd672bbfa2a647c0ae90206b172cda3cfda27aa9a49e15166d3febc7903e9614a753ce184cbca426673af847fc87b4e591223ee5bcc74d0cc3eb92a44290e8498291e8aff2c181d25f926b9bdd575200ef0478ec94176801948ecd0aee48e4438e2182717537c0d935974fcbcbc7090a29d30a44e777f035fead86720964f6012e74f54bb7cd3832dbab7ae34376926e4c7e943303b778236c0666efc4b36983134b45af3a01419af44c0aa33b690e5727a36f1a65400464466e6234178455de875b8ba44993005b5ab4bec8c0b83c630dff3871467bfd0355c6b68fea8b38c5a6a89684e345c902bdab00f8544d9ebba7e8e9c8eaa0dc78f06a0dfe9fed50188d1f368adbc2cec3601270a79430e1d50a20fee725a707bab01160272e70e47b4eb3fb24f0e6a8d74a722a3c778b0a53331dd97d7a3b718bb476bc2fcc130a1399a30d3e815d794f8da62c59d49f07eebe3c1b9ae0acf50738cee1bd3744fb5352e23e11317b3beca2f49e4f11d65f2a1a0fcc909217a8436956f0b7fdb48a021dda6919109d3083bfc4ed83a53412040808d962423ff830d50d0302023d5134b05e0763f69f3b8f1ef34013deb00ef44c7e6901ddaf377609dedce29c8ba130eae21875ac1cd035cfc6105a6e292dca098a2810274c2a4e62b01ded3783020c3e2b210ebeec1271f0a6f0167aae11b899622dde23de668d784e7bd73e461fe0dc27d66fe74454cfe0487078ec621d8a3c669b204a68717858afaf084070fb3f8a91910eb119a1e34fd94067626a0e04b9d029b709b19ba554a685a3401afed8599af124c7fcd07a4de50ad6491395c7ebb3d88fe25eaf1743de166e989fc48cb40116ea16a2224a0f3bc781d92d1e50b1257c17c51f8e0e94c48e55882b725448b5c065a0ac3a406ad65aaf21841c9849319e6947be96ba533bb48e0cf3243a1e3dfea8f59ca829205e068e8eebb0d3ab291ea8e3cf4866c6e0fc7d3a1490f538e22b94a71f3e84fc8d680d5c3f131546e9b04e03ebd4b12775b987d936fae51103e25dc643ccf1e2d51eb21c04136e2ea50a041ea0e4aed7bb037e438bcdee0d01b488c1a771470ea1d585206f9fc897a50321600650397dc863e04760bc2b81873a0a483f9db4f8af538539c744805428326eef1345543d7aa1f87e8c9eec0e2e88861a79824e5fddbbb735adffa323b84a77d4d89fc7e3fc6ddc44395507742984127a205c612bf64230f715a640daeb38c5e2c10b83e0b67ba48063d91aeea29e3a0b3e54cd3480bce32488c9c9e96f7388ef64fca07b10fbcc64bac263cb5b243b7ad1df2344d644e3b8834a90ed7b3afc0470a9e89ceeebf41fd943e07e6f84f5a728cef09fd81cc0dce8b87ad8ff4d563187675a890e4470a23f6f575a5646ff93fb4e126507900dea2b5551b0896c9537356a7fecb18b87180c6f7aebc40140f6e81e3fafbb29c0581abb38132aff792f791c7f0ae6a5835cdae36afa8bb6927f523dedf25c3be409c9c29ab64b6cd4677f60e12260f327c657488caca5c05adf5c0f34bc546b17917e81b4df5dd4efc600e764bdf481ab180ccb9be8672b83e58409e1edf1f97620e4129970928f621974f6fb4a22e9314fd566a627d1751f825e096bc4d0fc7d346d4338f285271850efd4501f94600ae28dff496649a1e2c881b6822d6df7bb92eeb94eff88d69f5a29da6907b1708dbeef2a756b5b24ded74e57051f24b616f5cca667e5e13edd0f81703bd9b6957282db27d474a39db3885f151e742f1800172ff8bff78c9d94aed3b4664e71d18e720e7cb16d638b8cd4ee2d7d8d21b01eee3ea5da3b098af304e8b4155932e9fb05f34f0273b3de7b050065f1eb2bed938b67d651416cf145c6e027641815f5f83e22f92aabae23bfd8b7f9eb5ef4eb75d518f4e5660425cf2b20a3d8922dcdb351b04145bf5074e2d9652545d22d1eb9f5f310ac40b6f6f8cf9e8b44034f0eea3378c6ae97db72d6769690f848914878c01041fd9de4ee4f9e44b40e643dc0667ae557c1f8ee5226f4645b50b946e1d3a764f5f3649ebf6144bfa185756f0d95814518d2b45f598b5d3b0716733181cb11121816b08c5ca457dbcabc22c73136fb5af4a59a8b1016c9ed7500d296b3ebc194f3eb09a2aaaa6acfc1de88de0d6b21e130072c10714b4dba78b0d47a157f73ba36d57c1eb124a4528c9b736da9ee6d2bf26f6e18763f55c46d51ce7879cbeaaae1ee1555bb911aee03530e4840f2428b9902df6460a48451bb27c044aee30453693473ce887165b3332aa6b081f5f2a6ea583590e54a0709d6bf51e03dfdbe112046877ba61b6ee2f3e64e6a4833da6de84924d78d6fae1dde28e094ea9146c5661a2e12872481225c9be7f0821f3bbbffcbb4e5da7e691c41aeee38b34cbb5a53c6869abe246b9a8c7eead149dfaf89eac588a265b7d942cfcdc596d132870c87831c61aca5a54424add061689c4101b31667956a890967590ebc845c7e4dd485b0d747792ecc655c43e7ef4c6d7c1df9557947614b9409fa4fdbc6f6e90f8c532ca44248ce096602d9b9178ca1fbe107cd99c64afb2b722bc92737779f4b611a974c34ef4b09859ed173ad6b9ef54528b3fbff208fa50785491b2f4c8f00d468b08e12051b22ccebedcfe8a589f491dd567580e2ed55c16c43b485e04297d1ca2a134a0b4b2d6f5903e53efcad9176487c174d000515800cc52e1ba21daf582413d46621682a1124d97ec422e57f9ab5b56b6a2989a748d28ec1e20cc3c1d2e0b90acf41622c8c056b00071d3d41f11bc46b1e195a2b6cdb3d03d65def58ece01adfba01900807326d10aa97cbe0d389e8ed7cb0e096d8780f38f8731adde9a3aa4094aed13786f5d3e680b0b1f6378af7b5343343dd40c433d7a17be6550544d489019bdf2b3deaeebe5deb03fbab73679f249f59dd86a5b549ed9d571002bd73ed5d595b5e2736adb8a9f0201d2f7811b5d2a50ec716009d23a2b6b2bfdfd469c440c18a03e4c4973dcf5cd9363e335d910cf6e6f9aec9c6619559d57efafb8a83b10c303b8f19007775d3d677574d1695bf6ba5a6075bd401abd596dca75a2698c6d51d31bd16178f992c67a3ce6ef77b22e3ffff37f8c136282dca20f831d0aef6ee7d11f428c52ef150e4133fa85087bc007a7dfbcf7f074465ab3ae09b8674ae2febc394a383c71abe861e9508c7a63a213a3e1380a836cab8d0a55ab39286c420a3179e3306bb51c36549434e64dc3d982220cd90d4fa54245cacfed3c4c2c2c68f540570a7f9ddd8574f8b1ab6cb008a9311aac7ecc180935d05ec75909499128fe275b6930686307ece252b968f525f8551cb4d137977934f883a48904771d8f31f440ab553c93012eac86534bb0014f34b2d2c025b51282d2bcae4ec314084b510fbec3b221cc1e3c2e86f202ac3af2fb7569fc4096b70151d1d14600f8337ba3332581f033e801b47a7a4f531202e2f6b08d2875b85f2209f6bcebcf7017993c48aa49585907e24509aef45f4307f97585093601e4e0f8a066ea025561383b592c7e10d628b002c2b12f7f8f659df63ce1d2386ea1fcef6cffbde3d229af70e72efa39ec36f670107c95c7cb2582301ebdc28d5c1d58b40fbf848f469a10531375ef8c10d563ce68812b02910d8e30869712c7aac47e578c5c3337c73e85be024ee29e10cd557ad399156b1d297d9c80076c2fbcb01f1019d975bbec8f2068c6038888547d67e2174636883252349c0a3bee33e14cae0fad9a90874ea2a75a457daf63415200611664b2cc87b7c48e95a6502e1c25a2b8d89403f3630f6eeed8063e5ab1e894f793e1eb8fd1b8d2650275d1c711595c894f76adc1d7ac6a45b6117be04465943112a0085e48abe919bf1df062b3bdd4968c3ebc0da4815cda8f731175b725ff56ce500b266643dcdc2b6a5bde2fce5882b1000e2d738b0e093ad7377d4bf6712a658abc831b32c99de653843abfafd313650656a66b76daff865d4f2f381a1178067e2040ac1ec39d119b7ae95310860f581d2d5db3fd03bd99569b298fe893213cc19cff571afa89fb9eb8d7c5378850de2499c0dd74cf591bfc79a822fbff243d9e713ec865abf1f9548f80f9119707d52746af87ed51d9862d1516fc3116127166693bd421f931baf6ead4399766963a775a83bcfce31de5bf769eb5d6f0860620d4d30b662362f4c1c2ab54510ed5b5136026413f0a5a737cddbfc1bf41c40505a79db25de36fb12b2fd584bca3e5b03eb7319415b1c3399a5afbe23472e287cfd3756feda624e0e601274648076ca1c392dd381a957ffc39c9a2363b91f0a228ac9393db86e86bc3878c37c855dfe6462c607346f96959b40ba2820a8c98fe1af51bda538b75ab3811e23797bb79d4ce0c630347376aa189c7023024861bb02fa20167995a47e7185d998f63fb0d453d15c75a11cde803287ab242cf557256f9907e752dec64029cee1bfefd4b0231b65bf9c1dea53ca53feee8582f6f1eef0eafa672af8f828f4c6e113f7b11397372a9ccea9f2562ac110f2ee31a09874fb49313b7c5aa45131ccdac02752a8f6fb2fe0f42c97aba40afd68b45f7c6ef0a29901278d8e781b24e9b4696bffe7f29ca205188118d42e5896a53a16bb0f5f868d370fe4981cdcf17af489bd98efbe4d8f2d072d4be0537af061604d03d49892a17f17f0436c86767138f5ac8c7f65b2a982ad5ceba0b88326cc9e22f27dc4794f75c9fc1dc40c81a6fc841b5a5759955952e7c6979c3aa291cb4fe258cb68cee98e754e868456aacd4790ac0db399367b7cdcf50d05b1b58d99393b458fee04ab02b6e2bae6196ef41ef7ca50fab9257d5d2226e8f862acc2ee41c437d28a10d792d7dd51d323dbfba1a9746f52af09b9679e7794093943bc10b5867fa178009c63d9c76228f9d39d1f33c13a3221c867553f3de94ea1e257039f7d9f79294180b01a0ca119b53488dd97e8db07c28b51245e36f8529af15985dda5cd4a1b6a45af243b2262f4ecdb4fac8084c75afdc9f12e5dacf3de7181f553eef3cd6e962406cd09e8586f784c38c87222b0deb0074c54b86c712f504b7ff1c264d93dbb00463b0e600b7e488d66e34356d284c9a0a5de2028650ac21e97b9b89cfaca51d4f74d1b5ed8ae2dd2d3ab99748a16612a6d5c5e39e741a9cc922f5519616f6541e9c968fe60a7dedeb03f820e0ff05a72d74e0b96042d2c8fcacdd70648189ce320924ece1f8a9a8217c34e7dd6ee695a091e043bc13dba19e2f9dac85b11af1f069cea1daedc8efa19c27c95ef20ca132f50498b52ef4c851502bdb8b453e90802bbfc77b215b02c21ba88d8fa7700c50241fcaa7ea48e50ca31b7b1ed56325d1db59a5fb44918d803d3c5f9fe607ba51bb6a3c4420977c0884bbf830aec47f205fa0cb3732795148fbe2e2712227c97f50693932c5c27cc839a5aa027d711b3060281847ce578cfa87ecd64af82f32be58bfc50ef0c904d74b08d239dab314327a3978e041bf1515e82bcb83c0192174ba57e46f4ad19e99f12b418f8f547cf54e24a09aa0b1f9466b2a908c374e7ae5dcbb4e63f174e6a9bb9fe5e7032a108a0d2a3d3b85126cd58eb00a7b6be18bb8b2453391e06e39f7b0c632ca946f8a4842f4fc18ff3dc2f2c7309d588e56c743524e57246d87d42ac2f7dbcc05f0ea326597c7eab1e48c55e384e4b469393a2d90ec1c9d23a9868811251ec449dc55ed1422dfe8798903ec55a42898cb2c2a8b2548e82c2b9faa8999976680d5a802203d83375bfb482d319818a4c6ebdaa74dbeee2df1eec60fa9a6469ad8355fea0d1e7b2e7d06aef9cdb8365aa076e4500cc82d64efdbe40fd53143605eec4448e0f571058c86ad20100f9b3ca3ea3eac441f6277b7aa44737603283ccf433e37c5d599f6773c3079970e630e2dd22ab019e476cffc54a95bda793d273424534a9440f003532ffd80b13f2cbd737b326dda4a9b54d08d95bee1d6b0f5e0f780e615b1b863d817cd266cca152d2a96aa164249e929692383d31372757e50e9d2856cc35eaba9bbe56b14511927e1b914aae972e60ca151fe9962cad9c2894bd438443c032770af6e050d6eb5173c5dd0d5bfcba3b563c7aaf5e07cb63d83dbfba6b94c26fd908cb93933b4d841c044e10658b1804596104453788328611901085d31008babf5a84e908390cc34fc231cc9d4369402d68820533b0704a810b9a34b1200b1f30218919b82006a00d0d3e87d270a8c116431da8011754d8745451451526784107a6c0248733e0cf943895c3d228f5b0a50b29801254b4822650010a4b58c20c424005143570e205cd3994160127c80009464f10d2e24aad93e0842e9c3c6551a50456d4fa17fbb6bf29a80c429a27d883ca209c97b693e08f1e29aa90bfb76e212b4b2e96689059b96f847beab26f55794f79b5755a8c215c7daa5ec11f35a78e03478b49b2ea54f583e614b62d8e47f228411d80a44052128527b690e28322d4525775c7d457af208fd45bee435346306fb94454d968e83518acd3168f4059f51d2d1fbd6cedabdc71b53ef66c757bfb29775c6edf72e7754b0f5e9d898430d52365a568edeaf42fd7e54e8963ffba2d71ec5db74a1c6badb5bd7a833f6c5ee19c16e98f6a063e954a5910c77ea86eafc22a6c0155cea157e5d478e4bb558a296b8d745a461f438bf4334bf86a917ec49129fd70d803d1ef4e2f63318e16a9d79fdd8d67700e8c1e470eb5a11b08bb5048ca3c93bf3ab5c4276dbeb067df94dd2b7aa5146563a37a9f961782ad2b1bcf8930a76731f46c9d56c5d05c2c21e9085948820e9ed4e66194865305174ba07882c20a65d4e6ec1ff4a0361c876366898dbafb34b6360c61bf2889332f2fcd525229294ad2e65128164bda2c6d4ed3276d4698a6dcffe69c734e1e9bb9f4c2a4cd1c5c5ac93b7a72df46769169c8520736b3bf1d72a79fd3f2f14696af8794f5af6868b98c09696939506db55aad3ed2c8b57a8d5ca75e84ab6b8990ec11954d21716eb68adcb724983274ebabaf7092d6816a9648eec8f02db7257eb216b6b490ab9829ad65f5d68dd2c2db03a6960e73454c45930706c906499c2390884062b5be9770bac2173b793afc99e3e5c3c2f4c9338370032f727f2ed940e2f4398ebb85dc841203a30218a4b92433b96e2907304893a762182222a2c903535404c354b35a5171b34173452767832c930edb9943b44c26ed051f12a7ff8254fab12371a03383c4e943b9cd963c0851a9540a95e718fdaab790fb5ceaeecc3293d26e06157aa0041545b0e1a0d6e701098c600327829024a444addfb7febc44e0bc5083d2e18248838d29c21b51763324b3beba349d1855d775ddb9fb340f42d427949086bd57ab573dcffb3ceff3bc96951652ebe816d6ebc89dfeee7c51e9b418e108f3c4a85313da43f1eea30d8241a233d36098e68f1c4138c228794f3d85ad8761926098aef8e00514014aa98a1620f6300f544ee11084a30dbad9a7a6ace01f4eb55789a0a8c0d447445353535313c6797ad3fbbcc9d4b37a054beb8b56532b8c9e757779086bd9749aa8c53954369bc4e3b09981bde3b88eeb3a8eeb38aed5a2a19ab76710d27f34543d463886f6033344ea5b4fd1581c350954504585128e62b629299a3cd60332477f28db25451dd4e2903c31114d84ab5729c2ce33c806d528c2791c3deb0c53032967305fdec07c10f373a86772e79b386c27f5496873db72250a59594a580339f3c2974b87478b5d304c5726c7711c27aae6b431c60dd504234cb9a09a2157a108479dd10615116518264a8351a24a4c3048120841158b1725b9e315218f158b7c453632737d221c6194ecc7b964933dac4292381d0482708429b281a9d2a20d9d2d962da26c17f5cd096aca0a8e79a0320a734772a78bacc0acfa964e93ce6cd269d2d111a94e8e8e7dd7d9ee66a353391b1d1d1d1d1d1d175a72877e34cca5251a74c2dc5994ed3a510c80d2cca124e660c9769ded3a4b25db9b25529a3c304c3a3438b5260cd3e49964d0608d309b88085b4ead0dc3160ce6fa43516cc192ca6eb7db9cb7287355745bdde493959ed52a3deb190d2f067557e151278f2d108f39796cd94671bca5638843b4798b5cad5cadad560b061d30e8504d3042950d923bab5c3f10b6f48c9321f75bd69f991225cf4ce9285d94411e4e84a30dca73de25b164b953cf05c027d75402a8e1c753f10081c05e2d1aadeaea4ab1c1cb9cfd689bb81244e11f3673aa55dfa638729f73a9e8c6bd8b5a3ce2b89ef373be8fac0d6d9094f5bba2be75f2a7c3329c2b0e4f261c2d5a691146a96d61ad61965a34214afeb2e4c20a0ff268955ce4ac4b502471fa56b99a2ba743c39475188669e6e8cf261a266d8a412985619a3c210c5318b6885a54b46c72a7c108bf3cb66c6084311e5e9e4765b065933bab892d07c2712eb59cb4405093a85c8d41b5aa94564a6dd09cf574d24aa73d6adaca0a60b55addaecd5ec82d1bad42434fc2b1656b6a6da07275cea65c1bc9d5bab246b90829c7f552f70ed5eb7251b2dc6aa1d1eaac6b875aee50585b611b3404abadeeba4c6c6d75bb0149c3aa09b983c43ccf943b1cade546ac4fd759600f17f2c446aeb3eeeab2ce24a7b6faeb75ad07a40cd66683a86ce85a222abb81008adcaf4d842d5fbd655b0acad38adcc12171ba90339c25ee76ae4c84948656a86d1316092532477f355a0fe4b61fc87dbb012983d8446c128eb36987ce12a1114ab3422deb3711daa096d1a6951621b443422d062505f56c0a0df54c56235c9e67814080f2c444542dd25f99c2948ff45b4e5ad66fd986c2d106ad2e115466836cd06bad2d5b5b69f98aae7ec2509c477012a7656b7940e2b4a69039e46c39d15589d09669c7e94c9e0e8b9ca8234613a5b5528ee366586bb55d67bbce16d9a1a029863879e61232476ba085dc6f2122034cc25bcb36976eb75bceedd637fbb1a37476d5b2ad6e2ddb949e8d7329f7ac3b8ad233ae673487ca6690b559227bab2f1eaa6a8be44e92dcc1194649e996a768a26930482debc32039c930452ea092821598404aeed19620f73f1e2a3a7910d65b4869c7711dc7b55a2d1eaac983b01611f55015a19e9a28050325e4389825982a6084b1f999633cbadcb995c71d372be1289f6094e48ef7e9e3fb7e7cdf00becf86ef03f27d377c1f01be0f87ef83498299d2621f46a967a8f761aac81d982b5ac8198c0f6060946098a6088d54a6296098729f8824b3e9161aa979ec1b13a57551cbfa3058644fba325adda76f94b6c2304853e44e1289b3023933428a7d0e061391043d0c949e590c43d4b30e27e981ca44b428148eed7d7e35d81f8f47d783f3517fd0cc0de0b341e58103f8bc331f354d88895893c25d488a3d937b3f30474d9362df8729ea3a2170e60925e4fef950f9f051a56f72a79b51907a0a2afa8adc6f2992876ba52410043f15087e5781201e813e90876cf167ca21ecec22141287c01e496ec448e771823d3280271812677522ac0d43d8932d1cfb3679649e7defefbbea3b0bfcf98e907a7f3a812179a4cc91c2466693dcb172c73b0a85c7b0a50952b66c12a75b4e5a442d282d796c8540e6b04136c806d9265656c806515adf56371bd42d5b2ba945a56795b66c4ab7a5a34c572b9ce4881a4c93aaa70ce6a429140a758a6938f11c9a93abde94355993b591060e6d15bf8ae7508ba107240e513b31c5db13b96b5148aba5ab22330b99528fe33cc97d3eedd3def496660f72bfd669148ef3ca6d125199076c5584e3c44265761fe7136809c2eea6d24a1e41c484127ea1a4f37de0cfbc5648164cf60ac9c2caa744850a95fc8142b250aa18357b207766cf2a0a754f65d356ab573dceab3d44e007da3b27869e7557125b67a9c58b7426752d95c1d0e911714410f388499b9e53ce164d79f24c25c81c5308120a390b16891384a21095c789050ba54d9b0d4b4b9e362a55aacc398564c19445952caab428b3902caa5461a2d30abd42b260aa5484748962215930a166e02441c8dd86a94599ab5099debc902ca8f46c52b152a569d3d6b256ea9664c7948515a52ca86441258ba469cb426962215950913b730e654a049bcd668332b1a0b04cdb1c32cea13c87e6afaab66c9e62a71a117cd266f45e9ecbe5f25c2ed7abd65a5fb76b495cafdcc7b9bca0aacbe582f90baaba5c2e17afebbcae0b29ed87a62c73badcaec1dc594319bdee7afdc575174ee2ba0b5ea9264542bafe4396d9f4b95ac9a576a7b490c2bae7b85aa967d6f3ace7b98c5c7fb9dcd80b2779dd05d303d55a5661d7a5dea5509de7e170e6a0216de6602e519948653998b417aaca75bbd66395bb8e48e2f40bf389ccd19f4da4e041a7aa5d57bbaee7d4a16bb27b7bb57ab5765dd7553abbead5ae1bd2f11167978f61def1fa088b79bd5e1f696479dce1593f5025c11ea9a698f79703d55c37561fde9959b95b402229eb83ab5527e455af1097cbe5250b2255cb7ff8c096b1835a0a233373e14ae5bab2f6fa9aaaacfd3e4e1573b9bc5abd5a29a54a61f78faac2aeabc89daa85a4757761b404511bcd1fa8ac6b7406a148444522715acf7a5eadb513296d918a1dacd6ea59afce5e3e18555d1d955dbc560bfc59fd43ddcedf8beae5be56f769973b6b3017e6ae2be4f217b083b9495cf641a618be257cad8294a1ef7259ad7f9f8c6a82e0e7edac0bcc4b07f3020373180ccc5f6e4c08ec2f87bdc0ccea3f14aaaba8d7a35cff813fb53beaaa9a41f0678e221197fa17a3eaa8777b676edd7166619c57c89dea25a0a7f250959bb3d6baf2260fad972eaf7731920657cfd06bae8f3459ba3ed278904e6866994def195d0ab19c39eacb953557e7ae76ddacb9ee90adb95c770651d990c46932c5e640ee8fafcc34ef5c22f6e272a0dae7725f37f612c45f3726e4e5afbfe01c2a6b172ca96ce21cbae6ba0b07218290ce8a2518c5249a3c1d947b62e91498603609fad6449ed775dd41f0a75bfd5057e0ed67387f90324b876b28d4dda44c8bc38961547892329905f014246532e7489e8ed64a2d3a72775733475340cafa6454e10722224a54bdda433d1b5799a79b514a8389394db9678ef98fa6464551f97565ed06e9a19f6bcd3d806ed9dacf0bdebbab87f9823fdc5daeeb5429b4e7012a2571adde577ae6bd065d4c88ebe5ae03d55e37e68293b8fc75a0da0abc2f772edd97c3e0b98463dd6796bbdc981098bb1c064f261cebfe82a71395c9621dcea16b2e77c1b389ca642db3d02cc2a69f9408c25166f9a10859f58f86cae51999b779eb5f57d6e8c4446a2bf552cbba6930c5ae5684632bf9e0049906e128445131ea51d875f65ee759ef0757964ea43458cbe6cfc995c7a4c309e4e95154bca3310460471d826403d37823c728b5a21c32a7e0be6591fb7585e507deae798165ca1a9db5a3375a458777cc1cddd51d58764c9e4965937663eec87dae8394481c39db03b2974c59575507c91d2ad48492308cc11e43c7e42c007248389d9ea6d3132b8600ec6831784c61149516abf44c7228a51671b4d897d9a2a8a0a8f4ccc3a8282d2af52c858d6894862222421d115927a22c7d7084254f6c000a4c388941ad8f1aa2341c49c4800a50ac18e94004b5be75b24f9e5038338ddfb8046f9899c69bc8ff0c637884b20b2b64c938b2ecc20a54b2159e32be8c15a2d830730d9a472180f2cb696c646ee3e26bcf6e2e0241dfba0daeb74eafeba336d473b22f173bbd2d2cc4ccb1cbdc3a07440c17f92189e4d679cc3c7699391c66f04824738fe1d6633c344ecfc8f437637451852fba980296faef9be08f99732e1c5aa742ccec7ae5ca62bdfe635deeae3b73fec1ba34f6238665d3cfdc31f61be7402032779d037dcc2c73fafafff338900056450930b30ceb2edc9222646122af5f8238c89c622064eec240dc9cdea76397c88b8589bcb2cd2b1e89bc2868038d1f37b8b668831f738a471b58b628beb6287237bcdcf0729abfdcf1884cc33dd3d07c82378cacd71e72eb16bc81669a09de307267d1fc82472332cd636ec11b3ad39cdec09d855b788cf9119a692e411e343ed61a7804ca34b8c66328f8a3738dd35b43e6158f5fccad9f61cceb6d4911c61c99991ea9995ee50985311b667ef9cc2fe7f0cd278efda071ee34648090394ee140e358ca1cf3d038fe276908651755b092f165eec2235076bd75c7576e9d75472299c55dd962e5689f99c1a378d6631c1e69deba0f6ddd1b675d7bf18e445ef9e53477dd21667e79833f3abffc7839c6a3cc5f5a9385836c71e421b74cdde5d32ff88e327ff9f772c7ce339f047bcce071e6998baf9775d76dfde5e56bf087ebadb3eed3dc59989bb932bfdccedc1d69ee5dbc40c8fc3ef617f087abbe5abcc136f9e6b1c7301032a77173471a4ce4956530fe8dfb50d50db1986f3c75479d7c23dff86a06e59f63191279e52166a6719f067fb8641a47dd946d4978ef38f33d775344f7f5c6bf1b37e50489708cd1a53c4e2b313a8526298d295b4ed9a8784ac291da3adf57be1728d7cf6ebcf38d1b974da3b696f56fdca0d306003cb18284cce1980d315b652966b1e4216a06c28641e3ca7ce3d6ec2d099d2809477b435275454751a6a83c9967ce7d2c95046de04ecfba407035b35aaf13e4d162e191fb1033b35e73d685d5ef93493296149b31164ce16249b1582cf6852aeef5aacbbdaa4ec11fce87b68093053387eaaaab52799e3773bbc7543095cb55b5085a9bb211718516286551842e94f08305e0a08a183c41f941d313bca0f08e610e227353d56521645d4e257a1e8b41cd4693a8127716a698b3f636ab10c67ed0980ddc6b8cbec628e65133cd36a416a748214c113969b19691b24df007cdc21999853aea5c10b9bec382bdd11bbdd12b84b245d6a90f655d7bea04457b6e927904273c0189f623a410cc5841538d86a5586be98dd2a4454ad95250687037e5a4452faed0228b104451c60a327002253499a04916a22bc440d23a29e502c973612077b0dc71495d497591d20247e66ce4cc8811d95b70e13acebdca19ceb95b39bb71ee2a39b371eea19cbd70ee2f39cb31cb39f7193983e1dc2f670098e138774a6ff4964a3a231c53492a4a4464072db2487d9bd4b7377bd4b3d763f8b8a3bee5a3ca8c10f69a1fc99dbfabd0201a837e72a2329efb915a414f909b46a11405e1686f35d748919985dcfad75eea40425746f0440972f0021520219b30032b5b3c61cb4151184aabef2e17b645f62625ec4bce7597a5b776425d59c1e076ea2714d31076883053dc65d8181e61a853a872425d4131c1cc601485254461415d4131a198929292929266e8ba30355e83c60f0d9f9e5c8dcf1a35eed3388e6440419779b81dd9027f66df0cbb8c165b86fb74d8f216f823c3531df813c363921c7b3fd97cd4816194d5c48831e71e368d76ee30b933033bf72a775ae7cec99d9b73ef260feb1c1e89ecc8f534eeba63cdaea72ea33a511ba5a56c3558e6e5e53678a4916f483073f4c56b8bec914dd1a091c7940caec16348833bcf603b45e2dcac109efb289bba8c8b7aaa42081b51aedb727a47bb82239d391aeff7130d2c43da51ee1df7efa562a132ea8ac469a79630357a566b65c2d294a556540b27037e0a47d411aa0cd94e768a3d6aa776429232a924c92a51ba2959629d05fea09c5e3766c38d73e73010dc6f5ce6186ecc061b362e33caa9e56a51aef5da72b95c2d897a42958172ea4631552963d725262c4dd2fb3fca81acbf62b855b270911eacdb40338b56b0e2c85c1629934e36e8290c9232998128199232994539abc791ebed76b24e948662b24ef606f38cafcca83ca2922073130a0b8a09c5c47a0b8779a43698fa7bf1a51287bbcbad36ee3346c7e199256b3f75192827541967f44ca867238a08f5940a4231a586a0a0986844444a9c943c2d095a32d4ea30ea274bc2596e71461300b8d72173e12db8806d60c8c9f1828d1bd74a911ee75629d2e3b8528a99e5c295b9852b330579d8c8787c06f60a55acccc24592e4faa644ddc4c27154e0905ae0f404269c904216a0122831050b2170c22d0b4aecadc5b647dd14c25b2aa9be351e915d77dddafa6658679ed97569ae4d5994d225484b9296282d59ea30a6b7c4ba2a25bc2521b04ecf6539033382903fd66c6d61c6195b985106ebde58adda6a3dd5baccf5b4750b0201caadfb740bc7e0242765eb190bdba29413da3b95d4342c5b763383a89f4097b6b002155ae7ac8a037bdcde6ae714d2283c1a0ae1489f2077b89025d372bdb38ceb564c89b8d605292ba39c6c4d44738b2d6909211ced8d8b6206454a68a9a4e6411eeb7c128ea92498eb6338535383a9a49264a6c1a39dc1a38de131c4b6a49e51e959e55a5aceb550a49eb584b745b115c1feba5fe7148e16a972282ca82b36149314c211c58485ab078100f1e86ca9489ce670ac7e662e7338f663ac99bb2a739888aaca162e78120220e4e008482451a20227243d81c45311a4112d95744b25b9c0b87c823f30df71650f5cf001a55a509633c8920b1b95fcc91ef7377fe714d6f827bb3242b945118e32534d5d411dc99d8a5357240ea65a489c3eeda29a504c3d9b791f75a56735efa3b0f48ce67dd417a8265418ad566b167b7dcded19ebf534b7b65831d3cc8d5dd41554ebe2d0ba2c3ccecca243284685d162b330aa09d39204d1640280fb9704b3280bf5451442d7c77662722aa3674f29a614d3f5384a398ec5a23423da7d282dc6baccf82d4a69abf5164e59613db59485a59c9325175920ca32e72e10b5c5975720648b2f3f42e5375531d6639fb923509e79833f33add3f7689d1e2873774c9239d6a5615da04c8367939923f6458edd87ce5cf1b11b73a45fe8b9cbdc1128cb5c823f312e57cbe5aae3119955736576d59ce36a28e5326b488b1c77d69599885cb91aa05c59b888976beed3b5e63e4d5d67d55c823faed75c0a25b5d4625fbc292b3258b6e8aa719f4e55e91910d4499516957a068411ed29a5165beed0b075a2b21b1f69d8f808b3b1a911dad0a041c3023af9af208f9bdf9b3beae49bdb1ba5b948d9cdfbd689d268bf790b8ff6e6376e6ee392d26ecebac996dd9cbb2f897373738aadd3cd2b76ca7d1f7a735d7ee38e34f2686fb71a3795347277f9efe823c34ec3e512e4d1b9834238a6926077b158ae8e8c907597bdc1dc972dae8045366d218426b293ae4442c98507ace49d2cb9f0802d73d1812fb8e8c015d6b5a8e2c9498b2a90b4a8e2284f284d93b576b2929798042d25f1648f7ac6ba4e663de6ae7f1fc803e62fef9c42ee3e94bba3cd23119d6c019dec7aebf49f4a35c11e307f197532f7981e2d3ceae4d6650ebb52e2c43ec6f0a27d74d1f948b3f3d146cdc790c6f3d172af786cdde53cb7c51d1b99bbcee54ebb1c4ee63e1343e61ea3c11c1e5b231de915b9656ecc1db9bbceba40c0fce516fc71c132e430119d0c8343abc8064e64eef6e8e5302c14f8e322a2935fb2045fb03d6ab12dabe6ee09291ba5a19ca8ac9f72e25e57e61d3af636a6926c510d3e4f51c943c0b2a48a325230042894dc8041962ba290820a9aac00e54469d4669d8c68d4465d98b630c535a49d8553495c8b639d7685a45016ebd4f6819065596f0181006516fdc1c2409cf8984908975a98f5d65d44973a615d7a534aa95136e5ce09e1686f497936a66c19e594671e5323aa8cdc4fd9b819b09411e5949d7ac601093a9232896493a44c661b9020252993200e34736f5007da756838a683ac05a1b27e0fa1062350620c32aca8bde05493355a2a89d274b00c1884da9a106a614494a516464334b2d4a2680699da3c20773823da1bb4c1cb94562911cd087562338522f72908648eee2c31b5b5d88d113e4b2eb2f09447d9447bc8da98108698da28b52d21fcbefa486d39858550823f6a0dc216fc438bf3e08da1c579d53d3206ca769368c6c0511ac6ec1642b905168a88e4165880e2044aa647948a11a664e899041fdc58210c7267ced0e18842d19a290a8542a1f068b1ea1d0ec34c61b04c3fc510c6ddc2dc2e1f41978f2df985ee38ff2e7f4a4be170e1f23e94d6c2e57d04a14328ede6f23dfc10041194a6bafc0c9a4394667359e3f29388d2f0e5e76d1e4da49994e3f27369324d2c3397b14b99cb984bd8e57be8e5127519d2e092239437684225cb1b0c65c9634beecba62c6fb08417b9e52f5c82b3c890f0ae0f417df51fa3169804872489993927e732b01c31afff70393ec1202f231af531580cf715576f9099960afcfdc5b701c361c046dc1bffa9363ec120378c6e6a18a135bfaafa96cde935326fa488911dc2b37ac86cf4ddc89024e25f1e7e4812d9618e9acfc9b9f716ae117a2166365264486c079ad7bc061b219ee6b286f321b11d6aaee33ab0113518fe1304e7130c0283d10b305c97e7dc9877170e54fb28cd6bfe4d7007f134a74364afb90487bc0c81790d8ed1a70e548bd1b30e54f36e101cff725c9abf6ecdacbd6256395fe5b8b11d5eb0bf70ece56a158d161aff725c1b38b6438edbb80d6cc49f03c77668e978eb756fe0d80eafe7780e6cc4fd0bc77650fdf597eae6ce9a047f8cc0dbb8397e7365edc67ddddca7bf1c17e79dcbf7c29db5972c95f314ce8dede0f217fe023642bc0b8eedc03242c759aa178c3e30c80b475dc74d220586e73cc911afe7380c39f88817962dc3d851f27c8ef0e191d1eb2f5c9c1b52d93c8eebc2d5915d1f54362fde9b3bc4c6fdc1e6ce1a77da26d1bdf3771ecd799a3b7367d39d59a86c5ee64e272a9b8fb9f389cae661b7616e0f51d9fccb6d176c6f8c73e189a5b61ebe8567508c3eb64378d6c3b3b04865f3e10cd282e712ea2b3c99828038081511544671900fc77640fdf32d0079dd42824fda4818adc33da5394ca57195dc484a5848597fb45972b691969ac9a99fe88d224d4a9928968a54b1d4244a93b226bba9b334d252bd55a26aab4335e8960a08aa7891852d787045ad6fd0410a9060d2c1192ca042addf48fd4e429238434042f298086b136d0e08df0c4f7d53c57d5fea16e62e3297f90e8b396cf5f05b5dfb960bce5bfb983142fb0abfbaaedb3bf6902df896fbb4b561181b00d078840b8500ca2c8c84465ee11e72cbc39175aee582afb7e0b1c8f3103299f5964f1087f02bbc3a78c767f02008e2980b0f0028b3f0ea2d5f5dfbd71d59977675f0746557e36a858f70d4c6dcbee58e2b7c8493391873f92306d71665f0eb2d1e3f5c5b84b5bc7e8237b4e0716698bb5c8237c0dc05e68e4764979683ffc0ebf28f53d9ebb23ac8b9dcb67c7510c6e5f59ccb6dcb57211eeb8fd0ecf2b1b698c2235076c1a9d7141e67b6a98316bc7d3d789405adedc2587de6d55bb0ea0dfe48bde5f32da9960b44eadecafbbce7dd3105ce7bde53e11d7bc82108e2914606dff2178d9a1a1a9a9999584c462626060683817979717179bd5cae568bc50ac39696d50a0455aaef4ba550a8173cda0cfe75976b738b97c1cb7ce4720b8c0c1ebb0c1ef6982b250e0c8f5d6eb94bcb250c1e6b6eb9cb5f6ecde05fdcec75b0f5f02d7f5d29c5163c7ef2e1e5de5afd3b6bb5fa046d985fddc33e0d4e700040ab151e675e85a72d6fb90f5dddce2d77fcde815fddce52f5f159056220529f1f3f7c84cb29ec5d8679fcd8439645bc2c64210b4df948fde19263e62b88d23967534a29a54d7b40ff165b9c728af3f38a36628b3de210e99c611fd6b7397d863c4c5e5a2524d47fb529f87a91d256af2d3772398e16ec62c11be35637c6e15802c0afbe83cb5bbec3ea21360274b9ac7dc491ebbfd59552667fb8acaeacd557eb72656dd543fd0ff541ea875410c7522e9f4c333fd007e186e40e4f76793de52a101fe7820ca1b49ad77112cda10f5623f3d5d75bae1fc55c5fc55c2df6d95183b4583f44488b4c45a20d77090659d9205476136d68eeea35e1656ecbcc33b1bbfc7599cf5c59dbb1176292535b7d28acd1faf253f20c01cb131b0931096bf4f3237db5ac3ea434a1a19c1a3d93b0b67abd3707bfc005eadf54f5d2789a1b7bcd753905ed67ac4b0edced815e1d98ecccf7991bc442113f6cc374c92485906b105411d47bc8cc3d89701a89a57090984b10212198bf2ef472997f54b57aae2de47a593fb5086dec331fe2729adb203577b946f4c1c7ae11b1bbbc869a4b333399088158eca33e76a5a0ee728da0d766e9f2d785b8fc45ef728b85bc2e8393f4ea2e17fcebba5ce6be72a05f5d08fdea282ca40f9ee66f66eeeaf05aad3cd55d5eebacfc8ab9aac3aeeb30acbbdcf0afdbd2b23a58875059fdaa257c0a4265556cb1bed649df7701d97e31a2f4f556d2ecad614b06a26f6d18d67b94a323919c9c1cb61944725c4ce08313e450e923adc8e6b0510d2de6743938b098638219cec3e6a5ad5ce883562ea83a85f52a1e7a4631106cc471785e944ebd03267b57792a11e627b9d393e97de48e9729b541a61ea553086308423f84de87dec7ab45eba38627844099b3a74a3895d9fa29515986f2ca679b29b5f8128706c99deddb7b39996b91be38a6a22ff60303656993ab6cacb2565ee7152621f4d1337920f212d6a2947ff691cf91d791bfbcc844494003a3b63d4d5b4dcda09aeafeb52a022dce1f5a363f41201168713e48a627326deec6b01329edcb69b941805775ef9893eb3fef12f90109a1d7230297b2fad49532084ae3f15c2408ca438bf395f372fad80239996219c6bc381fcec36ca8e6787084cb94d6fbe3414141b377a0827d0606fb6760b0993f6c5145c74b6e06d5e009272b64e4d8295b486125698ca42397294fe98412f6ac4a9e1ac6964ea8692e4859f3f05044f3e4aea16555c6161e26cf9436485a0e29ebcb2a994a22e41e35f4287e9c6a0b7994e4a961e6689b6b983c3e63e77a9fc953c3e481953c0af678652b71e618b9b94fb0c73c2277a6b4eff71001eb7928cf4b79dee7792acf033d6fe5792d9ef71944c1ee53c9bb192777e63d3c4ea1dc606e30a7af4ae234aa2945f4491b6ee61f9da73752dd287c6d31ec9b80f9fe0e611fc446ccf73c957dc3da85f5ca9a94b5ced1b93871ec7b4cc8fc95087528e5c92db20689dea3b38970b49943cab3462f52da3d8581c55c59a321739d54f454466760e32bd3b748c52954bbeb5a4657bbae4e1dbee1ef5d9de6ba4ee3b62e5413b3dfa1affa0ef3e07f65adc1fa8faae412be35bfb7e5134cfd1f0e52d3f2ee34ae1133074f738d902264e6e08d885df56f82a81445d1a756796e11cafa09fe1829528fc23d64f62e2f83d4d4b45c825d912042b0c33c663fa4affa9079f02d37564f53dff2f02d1712bee5f52dabd3e02433f3e0574f12b33887beea42a6eae1816adfcc1ec8c582842b5953c92cd3b1fe729f7691b9acc7dc97c3aecb61eeebaaeb3a785b3908855ffd5b852dd78bdee5854559414e65540aadaeba50cb41792aa332fa15a54c8494c200a1daa5834fda8c9ccc5355eb955446ab12a10ffaee22ed3a9edc1dd61462e93ad94926617f0cc3de407fca1d8ffbb81a5fc229eb3e76d9cbb5675689fcd179670abcac592ce47b128f82b12029d525689fa4febbc5425257e12460eac6bcc36adf8d79b86bd0bba112f9a32aefa694c85f55799ccabb367f9db7f26e2b913f7bbd7f134c80ea1f8ea112003e86c242bea7701229a8276949ad9ee26c9819751d642d0c12dedece764db9bb0df25d0759fb70fd97c246d0a7bc1c5e487d8885d04b2ae37287c2def5ba6f75bbb7dceee09554a9e54a5548651d97c44da9ff2e843e75ef9f673f6e8aac7148dd3bd52d771f77e36ed2a6c340701e50ed2f049f544048df920ba226f268739d7d467af464413845cea42f2411954fcf395fcf49f9399ba850d0a20aa18c93a516454d3ab2dce28c23647beb19ec7d7bf47d1fbd369f91e5952cb970e2084aaaa94918d6ec9c761a4e42fbce816a639875ce731e9c2416839ace816a232cf78d9d9ba4e73b68e01128d398e00f77c35322747911a810a2d2548333bca8618b1a5ca185119278e203f572339fb94f7baa098a7804cae22bf85359ac732c168b357365e62a9de1b85c6bcdb5ced43bc3e25817fc11dfd1c89c6bb1f0f8ec7a5bd769feb96e4ac9f53195e47aeb52e231ccad11965bad73e04f2b5567ae8fadd3e014959452cd2ddc617c4464d7290d75e2a271e11128674a5b2cd6b94bcfbad43673701df8437137512788a04ea8134a5b54e6d6e5ceba319999149594528be29394787a3aa94d16c58b7a728228cbb2e4c20363e44f7c0c8f3b72cc4663b1c75cc6fe12cbb1b3625e64625e645a2c168ed940cf7a0f7a56abc5a269d1d8dc756f6e582ccaa2a134766b6c286373b323c35e03fb0f0bdb32765c8fcd1128bbeacc515f670ed6632d3c1ec92d7a16115576a59c84ad4fd69c42187beb632a299522ea59ac2fab9d3c33f6d6bfd84d11a59cc462f7e91ccbe391fc7a47d558dffa0479d41411558ee114511821edeed416b64e349ce70dee30c377dea0109df3e0242e3aa8e9bc4121b4efe0243d5dd89c6ab41f568f9873d896a1e32d11d2cbd02ecf75eecec77369dfb93a6ff08697bb1eb3a102d1fa0c5db17a3aa57563d54691a410a6945254e83b8fa924eaa4c51d68b8ce25b8c30ca7e1247389075770aad12e41213bd7c1495a78426de71214c2f324530b255060aaf1dca725eba9a430266530b551272cae5622483995f4199a8a2909c231651bad53eed3f09d21339c47e7ee5c8209a09d0727a13b60e256a3ed5c9d53f087f56ff69474d5fa3873bf75592ceec6c77ac79a55b42b6b3a343ec3a53bb8c2832a41351aae640a0aaa7d13fc31a29d46c30da9ac3fc33d95a5925255326cb44ed6a96715a7a8d8327ac6a56cb694cd072e60c19522145c210756c0420928ca30d241154330420c87c7578ebdc34238525bbd79778530f6d86bc44e736332a7f9cc2e372673978bffb8eb1a0a3f1bee86da6c6efe4dd0e57af95dd7868d5495a59e397149f06706fcf924eb3d5a2ceeb6eecb25c8bdb85e5da8325a6c314525e6c228a7165be61af921e2d85dfe43731f1abb374e7365b6b9a3455d4131b548a7dca763ee48e485727aa2345415a69ec9fc1db5d8236a8a12e188aa82b282aa825ae21ec617614cc9598e724c965d64e18e6a11bd75a678668a8972fd77d6e958333efd18738a5b9771cdfce5e3cd6962e2bd94171f8f568c6199d3bbc4bbfe72658baef1e5f4a924eaba8da41a49aed859b799c147b83c43735b528437f848e7998f9fc83acd1bec01447dcd331808171e678e7de60827d30a418831cdc7187ca4b3eb3478ec30b81cc340b83ef3c38581a8e7f20ceb051fe92c730ae2c0e5eaa2d4899983dea75bb73bbdaad77dda4915ea2465abb4b6aaabd63ace5c5dae225475672e41dbe469bd5227ada36a8c21c5e3112ed38f1d468bc32d7aebbb89da2abd7504aab53ae9f0386766ae3ab5cdcca83ef30afecc6019d22a59f54f82387cd975c7995ddc972ba64e74ca466934aaf3d6ce0eae8147ab832595d5b80d95d99c06ffac7bf37b73161e6d328ae9f5f42d17be0f4d25d978833d6297f9bd33a3985ab8e298146f03d7c036f8ab2d639d7591f518d65f2e733d90c7cd59bf97b931548b9731a87b9f56fde27757bc3717bf9b7a26b3ead489c923b3ea923ac9bfc7249cf9c73f77e7b465eefe33b49fa1f8abae7a0cb51173991aaff1f2fb1b9bdfb0de3a8b759b31bee858d7757a471e32655116a52c1c9b790b8f311c6267b5cee131f390b9c7defda039378343eb2c1c62779d452b8ecde01f31173ef7cabaf434f8e5c5b2aeeb93f562635f5e6c6c6cf24b0d9b6c737c9b1a3117efcd8d1873994bf08698cb3c464646c6938991b9784f4694b90d196ce362db38bd36623e51145fff7d4c58c73562aec2b11f3ff7c7c45c85c3277843ccaf523d2676d71d6d78c8adca7d869ba1b98c0c9e99c1232cd79997179acbd0ccbc72a5a1b179eb33333777e1b76e779b3b12b1c9acdf3c7687f8d136fc07eb2a3cc69cf5160e9d1be4c1c2e3e4aeebf5ba3ad73bde3c86d6591cb669612062fe8f3798884d8ec1aae3fbd02f863bcaa58cdfdd91878cdf027f446bf3fff5b3ee6893594f11f5ac561a4939eafa98247378641dffc34984b5454f33335ad77d1aa5d433171e93645a730ba3a8fcd6538c52625d31af7e747ecd58f6cc2be3cb5665fcdab414beec19fdc15886d3969328ada2a8d4918714d32bb594590120a8a94dc2515a610222ad3025f9c11f237eb047fd44dabf1b4e40f924ba3b9a825ad633d46243c13bb05059233993d7769cd132f40e272a6b2478c7d3ec3d1fe011c2e68408caade80869c718fd1d58bee8d98e3076641112645a8194652e51824444c10d054528384201929426294b3a3a3a3a3a3a3a3a33cc30c3cea71548e2c530dc033426da151a13101f3a96966080012789b644b3421b9a39e9d0a143870e1d3a740000000088e13a96742ce95892e13849384a384b3856709870aee060c1f902a709270b0e0e721fc706325ce34b684d453ead2d421ba3082d4b5e729fbeb63c99b834a7a68dd12211c639a238519a8673d4331e49d4e0b304078e25356c38379c221c4d389670707070707070705e78e1851c176fe24dbce5bc86ad0611a5d5a851a3468d1a356ad4a022f76b4cd1420df007c93f1790fc43726ffecd1bf286122477a435e1b001fed0c268b16959a692fbf44743d5813c9bd0c2a065699910cda965688944084896881f6dbe117f531334b4c4d6a851032be123994c2693c964b2165a68c185d704d504d504b980c46d6c401dea7cf24fa6949c80d3e43e2db18400ab3890a714570a9287e036f9a2c67c8284d1d06bb7a2a32848539294a8c8fd9e2990dc1d585abcd2a21c9ac20aed803d355982e689e68c9e599a9b8d8d8d8d8d8d8d8d0d1b366e1cf6642dec090b0ea1db007fec6d803d6afe709eb2bd431cc9a71293e8eeb8d2b21d5fb4b8a309cc7309dec1b48369a41d61a710949edd7a8e7aa2f420f54cd1404f4fcf0972bfc78a1d67344d48ca10489e86b831e4b89c604eb024d8edffffffde8bef727239b99c8e7ca4353511c593a18b041317875824e61945cbc00a222ba05871b3e2e8088bdc01529ac9c9c933333333333333343434359f45479f0b4c647b6d6e9c0b262044408272e4c831313131313131323232b1f7606a7211fc994b34195330b480c42a795b3b95f8f454f2dd504da22b93c893896b34a38802d3c26882694d53843db69ecd257a369bf40c09115632033ec19e48300b1218b4b2b4c66839b59e8ee40ed0ebf57abd5eaf978b8bcbcbc12ce01398c55e06fecc25489060a3219ffe44a13c85aee511da26274b2c13d8048601dec021168bc562b158ac56abe5ba6dca0fc3278423ed03009470dcc124694d3ddb71a569130a1692a25ae2a290c873c9ed81d262cf8dcaec133d362a7be23842b887c8225915582c248f1048728708239ea4080d6480d2261413ec91ca7389b7a5473d3b62662791a7b45aad56abd56ad5d2d212ca40d28ec023c489e89911597a9684e6442b83f62481fec49763490b44443956c80590071da24374e88524c67eca19278a111089a049044b44881021428408112215a840056e9a5c54b6c02708b280000629f96b214a2735a9d25190a8ac7fd4e2909213e46f66022a6212134a493625fbc30f4fa290024a08443082dc07bad1c90404126732796232816232219a4c3c2071261327244e33f9c064e26432219a4c8826935b951ba5097c9c4a4c24dd6892c536d4012708400002108000042000810844200212f86442c9874f26374a9b44b69e1de1a467f648ee1011a5674620f52cc9949e1591d4330d50e95906942693a5c9e40456d0ac8872e3682a196232641ff00033c030c308661cc18c30cc4082196298910433b21431842288504411cc682a22084508a1082f8af8a288a6229accc832c50692033e4ea43c95d8042131c91da0a4052c60010b58c00216b000063080010df85432d4c38bc852049622be28a2a908a722ca28e2a98833920425114a3294c466461472bf0832748ea0d8104d8189a80826cb030f6458418609c838011951c89042060ac8b8820ca422252852451123328e8a4c51840445a8285254e4a8c811194845356e0af8386f432c61244f451015117480031ce0000738c0010e3064c810047c08a604bc0852915b91a2224745928a5029a254a44a91a522568a3015c142060c72bf880b6630b2b7c7498b3d472df644693189ca7a94a80c4a135148d1636bb1a5c03d4854b64411220943bf27a96748a8346d467104a5cd27412e8378413368c3326d4b3b9eb0f46ca220a96d5ec8d944010c6ef9c9b6949bf60429a3250105309033da11a48882a49ed19a5a7c02cd09b426d0c6a03181b6049a1268596849d8f144693b98284d0c1a126861d08e4033020d0c5a93dc994bbe63c753a695419b42eed39c3e6b7730ed709b77f868c520430c28881185dc372246196248418c29e4fe0ca242ee033d511f9a20717c7082c4f1e1096238e55e727d50820f4bf081093e8c61349413f4e41830828d86a612f643dc21c23c042c5b6c349f601f9cba6e35959ce80ed9da24fa3755473cb93e381189f134950849994e2e123c6d0620629330f40ff2a8c94004d114c4520e39e490430e39e490830e3ae8508013213a7221f7e189d27cc8e2c3183e384d2068024213189a40133d2be2fd09d87aa681f727e0a46719787f02449436a1787f02374a9b4f240189dc9f8012c92344cf92cfc09fd9040992b9e412fc41729fbe123c83241859669325486e076e924fdb2de24557488a0632f0b56a527127d12709ee7cf2398511179a2010e2f37685a4143922281ff10e5c231df84ce288fb70e4830d0810204080000102e4861b6eb80c01ee802cd6ce50943f1d54f010a480271e8690a0e1a4a3ba03821c20e4802107d8286d1201f9f0e1c3870f1f3e7cfcf8f1e303b8029e3e00a86693968114bb065c48595f4182dc2aa0413505d1dda189cafa76c90e58a8ac9f3cc13d4e5a48ca5c7226af49994b184d22bc438f8dd284a44c26f0913e6e8283984aae002c0af842014d0ac8a200270594d13323bc8842ee2b800c05102920e8e7e7e7e7e7e7e787070f1e9f4a6e8fcf25b74f20c5be42b60aa40cfd1db0e0f030c4e1a3bde2a60229eb5fcc33825bd18d8a2c7207a856abd56ab55a4d000210c0547251b986eb70f405408afcd5886430906247815b0652d66f81009e5c5153cd24ee270027f9e3a1b237039f4cdcc981cf28ee14c127d19d4f7c2ab91af85ce2ce26fe0128a0f86c728da47ec4e6b9814f2897c96b52ec27933b8443a081cf115ca119c5e7937f93e8f640217b46c0816b3404ab0931794d0a133f02cb48869ec4a54c259f1bb84d3e95e023b03c97984ba692f78c40ee08f17e8f087a42d00345ee1879bf470ab93399bcdf1385dc19e2fd9e2772672af17e0f91dc9942eff740217790bcdff384dce90181dc9943eff738913b3389f77b3e2077e612eff73821776693f77b3cd0632b9a441f7baac8fd9e5bd330897e6bb3896ba4d68490154a9820f7873841ee4f265172df8894dc3f820628a0c115b92f045227716b2518bab52a98b8352389d347726970945bc9ad4d21716a2468726b54dc5a914a887bc4351acaa94d298e816b34a4539b52dcc86482a5f81442f2a9041224d8684a816b4792062b90346841eed7b09032f4bb55ea5d9e44b77624050d9072a770ed16c595c24392e70cf2a8c93c9e244ebfc751cf8e78bf47941e4894369fbcdf2389d226d1fb3d94e40e11eff7a8d23323deefb1d4b324eff7b0d2b3222ec447c9450d72bfc70c248f10eff3c8c28349eecc202448902041820409922186f868f38e21be0303ef79ed76815b7b818fe1054e83db1243495cdc279062ffb3413589aeac09117d2aa1e435a43c93f8479542db4ce2f638f594d1b2b1c78681c33c75fb48e2f4a792db57c89e44b75d41b46131301a725a56a0ea7b21e5420b28ebc93af186ab346ca03d65cecac887aecc53c9957932b932cf105c599b4934c953e81acd279f485ca349f4b904cad112251f694c28784a81a3c041742ec1415349076520a63c7fc4952d5a274f88888c485284063200c5134b97080c0c81e448917717c2081e8378f72257e620aecc17b816004196dcaf00912888721f883b7eac58d89a6013b8a3e8c36462cb7d09dcf1118000134ab9ff803bc21c7047b10733b0e47e03ee78062c40c950eef370479802ee289271fb90034c810966803b8a528c2cd7218731886038dcb15b90fb04b823ec06206064c97d1bc0201a8f858f3b561bf0f8f90196dcafe1f2e028f70570c78a456d8ee096fbf91aa13e778405e08ed50a1a641741b9df73c763c173c76a033b838e1548b94fdbc194fbb33b8a32e0248d3000e8d0b194fb30e4d4b0c15ec0116fb98f6384b520ab09ca7db1e786d694fb37ee08b301833de9e4b1e7ba5c4eb9c71edb68eb9399597494fbb13bbe62516d9047c905d3d832c87d185750eebf802ff069ac56e4d1daa62779a447f4a8c796fbaa3bc2f228bd78a2c16d0ea140e905343fd0330633e09e70441c141c110785c4e9cbcbcfa7458ec3767ade9116f35c80a449171a9b1c4488dc110b9083949426b3c9e34c86c9e13893bdd0aa6c8b1ef8638466be5a5badf53cef67e2a091b4cfc81d6ea6c730cb6ea6e5ce120f9ab9b678f2d07950f20c60e6983c0498397a66dfcdd9b536c8f1983c03b8c116b969f55412675e0e60f280920093a7678ef99e2d79de6066c9e45d52ef56ee74f66ec11f0f485a256607644391fb5d734c70032d4d99eb543170617058284d14bd8f5c166e8c6c3d2fe57d9d67adb529d50ce9bbae9bdc39ee8b16b9a72c1746e6ee7160c81cdcbf8e4b3535f52c8ca609f166113c95bda8a7ae903b84bb01640eeeddf77136fbfcd873ff217980f8c80117997b0d72c79e0b5f216755b6a7056285fb0c926708db23797ce60e397e798c21733c99bb0e776f730e36138430352477b0c81def1cd7629338dcbb291c534d9449e27041ae9021930701320777ce8bcc7d9434c81c27f6ac3bf745cbb8731de6b0b4c89dc3d2d2d4f5ca0aa6ec7d86ddfd31811176b9f322941c1524169206977befa2744872e7bb2271baa4ec5dca1917852e4af65cf0e70329f3ee7d43c81ee794bd30b2778b472e0bd9fbfc985af4de0921ec3eca2cdd92fc028a4de866ef1d949ec95bd382b467a9ccbb87f2d1a2f75427d4b490764dc81da3a1732b088d5c6eb9a57024a293b9b77c94597e5adee00ddc2718a14b965fe1d1475e5d75479dacfa376f337fdff72e08e128b3d0fc3acd2f8ee807b9f3bad77ae8ea96248e4dfe70ea49e26009475965823faa96bbae91d9baac6b648677ac58e4d525a293270f3d03efbd879eb5dc7b047a36df75e00ff809651473cb5b7509fefb52aa4f25672aa5e3d96a8faa525a69abf7092594b7b332bb2bf9eb76207378ff3aee6362ead995a611699977efde2c027857ff2e91209e90bd5740e6f0deadf00f2d7a07af10baa52c462f4b94f6823d9b83f46cdebb01248f77040c913b2a4f02217b172277be7b36e760333055abe6a7faf25743cf52f7fe43f20c616f903c9cd304c241c19338c8de7d4c90072aa7fea56e4e0c39dc157104214c3dc99d25b983ba473d9985297b475d9a45e2783f5c222d7a47dd20248e5744e6f0ee3d0a50c81e472471bc7bdfd134977bd7e999bd7797db5969996771b7d4a2f7f9b15b0a9956ed7241ea26557684d525aa83ccc1907a621493c4e99ed59ed5874153369dbf6559e36b91b95195e2ce62a6ca7dabca55d2e80394689c95a294751714141474abd92cff4d5a9bfb3c6c4e25285a9cb1d4822721226ae68bd0f790797e5e1e3de7a511779f162716674bc2f99bcd0d9b396d6ce6b499b36d6ed8d0db48b1a178a4d406cfb40883d10331f16d60184c75e3dec6471b366ca0d9c6c6c6868d8d046df068f31564a0c0a0ffac97e9187393e9c7eb4919bd9cb5674f3bc159bb0e06b3b6bb475aa4e72e112dd20ea2454ac4112c84a328f69b9e11d22c83a0346b432122a5ff3a8ee33814376d94d6436a42c9b4071e86b4482916e87df49f7bb0921604953bf5948a31641a84b56108cb1e364a4ddb0f75de521da841962936283fbc8411ce3c2f3fc11b669e5eaa7ea8d48b14c7a920764ad588080200007314002028100c0884a201894cd12349fb14800c86aa5a70549aa8510ea71452c620628088000000080824690800a0172abc01d4f13fcd2e40017622956a06762131a3ace2fe18e493294cd23a047d84a6a9c788fd62a3165423bdedef2182ec1b29ee5aacdc15d93ddb12f9b860b489c1884c32e1ede0a140dc42e9a326d317653c555c0a1c16c1a99a5e611d433515e9efe5a184e92c8653e72ba2be26aa0ada6f998430933f39483e53d569823c4ce01a80a0c63bc9cef0ab43dc3e29e359111a3727a2bb47d5e55940af2aaa0535dc35c51daa2d19bd2df5759c2f661ca84b99cf4a77c25f1d72fb6999e76ad8cbb7b14cc0fab6e6e6de06c5bbedce05a870d25593fa14765bc2c2a1e614b4996dabfb9e1a9324af8cb739aaf09329787d99640010cf2c89cdf5ffa2128548023c43c39d40a658cb01dcfa8facf0ce6c9c3c6bbe35f0afff705c71944b72a69f19c1aeb98d4c5c327a1fdae26ae96805c9918321216d14f06d065f064414c0c5d12807e4f0b9732b4350e7cc4d57ac3c62c5a67ecac68510fe3324e6b8c1dfef134203094ad0f56811cbafd9d41e97443fba82bfacf537f05761db250459f0f2850869fa8632d09283e8dd066089c1a6c78516688bcacda1fa76beeb1e4610d9b1880a9d1365ad90ccd82fcf402177f3195c53d24392a8aab07dec33467da78da6eade77483194b523c7d445210b8b3b3725e2350ece590ae6ddb4e627a90676fa2dd4e2a7ac0a0a9be0e20200d3a10d58e13f00e68af70ccb525bac604a004d79f4060ddcb15faef2864648d056db064249d11a61a24b70dd482f90706b9411c28d87534cfecc7da3a7636d032a06bfd9fba2c1a58d6814761ed9678e51620c9fe9a8cc66f7ba14da35c591c3f26c05568841d1bde3c71fecff0bc8f675c6d85f3bcd4d0191c6f4b417e54827d10387a8e07f3baedbe0beab832ee6aa1e240257a9c3cddd9b7098e3d56a2f7fa7556ed200c1b31b06404308192e43cee30aa71a971cce4d57b77becc60ae4f829b259841ce4b9b3547e0878ea7478a59d11af1467249f89d2eec0ad40c72298b5b6a464171b04bf98cf382a41dc6832ff4e09fe4cdb267c6ea66b0dcbbad575290f7b9e010732b48403b93554e885297470131e3e425f0874354e781c777242a89ad3badb618f0bc1dd16f5fbb87c5b4851022276c7d179b451eb1aa1fe08e0e62e93331a93b37dacc3b2a1b8fc6f1c70000e509302774323e1a60abe2a1c2e225d18826c244dc344fe5f0deedd58a8e8f0bc9593a558aea28233c2afa68c5b2682bf2c7188fee75d256bda7005b1ab8b373fb040335aa83b24e120eb671d5382cab9e250700992ebeb4413cb176bcc2ec88d7e0108d64a72cf0deb04db00e63d7f00e3c27074982c04c47e1b6ef482a308ccafe0931936984371181e331ec2c69a68cb900e99d261d60719209d9a53a4f8e728e7c42f5a0b9997e278944f1de1b639e96fca785eac267762fdbdd3066e8e03fb029ad0302790c15068c1dbaa608dcd0373b5babb430202c3500e9fa040f92815453cc43a3226b40e53b0af0d17e186080ff2ba281086e7ca5f0d913a27575ed14a15434e5fefb8d24ecfbabd38715ff44322bd1fb89a7c3f3f02be6522fcddeb0483afc27da7308d8d7312c0ac114dac295f3704a5df5d051f238f692bf2a1d6df6c245bf2c82da520b363b0911796313523511a0e74820d9987194febe56ad54ce611decbaaba2f01c3a181c7146d19e9e7b85edf5849e42a7104ca3164df57e3eaf8159cdf795ebda33afa090e05e49b66e4bfa96273e7422d83608e2bbd45367e1325f52b7503b2aef9c08d2ad5819202dd20a3d47a3d6538a813274f71b2c3d0b45ac1c5bd62619941d2773fd94ccf6935dd575e5ba714dd564e7e5f2fea1098f131f0e97f023ca5e7a3615b6920e38233b21718b8c357dd4479f7f99fe1539e690aacff1b4a2949f4c26664d04a320a864ff35241bad13cff9d0abe272e8a15830b38449eb890b1316d7401a1ef61d5324d42cb2e27585cf73d95c09b82a6e23e0e48e911beab307d749e88bd84b5a79f9a8cd3f9ae176ec4241d77d59ea1e6eae97b284ab2c2b48980c28052b70d4a30bb1b00dd2a3a2830c29dc4c65dc4e6dd450ef5d5c3194860d7530d58e38b1e89516006cca24c20719edce4efebdfdf482d530b37296b9629cfe99d936717c9d917c004cd4c92e8a63f89c3e4c05d1d729d9313ba4a945a9ef64917a84b12eea46b189ef960913abecb6d643addb6092051b29512dcc9bdcd5457105a351463bd234e424c0d8263c4f473b101b5d20749942e8569fc008580526f2b54b068f78f5f9c27b21f22587d974b46690f59152f9873d8841703de22271b3770aea08407c0a46cc937586e8679eb0a51f47fec7916fa2e3c4a24301d725147ac72ee8c5059ea93577fd208ea3acd08b976ee5c9a72351f4ddc5b485868a8797f8bf486647d4f7839d44e3f241d492a8cf8510a018a9d56696ad0ea324c61bf0b3a407cdd1a9688542d72537ce8a700085f6c20324eb6373a8e4589679cbdeabf6a84fe059b1c2627dc8a10f6dc39e19529bf7d5a06eeb2a956dd05232451839f8d346c94a15d187491d4e707c51557b324bf9acbba175b7b4a56c503bc34e4a31d215e837dab95da2a7ae65adc1c1b62d24a53eade0dff595c02e4cb98f751a1cf96d48c57f19c78349d407e8939ae0b0d69c49864630222b36477eddd69c75333c5be9b9595c9af90dde6b88e1dc56d8c84c7d656e53015dd3bdb39c5dd36a79f896dad9f3b2ca042cdcdd8a66ce8dc7216cbaab659ebeb075f14686bcc468d310910f0bbb93e285587898e397cb4afc8f2675d687531c459b61650a42d89d72efc74b3778a6104351e7b93e4de049631aa0b868657ac6b3711b29a6790c0c58636910d6cc5e840d87adda11e561ab8ed54b3070de3689bb24542158a1d108a4509a549d7a67637b9acddc769469d105e4d0d17d2828e63eb9b7e34f5f10ff11523d7303bedad1ae998369660f490a6447b783c11520ca4e375d140469e510fcee0d5cefbbc2d91d76c0f7447bb0fa8536fd88880503129f69100a63ac00c74c9c279d85c9f72c0c79be4da98139e8c67520db8b28c64efd6960d45e9ffd4872b2a560795c230ed161cac6dbfddec327080a31ab196d9494fc5846d0c0724fcfa6122f500ae4c85618bb87ce3ed5ab2396a33b1302a1db91f4f48c12955102fc0d5851be6e2b986929074df8635f613eb783444b946c38b2001c381bf12127d6eae60beacfe6e9cb1113c4120d301fe541b8451b04601cf959d5fdfe7848b643e9985cf540784e8269f1b197cbe8b38145603cc6d07579e6e568097db20d509e62834b4ceb6db6130f90884aa291afe00efb68019037769cb4c42c72f70165c0158a69e735282a06172ee8d726109ecb7ae00feeef472e11644d39ae46944677402d9240f7c3a9fbe24db1ddaae570b30453f03ba3ba7a3c1cd820667ad903b3f25489ae6b978a8b61d880090876758ea78f40af476370d69063a30f434c299ec52ebc5b94c5103eb17db2db0c28f6396377d6f86a7e13359d1c2c7e499d218ffd5b0791bcde0de1f78d928667b27d426af8db939bb6acae8efdbdfa4662438772e78401e64cc695a5a8bf83dd6595ab71acf78f3d813b8229d4717cefb9e840199fce25d20d5c1f1b69490fa2a6e2f06df73a0319ab1acd4d98a5a0e7bc89c63f5c0172818178def69655e2ccc0055b62620ca85a29f65674848050c225579484a6692b38049433dba550709e2b22b73c03481f401828f61402a5e7b81a2c7fac0cabbd90424e966e819967c4fe4a578d76f02e15d19ea93c4243a7f3c04520f905d0a87dea3649f4d0dd2e142091e369ee14c26a0e07806954e9ad9577f28df70efff9eabf2b3c740d84fcefcd590693e67194f534128d49d2314c62ab8a91eaeaea496bee82f689013807e020c42b189a3a168bfaf0312f8b988185f2de44dcdbeb48413ed60cd086ace5170d9b86f3f63c24987cfaabb848730d81ab060791050b99021be5786107961ea8eadded695c1ace08d7583ea115a76cb1cce364182cb2b5b6404947ece9fe2918940ee3bbd6f1e6f6c5d008da3fee3e4547bf3824dffade5b02c1c3ded508f736468e15d761c9eb547337344f3f05e5fa59aecef68b66f2054ed6a8491b5923780b48c8b584ef106d4fca3c3f79a27a53a1958f176150a3cd39da3419e0889875d068746d90b99c781348b9d89590d2ec3b6434d3256e2aa253c6a5332de52adef232ea4772d03e9b61edfbefdb2ea2e120e5b9a996184dac804ecdeaf1b14fab0c051d256063a814b0ef6bce415c1745950a1e6c0eb2f0dd89fe3d802d202006f2cd4fdccde4078b81de5ddca403bc46101bcd151d05606da956b804748f86ce3568645607361a8592e8cb63268d4158a01809f182e5c0a9b2d8297f887e7caa05786fd64afca95c14510125cf065348dc818cc36eced93b4055e31a420439da1d9183257061716fc32249e2b838eb06041a8768e9ce3d1eeb8ab835f898bdd222f420c5d3031e5c1c4e0be2b43345ca4185c822d1ec1e97f9c4ef1dc518c5dc2c95fad91cf728ccf65120bab666a2f8e719f461ee91ab66a006d0edbc4aa350519b928fb4f2685fc04b80b7c54a09cc2bb8116d66b020e7948c90921de3791ec0d1445e39499e9d24cda43a44aa7f5f004671217870328710c943377a2a0cdc58edc99708716e43b1969155915562216869b4fc6d873af77adea67bd3e6edacfe895617b594098289a51a47cab5f834d3e8277febb15d8fdc69e5ec863c8d56c975abc2cbb0c6dec7be57f4258bb832d46a8dba0e156f783cf8d90bdb61b3aed868ff65d57d211d97026660bb3a952851c30748ae8d50c74828288f72d40987501418d60a9f255ad543bc016f5e8bae928505bed6cc270d2c5f7f41c807e459ff34feaa564bf37f763d7c3d16de8d57f3411949db5938264b6d9817ca14f8062379d91308e805c819e169fe221a9e8b0f217b968086eb1e7fa2d3db4b387c8731cedcc4da6a87ca3e2fc47435d36f5e947676a27fa81814429b6cbca607e27cfb25c4e223de91265f1444f61bb32341376cb6d8a5cc4f21c98550deaa90c00b9ca03f6a8c3216151db727138e483c4f7cb03ba46cec58014f72ce65d5cf3f466e36b188e2dd8186ee277e725e92299031282797034c5156a24ddf024409e552b7ac4be3bdc89ac74a2712a6e975201a24d566b496541c22c131e59849c0a9ce1b2efab7804ea7b043c4c44c03487c99967d20f115e7a6a7ad24e370550ec080b6e8caecd0b9141abaa6d43bf1ed5615ed7dbd09598a2046afaec8f32bacb07c13c712f098188f494409a7f0631e668e5c13d3f00c468e984c6d8933e4b4b66439305c10dee2a72163f30fa5465f8e45fc3bd046872bda18cd6675880a0049e0f037d33f506f45fdf1dd087a16fdc27e801bd5d83a4b031b6e5d1b07056345bf0911595f5bfe1d40232e8886468437ba2dae53163d71cfb9016c516f430fbd699d6424e9bf5e3a5c316ebaa649d3a9b36facbc9c1488bcec16b5bc6b4d1f63c1e575e57a9114a74c545a7c1557497d6e42b5ff38e32b589a00a3072244fa936f427c8bf094bcfc0045de42e641bed037050ca47ca06a5be66e8850d1dbee9c74b8126003b51b80c3511376d347ad81c676393f931243428a20fbee56a35183e09dce065a30033e4793d5cc62cdc815dcbb1d1c19c5326d4a558f7a90f616c84d807ff219873fcacece490d75fa7b5a6930f58a3d55ac182eb1fb219a40d4cb970f1481a07dbccbbfa66a8b04db937d925741f6e3e558fab641d1913e8771658b94ff64182cbc915e883cfef6c1aa792e2267520a0f73750d13af19dcee2df0dc2435d7d23f5002618af9115ce873f936c4d5b60d9605a2b4664ca42cbfa567e5c8d9c9123217a1c709d2fa4b9e5a6e85e1cb37831000837a6eb5a1db30c3412c9a78f4ffac5a216b5717d237cd0dcade4d0d88ac709dde8b9a61433748e1c65b587a3192a89e9c280e9230ab9a0fed944cbd7ea2f638de910a71f83bcda30dbfd8fcb24d0fd15ce8f8cb46d4e36e2ef00040495655c5fbb7b19c7ae1900e689b44e4c7afe1b3852460f0212f368632298977acb52f07509d8ceca6a03bcb18759dc61c4811b0b631bf305654de88c1d56263f5510362885e395949e5743187dc2673033cbca76d74698134f949781024c7c7305f3b8851c29f5fc7073e088a5eb2a07fe9cccd2ab3216bdee609f6a0a91d0a5b7686ae9a33a7edb6f831f10b0d3da199a053243ccd850b6cb5d127dc412bf35f92062041a6209826db917d0ae4dc66b0afe9662f13672fe000fe8fe60e43b633b9f60253fe744b18ddd89678627e93e05fdb0b9f0a0d9902a56f7e4a64662f6a32b5301cb5ab7c664476cf1ed23e4760d267f23f5c4812ae3e7e2dc61bbaa8e89db817159386182e9f70a2c6efe0de3f43137100118576f0069f3228e0974166769380d33d04881006549ec80ffd57b630aaf865e132b785f913f982197f6dc7504c654385f99e1e31c28575d410b5f3c45869eb06d1bee51548e11fed3bba15285cb458535be8094d18a4bcacb410c61698c09335a8e9ad8469b372be957ef2bb2a6163b3a023d92f1d526fc981b0efabc043ac4c339a4f2da0d59e484de9cb3d3bfe2c871e32393e989759afc109137fccc4abe9238235b5eadbf42d64e8ed8490f2cb20bb51244dda3c000f1eee95883c531ffebddd1ff40cac70cce18ac158c220700b1c164b21380c36e222983487acda18529720e79b4d5950bc3d644978409b6099b2f204e9d39bf7e401af50ba3477d4d84fbb949d6f134ac16508f71dc0a59db8338ac791ab88c1ce8a4ef1b3723129664e48fc2e7478d99ff4e0f96228e27a7d112d6e921250adaae2a2618d953a4b5dcafa32a5204b8d9deb64b12e88ff3e0683c62b0a5a08dc6f42f37ee10a885e5fd9a14cee51734e0eb69151ebeb84d818d9e4b92913c98c0e9f89eb5a933db0c5b53e9e49aead822022724e270db5e3e2b186e5c5de4323748655fcaa1c5981ff2d9ad987d4a49b245204971683ed3ab044d73e5658b9373eb5c9c08a7ba74329ec1741a6ba503bacfd6a5cf5aa6bd1569f34a7c89b5c7f2a3ed42fb725a016d34aa05817eb8d23947b508b548bd2d12ff5acb31bc1c5f992e5e6ef2dc546ecd8ac465225dd698fb3881704b10899f6c8d44f9dfe2a010c943867f295294d8c0727c4f689c73674a363abd6e48ba55aa2864d7737096027c21a9c8a6aa1ab161645841289dfd05bf751dccbbb58c27d224c92deb7a5c2f6b97ad9e80fb1a3e04c09471ced019e73f4b4d7190f6d8d3d5b74663f9363d12988594e23a394245a70b6357e3e5389908527d98ce5a3d86809b9022b894686e33e7cd71acea200916459879bc36cf58ae9446ebad11926a1e3a7a0282790cdc091e18a99a3295a0d1668e902d5d68806430eca0ad785ec0259da87f26ce13a702b9c01bc1de7dfc596682fcfed3428de6802eaccf49b75434a3dd0361b9f2d4607fe3f6c969c5fe9fc4160f93f6e2f57a838c7d8b64dfa341350d4f6ce6f52cbea6f8e8eb058b2b4b9a02620dd98226f5fa20fff8f9422193d6d01121687bd114a76aba3a9a8fa630adb1ee80805944742f1564ced865bc52c30234bcbb551db25399101a65499ca8c795ababf17e71c97a925a4d4a5a981ea8704a9780175cb0c1c2da186d08cb11790f5d6ddddcbd1ad95b10b48fa4e2329b180aafb6480f2b74f847f63779b6b2cee22c2ea239d57de0a828a36820ebe19c81077342bb126b218873ffdb96ed8e750fe14053104828003f77e3a6c3dfe011e0ff60ef2ea1acbd7c8eb6e15d257d9e1ab36e71a47c6065f2d20f4cbf74517034b0fdff376d96110fa6b78810e891ce575204e09722801a991682374e3142a2cd67ec2c71a1b38f119cdd67a4cbbd7df5107513530540c983eb63aad9a116987493fe12fca1434b357b32c8dd1e4a9b850f50526eb59b046e99b01b379ec09d92fb66bfd03846b21dac616b3761d6b44c1f0b294772280b6486b1529a269d8e009648d6aae2dbed1727209e9df12147c59a9e90a009ca68977ef3acd227512926e09e62227a1009a6621c8ca8056e41a02d369e5a55f4f64fe063926981f643c9fb91d28fbc15687dffeabc24faf03c3f9bf944f328691e1b9b10cb8cd0ea81e4df4ada67fc85281216c6d100e97c50df501778d1bb5c8450e81b26992ff360be4d21a7c140a06f161d8ba62c4d984c6d836f58ab413f5e59f7a4024e3b8fba6b03b41b95e53c261b8bec47e4002d410a59d28391a03550251bc4484f780a0d7656273d08da63d879a7955f3e10fdeca53f7838dc7acca9cfbcfa4cd7c59cfaccaacb7c7dcca9cdb45aa6d7c64ced4caa657a7dccd4ce546da6d7624add4cd5667e3de6d4ce5c6de6eb4aac5ca97bcc19abbc0701f50770c9eb16d54c09dc124fc30d4377fcf0994e89c00c2e9f0781c50c40e0a84840b5d1a5bf89ba79a70bd43098a8b4e3edf5d79bfdb10ac1641f74e1129ddb060926ea7037e7c0e72ea648fb8dde44cd3b48dc163524eae1c15f3b7487d1c1f1e4ecf27b0121d47faf58346d5d100449c5a2916b4f3a0f3f3b54a6409e01dac771d6317c2c67fc7b2e0d503cf3b022dc301f007edd884ed1381ca27692c221ad91d82123b4639a80c0c58a3f660c1e5f702e80ffbffc5828c270869da36f07499bb44b2fe1e18f10dcbdd3d72542eef34c8b8e8350ca69d1457a0cc814f1ff78d68954d5db542dc05d2d1fd6c9fd3700d52e600e16ad14dba30fe6ea97057c1c9094b976bab5d0b0f2af495a9e6c10ee2496d5ed63e63b728b41a1bae66078b350189ec8fdc5a7882a362f9e7c558e953f0f945f45834d78f1455c0f43424c44415899cfd60f3c8be5d92e54a3d05f381e25c861df5f9144c51d32216a99bb745febf68dea64cae60a41af005839d6a24c1978fc9a4c57c4d99d20f3726a429c6b325665401b21edb2edce3c4c55948633ee2f0ed5e119d8637f1374a1873db9ab10ec11a85b375064bf343b51beed2d4d66eb89851e2175951b451442c22508d5568c5292205fbc88e5e2df48e524b92f5fe58e65b4bf78446e46ddf2771c5e5d703f40d2cb63cb2c44850d4d80ecf25e502d49afc88c6a39cb8ac4ee7e6450bd4a49a62153f9836008bc1d1d928f059f0301099532c6932d44f34a095d5324770b2f01663491e28ce4b2f060b22ce67e95ed5328fa466288986cfd66af63d794a309c2c7aa63cf6b3e52cefd365d1fabeb04959ea25c13b6dff81f6d3eb5265200c78329e29ec827b4d1cd619f971853842db49ce03d090d4b2d9498fc9d610aa3694c884ffa614c3fa521be7e02e76ff8a271727bc4ad859237adb178b8678175e3ec0530ed133e1a17656aace16926475497a01139eba9e0c5ff22adc17a5cc83ea16863543090ebd13496aff468d5c5b1724115232c9cb0b206b702960b6a55a675207e5a1e8da15c20761d8cfb1a811282a767d59106b791d41a8d9c153f1ad036ea3eff15e83b45df3264c51897094684586a7d1d54cb3952650b4c929c23143a6e07fcc69f9d151796410d234694c115b991c87a55cf133073126f643272edf40c2af8b92491233247a6aed10529a85a83ff24a8ba853506b379d522b961889b3d4b535343b55873d63531a57d3521035151f80cd067ccac060539313635aa6bbc90d2a923cf172f303917f17d4bc06582aa986d4a29de90bfb17f9c665bce27c5125188768f53b3e5129bbe28f538322feb4a115b32b806f9648432a9679c13b4205e9de7d65185d6dcecff43cac7e1a8b009077dff6ff9ff291f20f2cadea32698350ae08163721ebb15586cf6f1f2fbf8a2726a9026cd916fa54ff4a990d8d1dfe965376749b3cf22ec1a796b22a00cc7bc39ae69e4e66274cfdfbb0111b02e0d676decbcc229ed0bc0e10fcf48f26e9b515a0442d010b207e823703b08e935055c2614cc2bc231f909865e8f34ac7899ddf2d1408528949f23f537deb1e0a29dbad8a1b65b3b0ebf1291555601b1b91905d2e80ab0e8700ca729c05886f409fba420d2d152b2e6207fb88ecc179fdc676e27c273f29db44988b50938d00683bfb2e886a18b12cc52c50b8daece4dbbcb1935e5377823fd3cc6c84a95a2a592d9bdd0285f8eef558e541cd4287b86c630e00c4bf3759cad3b6bcff112500aa5126180fa81227983a2e9fb42f6b1b2ebdb24c10d87e191b2f71b0d2ff65ae5ca4a59068f1c744c2283ab760c3275bc3bf06a14111143630a8eb1322c361f390d0da5f918c5572a6736e194de65a28f9c56c8990f1ca144fc02b012e913c01a4384499d220cd5ffb4897f470eb83f4b7563c648648a43cb9d3635eef446a08685831d79473aa87dc0c001ba0a0d9a6d60d733665cf8d536f8352833b835bb9d3f4fea62a3fe07b10dbe1125ebd61e239e3d2512ce9275f402dd320275945404f9064793426768f951b15594535444e70370c09f1ae2ee4b5673b568adf511ba604112afe175f323ffc823408d2881948314ea2186aa2652cba0f32c6abaf122a7e536485a5a4c01eea4040615023372163bc3fbc3fc97d48d4c5a8419c3a154b8d0a88bca55cd27292771e77b093b87295d0898f22ec549bf15cec8bd94fc1a9576e9753dad5e74800a2be3374cfc2cad20ba4e38e24acfafebc3a03158f83c6759d4618682e5ab9d132ab646d6420913f81f7ab1e603c80ad85f6c895470f3c5bdbb86d984f43099cb320d196eaa94568351a9c29102ac3b0545452bad63f264f107c7b40e318c576218daf26a385d00903eb345ddb33d9579590c6970120eb3f399c374e4c1532cab1cef33172d86d9cf52c1f1a748134fe52481e22d783e5a241610c20d349e9928a8f96742e627c2d3dc4232e7b7af44686f18e344a0388021167244303bb4bdf39a2551495c89507ff2d96248776431207db618293e8a3fed18b562636a399eb6e9a532fc40546077199a34a31c58c73499978830211d08ac806177198d23caa2817ec55252b17a06bb2744244ad067568f629a0920352315b5e1dc03c3734c756f89022ff44e8be08888454b7d57af2f4c6aebf8c0541415a5d44a38450701a7b2cf24d248a115476ce20a959a7ca14643397f8872a42827e6c9ecbee194e81f10c1d2a47a533f1b1854497d3ffd21eb68fb3ca57fc153186b2a4d11024a130f03ce853cd9179f30d77ec4fef007abcda8c7d0dbe1a8af5f595d17d4ef0106fe387cb04647ab57dc70a051118a14a82d2aa2b08f51f5483b32ea471d176e8035fc9a4f634c123f641ae4ef855a24994764a80ba4c18525b82940b6a3f2ee18e768c9cf0d4808d91ec82a59ac7bca0e6a47173de6090e5773ffaf95eae10781ec9e75da6186cca88bc9202b25a3ff0b498b2abe216f728f257ac98a3bc0350f9621d9ac3c952df9e4dc6cc24d8c09efb680df13613bbeb5e983ced30ebc2d64bcde023152e3456740a5d7f20f05bb960f393e453f502e20434ab10aa863023308151afe3ce8409ca050c81fadd09fed6ef7fad05bddd5b79cffdfcd603e695f427cc0a409ffffd2c27d461b5d1913ba5e4fae705072c9c997fc27f8243fbf92592ab1c8da694d9175d3995a24e96b7fe19f484870901938b296c15dc02ecf62ce71e48ba70874fccb9612c55694eb4127af3a04d4ef9fef9620c0e99122a2311a5ec00a29621a69a658b32f05fa4e972ae09b3de07b37ec29022746a70a2d921fb597537c74e41282265eee3ddb0edb62d644d352905823cea61f8f4d7498eeac9abe3e8c3832e9020d3fd0eb1075fc30200fe0e0e4ccdcf9a90d8452ae238ec529741df8f9f54ada6ab144a63c280acf2f26bfc019a53e9bd59f6059d378b09b14820895d2f504f09dff4cc50bba6d2692d902b03c426b4800a842629284ca9e5a55950fa690854e3e4445cc83f3f44e11c7baeff3f2bd627f6890f181b2afe229f5aa020b60c15785d57025c1934accac1592f81d4c7b5e34cae8541eb79690e3033e6231b97e076b4cc00422aea885e078f249f02e0306a61d2271e5c99a0f0d881408daa99ebe850e1fc97093a5d946adf5c8df8dd1b8cb6bc1716a6abe8cfd6ca6ddf89e92f6ea2572659a76149761a0c11d84c031a589dc338797bc5b31ee9a6bf88fe21dc59e09bf7eb450595fbf2d3ed61a72d90206395512c2ff29531e77e1bc98bbcfc08016c802cc301f5ffa2e85c08b1dcd96babfd368a5adedfcc400e78a28885e8b1423182121646040317c9e8cc414142df0a52738e8925a1044a775181fa9319d69ccc114b86bbcf2db920f491b9fa1c3af3a46e7091c4278bdfe41df74c6993c53e9da75e6e1904dd80888ea2979a2a7b84dcc2d35a47d1b0ad5b0357046b844910cfb0092b716fc2668ed4ab755cc2d2166bfbebd2d5abb1d7fe15ba109bc1278e852861821ea4a9d03cbb76da9461c248fb895c27323eed8a5af21e5b382c7b694adf2ab08b72d790d7ad77e8eccb0be08fa44add49bb38c7d5e16cfcd87df0484828d4a9704b0b86d632c7d401b0eeb569d1dd9e655d8e264cc31f30b5a3c4a228171c37c4ce127f9a24125f40595d6be88cddab0beff2db040af2f4077949c15a22092324278b64eba6a3cc47b7521eda09181e67270ca84f6fe0097f6311f973994933672334311f3a4f0549b42c94229b54e8cb3dad1685b2bd65cbc7ed7e427dc0c180fd74b8c841cbcfb3e3c1027275b1af548ab1bdf144d6e7896b8476b422af398990ecd740531deab3a9876be7082d6bb28f6861b516872a11af2d279155a32ac33f40f2a0bda0f4c32483dd7b81d0c54f577cd33abb57e11147a127f053f5682d2d64345d59f1bd1bdea41b46d16ac81b16740ba7de8d44db4b0494fb127d6a805eb494b106e7cc53580c50eba1a316229054b164051e1c54b072dd79822ffde378ee2c131ce8a18dd7eb69dadc6fbcb97e7ac5fa1a8a28bd85ee6e4a14072722f5eb43682ee921206442fd8e64bbe82631f8dd0b885afb00760cb715c58263361fa6ff9e6bb83c0cdfba0dde5a8a4d6d264860dc6bb02a20e4ecb60d112e621e97a34326ecfa93800ab3d76888e37f88e1dcab501caa579099eb559b8e4dfe9688bafa925b279059d2e203ee800d99edc0e5615ee823c3e79fd9a02c4ca81d2c917c6917e9dafc3a9851df7a55517f4e56d6a0bf44b6cd2df7c47f0e2fea2ffebf364140c9e11602de0ee977aac6854d54ecc3befcb153fbd516f1b523054af589982e50fe756f0f4303d7e0cd625516a443f764886c7800c465b251f7f80c25806bfb4d34ecffffd796a6185bc97439142ae82560647adccba20eb0781f4d835d0b37e99920b12f2b92d71d3b932987da4bdcded28cb26cc699f980c26fb3725b0c0574b5360df8ffdb728a89922a33327a88d7103b2ebd2a89761748c92e102de5658bad92bc5843613e9621227ced53cdac9cb4e92b50b28ff775e47ae1aeed736d05fa2a51e68ddacc5fe80675458d51d94e6964f3a4e10f662844fa86ced2139eed0555f123451b526f54612ab638f6a639979b53b4c2f01f23a412b87624c060f2dd6de92970d0f534c9432bbb2b03d4e4eaf7b78e8170940b498f236592088c6e7075f749f8693cb97cd0eec6742c8867015b00016ba3138fe379dcb9701c4401aef263659329bb93a35fddeb0a392e94ba35cdbe6e94e8a776ad4f50eb57cd87991580b3882fbfa6d21048b59fa68f607ed30647aac5788fc9127479746af424f551e4204c00fe6f433189117cfc070a8374c4e6f285b2de2cf24835f8f04f736adde2dc221344b8b093030b875b78e331e8d570d08e86cbd81463869499975887feeb539a01a85781c72bc9884581773197f815e2a2b37f2105cdf9efc514468d122f138cfac711590a62c331a276bcb71faa78d02622a7eb07d0a7ab92215a868b4d53298fcc50ed4cc6ae63b52481dfbdbe842c8698ef7c0610fd271c1edda77bf08e64356aa5b4ec57f88160a63bedc28801e3e5478ae44a3f25f80bb024ecfca2ed818369959c260768961a771567b71e0e2a867a3fbc0e50e242233a84bc7affffd0b3cc6f097056adc4b3d2af80e63b817301b366e558079932014bb0005a2474f74d638e527899927af8acd61eaab3c1f9450dc6e256c486d3e579822ae141c50149ed900b2b5139a12810d143f5cbaeef7700a93e12dba8683e041fb46b842d233f147520c905466d4035dd30cd277211703ed868f88d4ceda3486561416177bf6a26aa1152e62702ea6fb2e0e79705e4577aa7b3b84dd578ed44abee4885db3f5e9844051c42e38173ebc38187416ebb85edf32dae51aab866b41395b0cf650a2af7388eeed6005286e09e9fbac06e904ee9d7d114c423bdb54d936f542cdbc0f5ac7143cc03dbaa17161757a5370e24d90f19668da4347314ef96c363ecd62b1d560d67788f71a88e3fb78a059200ee5c0547b5eff8d54f107c478b6f102d1fcc3b479e1b8305d6e195c8ef0387f6ac64e81187827db41695747f26b0117a8f7d4b8023b1d5a374e0f48d0c91382b286da5f90af229b4da73e97842211c50d799ce7173b7495c489d1697a661113050544cea5a025652a0c585a267ce05bca6e385f1c7fac001811dda724f5eec3bf78f915f40f2c328bc7013ecd8ba144f4752008b623b86bca5cc9f64afba926b62845aaffc4a0215fa36563fcbc84531854463ba709c5ac529f61cc1a517cf6a92153f8b1f3bb279c272e320b9f7801afd1a2aee4e1a6f54e1ce54fe91252bcaa902089af0aaa281bb2051e55f4f9b1d87d081fe1e3de720bb62e39a51ccccd044db061e41e07ee483a38f51a3c548a7fa8b7df3f325988b44ff10a03ce8a8033f3c2de476c30017659a96d175fd02a7d845c406e361150ec317db30c98fa014d972cf9a40d61010af30be0c44276d0e04ed95c118a6dc5dc3bb9cbfb2bac063153f1d087fd43940f06d44a1ba106e258cf397172b5807e60d8b47a2bcd4e5ff3afc46733096bce4357a0e09cf553bc63eef65d1e0b626409d05cc0b0b4a1c858045320e5d830650a4a89926fa1041a55a52c6c1b66b3d67fa88c1214667afeb01075b82d74effd4fdde4b35c7af6bb41c83db4a7a082e52cade69838b7bc81ca5320e675810710df43e33f0f8b31d377575fb8839299250f61cdcd624fcaad8d5ca4151f5625127af2102f0875a206c4e0e411199ae1f3bc486107eda2fd37aa3fa39c963d6b6283d7c4fa8a6f48f791ec8458ce4a7a0c38f08f3c5b84f7023075ea9ca281df1126f811446b287303248a7ad8e24ce790623f7eaa7350d89a1dc95f6d7722959e2edca3b8d78c3a89f6ae184ec9e0939c01876342f6da5ebcaa0c07fa5fe704fb9247672e903caa34e4ec0eb2d6a9ae759da0c37626e2da48ec512a4ecf03b1876004179130fcfec3e05081d8b1f95f5c2538fb24bae175de500fb1178f90a1e58b8bce89de3a594a36a33f31a85fa1aaf0ff269a276e14d4153a4d2ca37e4517af5c77b2a3acc61db5a2c81a2b4af2f8b7025a13252d7592968bcd9b1c45970ecaff4953819ac65a05d5172732c105fa7b8099cb17c4c5294e331450fc4d98f02eab0337661f61201b6e0b265893bc698abee47a3e023a9866836fcef7be6c63a613f9c7e6c5d039140fecb43479c8a8118a6d237e9553a1cfa451a6b2b2bcb31c58f21bd23642f5f00951d157dec0ae06e63b9fbc6a831fdfe5ac18e41821cc4a4e69060ccb258a5e2ca601dc00b7aca1750fa989547a00401db9f1eba2db3ebae94c400429a0787ff83775ea8f5571a59ac001fadf2d5d93e046f1b0b4237f84c19410d84505107cdc630d288a9ab78ce9fd41866b0ab613fd02aaaac682ad145ca670fd3aa81916a1376132187727846180395ba36eae3a1f1857adaa4fbe5f7821f4616f66c21937c3fe460d29e9028f30eee197bc36c71a02cb473258f6bd8519f0a7663c8bb13b96cc8d6eb0b2611e53a4e2f0b1bc077e10b4c6b50b4b2e60e019df07c48de9dfdf048f974bd594da7d4d98df93c110d40c2f883d312496793b2aa8bfbec8142f9f9f95b1eba196b42b11df8088c830fe1affacd072061b9b17c7b0cad907390dcbe128254b351910aa464a47e832cacefc42a96a349928306edebe407542af7ceccfdbd7b5efd82538d55d1632cf2fc97c9ca2a78acc1bc018d14abe0d495f661c2d244e6e1a00d9fb6284342c0a92f7fe08377160d9a1c34fc2d96b1afd4c9371672412f7bf3636e7a2f89d12e30267169d4d761e980160ba4a617b405f69ae3db087914e9aea2ae272c31db90fddda8b43b2544a022d21c0caa4d2a1a85af6f186788bbac6138353d67472c1504037895062708df2941a359068351c6f520e952c1254379199c513c8503d58cda700cb308ec7a13d3796f045096bee2384b102c15e0bee2a540cbc8f3491a1705465c2293166d543d397a7490ace4e5cc89db74bc7d118515fb076a58eaa14f9355fd2ae6316f6a880e201c6d9bd12c72ff9b8a6055b23bfc6f387eb94b3abf0396050015503f345533c4e7c0294f61a8e205b505b0efbd0129ab687729c8e0bce27fbd14a81655286435194ec834f8d966a391789bdf793e99d9efd58b5ec1922a177bb274856743a38be759e63ffc2949ef80ef21548794669f5a4f32a9ae37079b5ecf204c7790d04169044568b94538f03ce187ada18c8f0a99b4dfd3ea6ff630c0a181cd099ae7d5543ad5cd08b3697fcff6275b1dd3c55f1105b0c204fc3d1417313e6f1079329c181435779fb7f4c7f73f75dca9607da546771cb45adbf703d868c6a8de616c7797921cf615355d38ddf8c1da0671b4d95bf60ed0cf04c1d9d39e1c73ed44cc03e21f444e5cb6a4fa50fdc2c01feff344bc084e2eb1356a98ab2229012eb036465db43a0ecdb50e645653c74e4e478bf2c93707e663814703fbbe691172ea6453d8e754684fb11a507645c0bcc587e656c1bd1421f8aaea5458fbe1407cc22fd141be4a01fa45d970c8d4f07c0e34c075de22a63dfcdc52575592f0b049df1358e765f9d61f88e64959d844a55a66d66d8fca872ca9d7abd64902a1a44a3d483b976dd77b358cd38c16f474765cccee4f67fa00440faab374d1ba1f594419f3d3838d9060ec73d71005d191419a4ba53cf9a4526b0a5061021da2101d92c62221375ae5a18fa1ae6a773018ae4ecbee351c3075cab429c2e00b80064ab6f2676ddd4d5264394c8aceea0f8b218d7a47a8c8ea4944165db6c352cd65dedfde67fb912797fb8d2b95dad0a12cd7db9f4c1e70178e20ea64b264a5740a7ecbd0ae2fd261e5ff8fa9b9dc326432ed3e105baa968c82e2acca1c32dd9cbcfec42e26f9483cef87768cacccf3391b2793e68f62351162beebad1712f401eda2ea1e907c95f524db5c0e1cc1e7d4281e06002b3934fa9a6e5cf02e8b4cd041a4d43a1b540b90c110d6b94261da36395a7994770300cb19bea17b269605f634f8ec80600b5f8668d907bbc8ce13e45a90f054bf682df4bc4bb9404914fc44629a65d1c5bedd5c26eeff983c215566e0e14d9a0ae719305fd23d39d8121652411d5d5b238cc0a888a42002f13fad2784640b2403d8935f7b3fc76886a33e5f62e0af51b7bf6f96e3e7aad1c406c46d463716c749a83d1d886de96b54d6a9b95f7bfbf2d6bce886454a04b26ef840e1e55085b076b1e1a739eb2d390d44887f7cf728dd2c48d71af8300eb235249ff32be24b8893ff43bc6554d6189d19764a0c35c2a1ed8aa9c75b9a105ec37208e0f2963001564590f1a89519a866c27b7ddaf3c263b9910b9b3afe73730c1be4938b9d680b4d93085edcee645461dccaa39a6488ef1fc134233171d5e51edcea667b2d8a340d84e08712711d4abaf0e2b0db5440c48fc1fe46add2fb5f35f056181b7e7e5bc214e5aaa378bbcaae17441e977eaf7cb217d868ad9b367a007931e489c480a4f34a3e1b7c2f4c722698a078c7605b1c5116b02e55056239f211833e76fd5647f12f2d281bb20883689d3310ac1cfc497b9b796037925b596dd798207b6053366ce2fbf54cb86660920f925d9faa5ec1869ff382bfddefc58e88bcbb8f883c03d43cf99d8fce2d17e8a9a39e1d98a29948b39ca6e82a297fdebf162557027a29043ced837efdfb09ba0ddf98a23a99ac5c444d8ec51a3f1de9e79cbc5f79bc9322313148ff450550dd60e1df9e90dd757acd1ca2ad1eee82f9a33b6228b0502d6502ae681ce576accd4d7811abeff5907543c4aaf64e684ac435e6a49842d75b9f2ca2e61c423bff10e84edbfbc45dab80385c74263aad09c84b5a68de26a9db7d1b9643009b53495748488b78ea1a5c629b56f4446fb8b56d19f746a14165ee82b4a9bc0db43e1b9b9e1d2e4dc11d490e85120b4142911e3e5e8f1a097026d8dfda0ab997b865ad86c14121b602e4f756230d0a4a0c4684faede77e40d13781c08ce7fa1f12557f45c422e93a549e95a63b09245d586ebd18f182d59a830461465727b5b399e97b1caeb8278eb388a6202f5a3e2f54a294c19ae61a4c9c99b81240a491904c7d96199b6bd69d3d96b9dd3e1c4d914316b6395d3224d66c474c279bf352d21ea694ccc40872d2a2340702db0a2dc4d2e88f563a17fd519edef01a257d90ecc2f4204098e37e022b311e440c374c851fab37424962a2a64b2dab009d5cfc2d6e5d3b39a6166aa6b8cf29cdfa5373453e6bca584453856302facf016e7ff3dc145c228c325451e3b145ec29cdf3e904e9d92782c9885ea25ec22522f112dcbccfa077c6fc829f93066582b93d3018f1515cac04c92b72011797ed4d1ce5c579c67eb48636eebd6d05610f2e5fd6bbb7800ec778ce69a43c01f63f703dc992ffca69652d1098cc2895caf32209fbaca20fdbe6b1c7a983fc711ace6397516461cefa9f196328414a27257fc26f0e9bbdf03453da006f3bb6944ff622d9f52d11bb85b262e5110652500da2d60a045f0e5518f6a573e3ab09db66399dcedd1990a2b411d2d9426f52c4e5e2020599a580796aee691c00edb96f7d16805e7f28e8a83680616234d06583c0854d55c6996ad48c80935802f4e29200bef3cbea57f775cb17d8854d936d4b9681b4323d501e55953b62c1426c44004ced9ec3e3a512575b23070d8aa0a29aa0e44151c46e94e5c9046ec325d20228693949b30e4f18311ba8a186ba085692f09d546ff225044d1710e0202a84eb3309e13688113575fef0d09e52619e02fffd4d4abf9d2c10c9dae08eaffc71d7c1e6e71dec3a2ca546b05144aa2c852769089522192bb4795a03e338285b742a14d07957d227f7029fb2d77f9f2aec98d1eaf8177b251889581203eaf24292af9909d289dfc7290dccb5093da209c34d5407ffa4b882fde154c35e72bdb2099e62f69ee2b6001ce576193f518654ec3729ae5c06d64e96fe2ac1760ff1fd7fe6e9eb106352dcc595628484d1c1930aeeba71394eed2eea7148f05afca7a801493271750ce971ce4503695e9dcb52b10a949059c266977b71f2bdd8add7f1a216f9a7d97780e2abc34f21c222e1b85c73b9cbbca18ef6f36e99394100f50a68bdeb7878e5a712ef46b7aab966af1e66c851fa77171c80380d5474c6e307587539ff594d6fbf16786831888b21502318efeb1807a0977f361f6f6854a523c7290367b7178783e1509b02d8bfb935b1149dfc89f9e1f50d02ec4330899264d764af7ba2cd08551f7cfbbcd33a5e4d9569f47ba65d5cd791e498ef84ee551d6157e4d8db9a129ffbe2b72b3427f5f0e87a9c385922ae149987b4153ca5d05c91e607fea7b0b0c8d2f5c83ee11fea6027996fa3b02fd2e05ddbd428815b6763d9cc14543fe4570e4b6a09fa3950a3fe4a94fe9ea7468384bb3552097a3b388f7afd117c4492511c0f083ad571596c5455c12d1d688b49af8c779828c96f15c3524a9317f5fdd3b9ca888dadd7e430ec0cb12f38f43bcf6b90da8266a41451d4c1d7eba255b0809656e21badfd11f890b556843dd828b6addf7bc1e6806c200fc41f07b2a494414531ce94420027c0449e1078bcbfe853e95986ec17e4266f066bb6a13d5733f68a4a1dc74e41d3503896081e4718532b6d027e9ca6255f1b9deeeaba93ed17ed59c1f46f9175db79246380149113bdc3ec3821cd1defaa916f2bd9dc967e878e732deb1bd62a752e01fed0102910ca62073284a088dae6e2eaa10fc9290fa8fa07d24e24cccf01884b53fc3bf5df72f85e4d5ae0fddc2ba3712a28e3e2f9eede0d69db00cdf745e7465c934f3b5cdf67222285d138021fc56e993072064294aad0f67f246467fa4b65e058144f515ca2c1cc65993d2d9533205b1f86911c4534a5762ea82b4a9efed98b6d9da162493560f1d13c2fd957740c4e337d485159217009d3ae2d5891f453519dc018b059905b1bb8e7fc66824d8713358498238a8bc6c9384f60b63c20865d9c2c8f7aa51cef667ed871840e61551c043c046e6018900e89cc82128a4817eb81c51e0dffd99f58a6f1d4321847b10e03360afd0ede1f992b028a5806dcd017ce7613e34485d6db84155219abe1e33e6f64b95ab8b99b3e00285481002046de4d6393fc90803e98d8fc6165576f4a4255de06ba20cb5cacd3eb643ee92b0adfb74f670034c4090d4f1cb4fc4d4d471449da4606b0a3e0189e6396c0d19c5243b03c4077562d27ed7d1c735f45dbf68cc758e42beae75b8f0580ee864b1cc496db930dc27d39af47bf06b6325829e7e58d5f51af620a775004f84372afee20f3afef8b6734665dd7386457e6ce4590b3c1e2308fd3743a88f1d7538827f9fc60e1d442c28ab60ccfa8c9cd3b784d7ef8d414ece50c49d1940c0f8372df7f3dabcba6a0f382d80f149945022b20d36247f563bb4416c0876a1f0f46c246bcef86b1fa62714ff216e4d81b5a906da1721027090bd8d7c52db7facab43a06fd5095a93ccafb9aca7faee48430fcfd59159c7dc0e10d3cab797b8a003c3a4025137958aeaa417c713aa1724aa4f3f7e0a7c8d8a0bac86c2285797b894aa48e940acd2a207a2ce5352a669a839db1783546f4f6dac1a271691594cd2ea0a606dd55037775bae3d82b59e6d10f0608823d8233feb8e1ad28872ac1d97d89b0a4e20c96cfc5fcf74d191315947faee0907ac439d970bbb25aa6411d3aca4ba3b0ac89313914d6aa317d4561fcdc49714339b41481194de487006b9ac02b2c16f522700325564c8a8f6c75373e9c46786b6564a698c64572dff7ff2f2f12cdbc3d4c1f223407221bf0193b86b17f5b6a13a61f927c465485464bdc2b4879c4a76ec94cf32b91ee441b9737ae04799cae69c071e34337a4b396ac18545389d1bb6069abfc5980f4b0c8317270dc1a68438723d9107fc5d6297b22d87ce6369f48462931dcab5d54da92054fd8f2689920c8df18088e78b8ce51d60de25ce7a9acc1439275599474cbc9001352316a334d7569b970678136cd3602d5318bad84ac78df31ef667185e485f65d74eb6af35947af175c71000166facb1746de0843efa670cdef78e6fcaeac07ff427471f26ee78cd92f07b277abb4f993070ca14b0aea0751dd6951c9f6d0068cdca347162383f51a2506b750c06602f2d5457cde338a8e03971e16877cff4affc370f22f228f64a410faf13d804b206060410221f1633dce439008b1667123959341a8af102e30b3f8e7aa2fd12d7d2961cd61a38958d4d96f5a306f427b5e7f64405bc5e12598367e23ec81f8ede0c72e1057e884c56a7c5a33ae2599f2d799807d6a9b66891012abd508a4fcadda557b8c49bef835fac04ab8e0a62197eb79f03d636607cc73964147ef316ec6bd64f8cf84a71f06262e30fd72a875cb800d68755d1f8a0b90686894150e3993b869a031909af44cbcd71cc43f3996fe980da6d5a13f5fb0f6b7f6a4b02e36368f9b0ad91ff54de641cdab2be4755a20f9bd943acf45be118fab0814c693464170f06e9c3fae2e6d4aa19cece0ea4b0873db5e3097c7aaa7b9687ee28c4c0d162112cbddf6101c73509e069d22960a9a510d9f201e7da004c7b6a2f45e5aa3002d72d8e1e6a83562ce7d5dd418f202826036058107abf8b60429b923224f5365655b86cae460dfc36619e51bce0e80f4892dd70a1d84919867ebaf749c27c6bab64010e537cd62d5ed17ee3e2d16bd40b8dc3868c124af2d8ad2ef1845a72d5d91255b356dcbea3908f4c4b6091e1176b43baab49d23fea8ea2e6386d18d773d55c9641d521749223b0e834f213510231cb040cbb74b3e62ba7930929cce0dadb3fecd87dd2c812eb22ffde1f47d657263021d0442e5fca2e620fa81bd06312eae90f903d2334aff436c846908022d583ebd39de49a50110a8a0722aae31a8dd8238cd3ae497d8649fa4239dee13746a6d2ee9d1dec10d5f72e8b82ad3a82b70fd615a42ba4b6fcf19e8c8c604cfda1f925c6ea009265f64d940f6f818efc8f39ba44f59a76718aa5e205e1dcbd256cdf0d2deeb522a2e29b4249fd90c775ac3911ab88b2cc44f80f668c836d674eff8ee3903ce96b99a2c32c448b2d86421fa8490b0b1430bb824ca01ca640b362f199658232b5173cfd051250b89659bb153126f4e1743d31b9113219970b1f8e2dfacda2f0207336c589f1789565e8386928f73f6b1720a9a59853405a4cea5a2d3b6bb78510cefdeecb05b4804bea1eca6b846b9f0913298546c4fecb20668d768214d1fabc9714301d72dbbd44c218a33adfe81cd9ba9ce5db24b3a95005dc24f2f70df47b9be7223058d3769b11256304c6a186051f064c608f131131320ac1dfa7506c83ed2e9e38cc5e7081646944145b56516b37e2b53acf86881615afd21c9cb3e6a040304fa2302f154f79c0a666c42a8dbb4715e1c3567a60d6a0f614e7880623203c710ad8b490b869834d331cc86ea308e90b8f26c30801d3c5e819c083a9b32d79b08ea7ce945c2048eb4ce44e3c148a87b05e971b691f2790d4bfe78413a162937aa0d941a72dc990e4f877ebf6fab5a98f083ffd551834ba8e1dc31f7cfd020356d11e14b960007e05a6ee00720ddcadc881c356a69dc92a0b9f08956e6dbe8e6e765c68b80f70bbd90200f7611a3ad3cff8a5d38cbbdede2716d784d873923ae65aa881803b62c88dbf4683a7a7a193b3877abfab5e1cfc9ee12f60d9528d39915f1b696014202e5578d0327043823276e011d5e1439fe5c8502134ae2eef8336f4bb1d3e34d5d34d1aa64975502fafce4002cb928bef1eaadaafe9cce1ab948557aa7e1cc20f41fb8cbbf39e0ce920aa0726a7d4362ca62509a075e8033b59c40a08a36dad9e7acf8ccac30b00f9a5e0b46ec81729340646577bf8a6853a1c733ab5de4c66d4c40d4a5216dca72cf2b32b45d54a98ab738e86b01022c6892684ea81865ba4fe93912a33c447188c232f6f40e4dd80f0d2a83c111bd813f410e1ab7308a0967ce1d2cf46cf67c2db437d22ece3b0a31e1c678dd48469d7b74b258a44d285d21eb46a7aa774e428724e21dd7842ce027a9b4595dd8f0d14a73243d32468e0dc4c1509cbe6515ad30f19bfb2ea419e7f4ae3f12d15e7f7709b521b8e7bcfc0a13ec4c54c4052e3e9a7055bb9d117409551e800207a408a8a20e146488935a4b883cf5a486826f4abc97f0335c307a77d001177f70cb4591b5683de3c1330b69db45d3d0ccc7b91fcf725a916b8e1e0dfec0a99582ba2d039925c4e288b141fe847a50e8cb1a780f2299ec2cd8b1ed12767b5f9f337d85869f23cef6950a98b5af85708d1cca1fe5e35b74979aaa7ef00d5f46148631e52c7068a5ae8f206b5527aaeb91c5246c657bb95afc741e185acba79620101f8db1df9b7e37f03d7e1216bd721d5d63878b2d6620e4cf8f126526f7408fc869e74239c5ce79fd9e680733e85391bb7830800d44ba9c8c6ab7837a11e7f4d7e1ff51cadfd04386f56c8655d7f1a2e0b0468ccbeb5887f7ba3c8ca7ac3d8bc0fbe652a7a3e42a03be353eb2e9e7ac6059a47270eaa3e49af4b9a42ffa615078ed8a650ce561863c37e68a44139f486ecb1dbe0ab41f957554f6e05d2536b6392108906f415a961b0770c6c6f2029a621a8d984d822b45296a6f58ba2a83f77bb287852b2973d7eca888e8efdaf5946b747090d2ae6db6f597e82adb6c4b17411d9b48a43ff52e50c2bc04a4bfd883a0b868c938cfd644cdf16c4a20775261831e84664ce8dde4a9f2ffc0d70d306c82d4e43fe278e32977c61fbfba1f285788997c968e900fc1159d877591a07b2c216445736b4ffaee8c3622d2d36a6bdc675b7a41168a2aeed9ce7af6f24c48947f2592988b5509ab85343f002a10fb3d379e941bfaf9507c62b6a34e152253d38f94934e1486dd9a16e3ec15b4c9fa21a821ac044ff95ccf15f9178011669d5568ef1fc8745a21748c2ef6fe96122e374a4043af06191d41f9ea2763d4c3a2c92580e7ae4ea72886409b15e2f8e4f36619164821028b4b2cc30eaf2e130bf8129b6ac2f3e2cd24ad44a32b891519ab08ae4d5dcc9a537438931c299153a57dc4023114c637ea7cb73c07d73f2169316ca43de793336bbcbc88122f2d31048f091dafaff17be0d15abf108871a2128908589c3e9ce2a03aa1d272869012f057f5842591d537ddb4bd4e64a3ca3a83cdadcb2b9ad19419a46e328415be0cc7884cf5d32f4cdb3538238c18e5339e140843e3d293d18f26ea8e2dfa9d87c182f441a0e6320fc739174e686765381d7e870addbc3350d5b31bd80df889850ceabd1f0c75dcdf91ec0e302cb9dc0a9f82c5d6b3698ae9d5a9a2eb41f9d7926ef610bf8ab1e8bd182ae8dbbdfd327f03ceed2c0dba8e2332a6d2cda7bfedb5604169e74f6a863741c55d93f04b4213294db0d0d9acc515d2e1c941cee3c4e5760234a18f4b78568140bcec3673c436dee2e6dd9b32f4618e434d66c4af930f8427fb8f3c04d977dfd39390b0b6bafab149cb7da79f4bed4515f2cdbd6cec321ca3da91a3ccb81c6b87e044f82c2e9eaa7d53c11355a851ca082fff00e7139aee82b86937c62f3472eded8f58235a368301d7c18e66a04a690782928e913ac663af97753267cae21fd183279c6ed7cd93e115d976c445c13888a8cc2a71c2ad58949fd69c2f31c6d9b3d3d987b15cc20bc10d17c8a735e400608eda9eb2600d96dc06f741a86dd8edd8a0b5619e61845309e2249089105a68787279936260e6fd0d9e6ff06c76cfcfcf99f02e9614e9c7885f776e48de83345f68fca9234adcf506082ca3a155ba9355fdba3de697b0309bcd50b09fbf58698ef6cfa61a8704669e26819dbe1f760d50c828a0afec7529a2c2c40972cb436d64bf0b5b4ff5a43a9c1c001d739e0b065a79eb3b049d795c11db078381a044b3294229c04880b0db2dcffa01b2c46033c780564f319f7b417bcdc6bba2008a5337d64b747e9d078f0fee1c74cf3c6629b5fe2be1400f141b97610406f4c330e70152e5c7c312f193f3747a2caace282461a20040f72bb34730e06bd54a2d6c3df8149e77d4b3e2dd03b2120d5a1306e80d46d2faf1cd1ac8dffa33f57b9d78dd6a5e4bee917804acbe84962f0801c95598491596a5829913b2a58dd3b94122f99dacb0e69da549793c38ac8f7d3b45c63cbe113db9f94cecf54022cbfa047e36ab09ae759799bdf7472b1f86c7c4d5bed54ff7dd90994d52cedd6014ac48be1847ac94780f3b2b968023c96f5de29d0d75426c898f364bb491462d72fde3ce3daa22fb2b18d00624af40f48def4b9d870ec022d0327a3cf64f74ec07b65a1ccf097cb77bb751cd454642fe5cd60bc04d0732f31a169d53881417bee789c21c38bfd6b16e70ea2e719c13036068c40cd1e82acb3add98cdaa697126df892fc55e2b168fcc6146b3c55ee42699ca28e7f7530fa6c7e24c391c71d095170d01439b7d18de9d1a56991739ba849617f13465e38fed734555ba0fe6805f587776011ce91544079ec722b1ed388086a68262a63fd23c5fe4358bd167558ba1297a7203d64bcd4bf46b21187fb3b2c2a5d9d1dbeb4be45106cb7bab74a2c92cbbe494a6c435d981c3eee11827a654ee2a758edf1f926a378cff7bb0352245ca5d2c0c0b588e53cbf23a9df4e27a83575333589dfb2a83339a0d2fc00ae74a37de796e885a3d556750f242cadcec86208b7205d3651fab98072eeafb603b31d667f66cb5bc986993d2a723c4873e19e023eb416b9baf2e2cfb04290583f05c899dc3a1b63542000227046f957be02b41eebcdbbc02b70e1da967560a23b2e5838a29200061f6c9a953ee4cbb6397fb2524a1c5084b100ae10d686204aa635a09c9c61d1fa25cdd8d133e68ac4502a8ae2c42cac90896ae081ecc2880246f25ef772cc9ebc2c5093094bf4596ec4b76b3f2b5cd40974acee7fe6dd38b9054c7e659b95678bf7a2c10adf852bfc965326063ffddc0c785d2ea0984d311f4c039cc6667325183963dcdf361f4df91e04bc30ff77946617de2701811155e8a9beace0dd9ad9d6213a60efe764342704a84c1c02f3a668612ce7cc91a8308f5858297aea7358c4a6ebdab366f274a56c155642a275eaea41b77af0039ceb9361d7b9372d35431a414aa2b76b09003ef55453d0e342a2de43292b973337a424619ec41d294ee5ba8b47edd66e7b7f7e3dba290d5e21658f869fdb03ec70fabb6ef676084cb2114ab106e721b065c0fd36a71b0a6c242b1835c7a13d26175e94f447544ed889068bd1039ab8a60eb2e7eb796dc695866e56ed9347d1e22530522439dad5e0516564d8c118934347742a0e5a5122d20a31ff227869a6656b526dbe2da227882e0a4c2da458345c89285b05001f6dc98fc33b539aa8b3b3b020d7df02496058040ae79053f1a11cba673b3406d049d96d033e1d8de7d2ae7ade23780095937d89136c91b681103eccc84b244de07de0bb839d4f0faad0f1f66b41db780c47da089d93dab7f97d00100460257e0f2e19a9580871fa718bf648f626a45ae93ea133d715893ea6e711b139e8eb148c0f65c349b96e58c54108573c8e172b010660ec2b67eddddf74d2823d33b708c4b3e39c7ec048e06c055d341dd07514ce3d10b0c342cd49c8c6345c6f78085ce188abcbeaa887ab3dee6875c456eea29f74db4fcc9452d0be2d3b7122ce6b9f46d41279b25a1a16654c7d6ff34c219633c3aa0105444216f4e3712e1d717b1b46105200247d5818f9687f12507e91982f5d33505712427f40863ee93e5fbcc4eac9238545af6cfa2533c8e2684dc1ce93af4a517e7b207f54fa963b127b35880f05c043cc56825ef3d0ca6fb248169150906288bceb452588950678dd8ec806fb0590442de4781a0a30a04bb377541ded2760008cf1e5159a1fc68a3446318b21a9162742e0d84c302e8b37b5357591776e28052139c0a7c96ba96c1812178000399dfbf5a670a32ea3159e087a01c7e5c8782929fd22ac2307410d145b02044ba6efd48b7ad0b0711790aa06bc4f859511d7f45cfade753b3544f6d940f0d5f02803eefd32243d23744c42d8a68a631a16ac098d1885daa2d782a10b00594394df5db78d198f08baede4412492f0347a0c205cede26808a582ecb85679aa02b6c24b454bffd5ba5ec5751b8dda1b6ea6e81ac6e80e0a76ae81557750917e7d62d5973aeca0483f253da19f4375d09500090ef37836e61b9abc4e8f4f97c419308f26864f1eb03e1aafad4ac73e66d7ee4912ce1923e9842edf2367531fe5c1af8f60860658a7e199c37949b17e1ae974b9eb1740bb9cf0fe2db6fac31ddd5e01777efe40e5b37d97888fa55140beb5c6268869773240f8bafe12cabbf5fa75af5ac9857dfdbd57f2235aa2aeb0246ab1cb0eaa6b2b7896c7aea26da7289b2841d315272d670b9836c676bcda4e09254dd2e76433d33df6447f8194ce06e27559825eafd63799728e4b4bda951653046073f69fd09f83a1b573486673b97336ed3967372b49b81c5a9cf2d4f823bae06621d54c91098e5472dd4e9a2d4b47e60d8ca8875bdc303c76571c1600b65071bb72ac177b964b9c1dec0b701cac3e26c4335d5ffc3318815338d4c96379bef8130d380e0a6c1f6a111cd684fadad2dcbdadb53ea5ba9c55f748fb579714083d0a849d205ee18ddd17eefc33a70720cca538852e70a6a1b203d17c2ad810733174907d52f5c420f76da02913e6a565645b46a325c73d0af7a3678e86bbb75a19bf03e8949e6d4d9934a38246123ed8f5ffb1f750c85a6d7d4e561c44acddc57f08b30406cd4f86efc37cc6dbffe3294959377b36f50da22f3ce52c4134a060f9d3d05fa56d2958874096c76c81373342cf14ee3060c326e77b6eff19be2cc1f0518bad58b8151097a98c10fd5b5bc0b42283c9b0a9022e859aa2e53682a10643e4598a724fa4678af2b55fbe8b915fe2f6268ed7b2ebc2ee3362fb6a40f2c1238d18ae6574d787adaa9a4b1485eb537c61348669aa6ac6f7b329ad108fc6d1c8b04c6d39288d5804e934469a24520bd3c6de52e43c41482d9384c0e2e94c95e19941064e90766dba2ae87be8ca017b72d2b38441dbeae1c9a18b8dba66a884b868176d425821875105a3a9dd71bc14f2fc87dcf096672a6a64039f8b76dd38fe4e5953874527fa980ee9ac28b81e411d715a477256ea55ddfa97111c528a017fea5e6c06b52a9112c75b8158a1b03c73af3700a7cb0a3f3179a1a23ba071216a86d2d36a8afa34a2b5457c70895bd802c89283ce92a9dcdd8963090b9f42026f05e40c88bb88647d7696a500470f32b0944b121ee88aff03c5c898f4be9f97534d239959b063128116bb66bb2673b380e20bbb91c100ad97497196dd44789671fe23e0c1bba99ba9539998a7f201a8b237aa6ceae9d97417357f780fca8114ee4bf251557c5cfc4181d7f5a1b5ea279a6ce336186e42fa30105b3948e29fd2da0c4638231c1876960c3adbc727e8fc7f54979934cff6dc594165aa0df31a6192128b9382a7af8c655dbc0af6836ec47d939dae2fb039c3c87f080a1ded040477025d7707c135e3250a8184e68bf7a051fa3e4b2bc88eff829a75373c707e828ed0e2078306983ed2ef315e7ffbaf81a2f8c59f53b8a930af9f6a42a74b61ce714ace5b612924ae7a8b616cebc1eaca31a8f41fc6de09d6a281b468b5165f433a20069c4d1eaaa5d784388438cd89f334d04b28ca574b0558a97c5d588c01567a3c632edec5f93aafb82de3b8049841256225f07e6c0e9aaaeb2c616c47b445faa5ed0a7a2d29e1432ca2330f5ec255c3fba3c49dcaa2b8d01d29351e823ea57b5d4e3b3a4826b73a9d4f1c514358463473511bfe62715dcf11abc63c082aeb8c14a029cfd277e4bd2d1f7b236e7797b6e09ed90c32e1dba4abaa9a7528f0c6c8ecdd9c7b708de5b27b45c07e2bd1a34144a45670013545a753b2e756b7bd6a667572d7b523ec240ba7808b046f208548206346e66fb8b9642dab6609b86cee2e7b4b59c914d7365f33b32af5335e1dae92f753bde20cc24910b0bcac2d533c288f8bc36d955f6091eb946d83e11ff78d8e90eb66f9f7b29573731393ba9241eea5a9dff0bd6e48f1dccaaf80e59b04ef5f0f6fb27b30757e9cfdec4a95d3c10e11b59db7acf7e3094745be8a78c77f0ab93861794c67c417468422e87a15f19e9f97786f8b30c7c3fb4bdd5e42fd28f8c19209bcef036470f4fb2c4cc12e08baa01e734ca8f85647e8f8016976490ea3d1346e17ad6c52efa107db61ececafafe405292f754c665a3bf37ecd814b0a217c370483c27176e94bb779eb37b9af2779d30dbfe887b978c5af4751373ff45b2b36c4d09bf68fe636009d7702a015e4e526d4f8ae61d995c96efdd8779d2207f69d94da6bc260543fc6e8f3419c182b194376f67527d8ef0c3b63f19426de5ccb211b24395aa4cb34399ef8a1cd423642fc4609fe7dc5afb9164d852227bdfecdac74dfbbe5a8e0c7717864b3e757723faa41431871f153388ed24169fb74e8f0ce59acb9338e7a40e019226890b2659e520427746cddc1f7007a7e57b0aa01ba337e509cc311f49838c66d8c1c7b7c476e022e95a1ce809cf8178e16b795a5a4386ebf241138f3b6cb7d4827f3a00765d3ef084b56968ba9436e13ee332c7a968f6ccae6e356ce996d64558c7d56b99968cbfb0988034e5b489c92d88e28cecc01b4356d7a82678c850805d193e0882fab10fc1e84c280d05f44897fa02682f4512b3983b8beca1e3f3c055c7ab20dd979658eb9c6f3e2a1dc68b84bf4fd850ec14fe3d73484f39dbf597e8c0d470a41b116051c4d1fe1eac90ded9008506e29f07bcad3e0dae6c8b5b6829f772bf6a371ecc1cb117b5cb61d2a55798863932423e498a3d728db62cfd1ed206fb374c7d31d3518962ea25af12d7fb167af19e20bda5b3bd67fa4022e63e0d19fc7041222f24e652536397f4d9de907140bc11993e644171a8eab0fb5367f113028e510282e8b57294e678baad8ccd008b99236ec0ef3cc825867580d14bf2518e9130b7d6775893374ddb80ec62dffc5562816e88eea5e676386787984c4f9f5fe0805268604c146d37f3f26d7d5996576049674cdef9834035686097af7a30342b4579b285b30e6220c2d1587f118e324b968894a2a7132806f3c4426188f69713201bc04be15b7bd1ce5489a7e61e57d09432202c4ba6f501f9f9d200bfdc4e184a29dfd22e02cf32c1d74afee51ad9d025ba03dc309ea79c5f9ce8e539612b0dc60959818a569e51561641a72b0f9f556e110b1d3991f7a449348c2a05b44f8abbfef924ce4b13af2481399822d53fe5b64ada66776b99f9ac0bd115052368d4bd9420bd54f99e31da2960125dddb89b2fb8b7b0643465acf4146bcd8966c6ac3b845805810a0909efd60eaa3136ed13db21cc65b5c73b28f3a8b4aeed2cac9c1716c0adeb829916e33bd1a6a3f0f8c166a096e089c03e3ae500ee78033d3322c81509f34c6e965c942b3624bbdd84054164f167b20d2c6213af614a4df81ce592f3da19a6207a385d9404600f8fc4c741c75dc79e4986f2973e28cca0b565646d4e8c58696cfae0979de8bc48d7d58a5bcc2e8ed5a7cc9c1d9eb15a504e4bc8c91ad8b775ace8f01aa62103609d93234775a5922acbc3c81c6a75377add716b64f0a06cc8ac038ffe610f4b36fd3f97968f9ca549f900c5e0f81e3660f150b957d96ea57c310a311b3eb48922e0df645dedc62d137ae071b9e89247848cda708a703cbef9ba67bf084641c285a8d3e90e2b7ab7daad80a737cde47be661370e2f107fe1fc6e814188053ed4e36039cbf89fe2f914374e4dfa6aa35d41ce80752549b35a31276274ce84ec75de1b446745ac87f843375f9e333604053960947fc1fb770c500a8e747c5f57f11c6153a41eccd6e641b907e6c95b01537ed8ebde49ed1d130c1150d32bef9b6a254d1f06b8957edc48ac400159e707024477db3c10606a68ad778e22435e6fcf6c7c946a3e0169b8b50b4f936c30d95ac796e2a095cbd1a18c8904ae47aa8c6177010fe1210a78f530f9b4c03bf1fab12c1c6ff3840a7798b532708951d9693f50e1e90846a92c5735c1e9e2606eaaadfdea3d29b267d1b4bc725b333ee7a14d4b572e7db26817603ee636f145b7fd5dfc6c940c1c447a597ddd8aabd55569538854a4bc5dca3f6a090fb88b563f62c6c7c13f4a5e124b03228926a12a28b46b5bbaf24072f57977c4c1093dbb99ca1e293ae69c812f8bb67368ad344dc79803f452372d6ae3791d1b9ee776f84787f86c27ec3185fff1c140ca1f443856aa5312058a53133b55f7ec5732904e17efbfdd2f4a10eb2ecf7d9696efcc5a1068443058a1c24579942cfcc34f347291cbd35ca5738d7260dfd97c65713c93f4f0382eb5257e6365cb1fa0b2d43e7fc1674206d34b0f4fd2fc289993ebb50d58b648a0373fc7a8504a2b352da5c9f39b79926de117e5a60d373ea43d5c15e367e441f9529d99634c45a5954b5f671915799703b54579937d46a5c65f702725b65e1c0d1b4ca32196468975546773c7b722e173bef2f72e7984988887cf93e9443a2a3df6262d8c144b8353040cadc6291eb9be548aa3bf5feee6ba5c1f7e998b3cc17eaca44d67e0b9aa9ca2805997ff68954d18859fb40a6a9b8f2d893557f33e78ad1838f961189364bcc3ed7eaa8638affb8e01d5a6e88e305112a2c1b2655641669063b2a154bf7e369165651a2ff6ca961a71ea0a8c47c083234881441bc6aab9091e275f0e4769596df00141ea528d151286752b4c79d7e398514f471ac9c18f8e70236863b8cb0763d6e06475d73fe8cf88d34bc231762669c086c8aca2f3582a2674580e675025e813e903e7f7669d3ba162e03b553a830131bd3d038b78dd85705d242296127225ca4b62fb064b79a1823ca33d4f9b2bbca26d4eca8212fc5fdb64d13138d3706e6649e3d3644f5bc9c7dfd73ccc41dbb5ed5bbdfcba040655aaa3c0fbc6e451cbb7a1183663e908dc1becf24f3d79833fbd462ff472ee30e1b9842eba3483767724b2ec8edfd6c4f98e85d306f62a80c0331c37a958886df6642a40adfcadcc57cb581920f9fe2e0386afb5ae098937be7eab015620e4102c2ac57cd0a0229e282b2b4c2fd91ca69fd481a65f9716cdac2bc49acc991d926035fc37d4b8c19ec22c09fdc03b6f8a659cddad85f9cd38e868e3f90b2b425aae47052693c748d68a39fb6f1fc94b9fd581cd445dd184565c79b6f7dd42cbad730934ab12401b0d50d9b80ea135e9e048feb8b123aeb0c431349996ddf834e029418f4112d0fe59a02d98a1346b723a8d9c11e7871a08cf2229a757a516bbbba683d71fd65ed3468551d76f89ea5db89017ce0872f1120cd52f3ab9848a3ac7cd8977a3b4186f3055871233d1b9a78005e9c1f5a77be38cd02000a3001d84685327ddc0b1574e40a90eb8997f73dc089c856ef6d90377c0c4c80111b19fc030f3f2df2df6fff354f263f6aaf777a7bc287a069ead70f49a9a8b59823e738e1d820fac7170f5592720d16c18ee512d73a4b715dcb3824fe9a373eb1a94ccda0c7a79178968b7726d2e137f34e88a0b3580c2cacd6f1de785853aada185a0fbb66f1bd0bbdcf8c10b00fd935d17f88bced1634f49b17038615c7a8c641d51029c6b14540ab29d39ee53a10a46bd3301b15c3e457b374340e29320ce2a1fd05faa600e89ca8c741fe22f311b14a8a09a8f993e29ece93606490c4102c70afb692023940eaf1b65a6eb482f14e9de4b8c1a69ee605d4edfc6d4c9e7f9b59bf0b77bf93737fcc3bd9d93c21756055637a2991c7bc3924c374a02c7ce76c85a4616595c98bfb07c3d35416d04304582cb51dc582c6f3a474d005e6c53d441f58f1f3c4c0ff2a26ad9fbdaeb5ba06441936a22d988f0e1da5fef7875210171ed694b723b1321c9dee3ad8a5c1ee7c92c472833581f37208770908d832888f9b7959a52d206845d7db98bded6c5dd99e77256a23864d281451fc08e1cf67d29c488f8df22546266819b302ed6f85235108e5566ed0109cc857386b84a06cd8ee1d4219cdf2468b726a94f2229c50f1e306f3e6a1d435b49b5b5e33b170510f76e7a6b8ad1473896219cb3e839f460b04260485785f8255a5bf7ef2925b8bea556cc697ec7a5b441c2c2b20edad81ab066caeee4e27082d195f86602e29a38c2fd37696b7b46427629585c39c8d1f022309553c53da1175e74fa70cd1aefa43a0d7aa3f1019efba1c1b4b987a5854748c27949906fd207f3bfb84717c2fdc7997309b020fbf474ce3ec217e4f423f108e1622e3642128847b34224e38e0b13c9db228d534485feb30b1293fa5f4e88b70ff49ab2b5cf09f52eee6cb64b189ee5a62f4ecab5bfa83ade568f0f706ff063fd792c34b93bd9763431533d254bd29d73e96115593b2e0c69326d3aaee9bdbc6f6b9aea80463cfc4cef5fc4380f827b677567bc467c393f6be429203ab7d5cbc71c0dba47bce9544d73863bbc2679db6a2a04bc55f78135cb18b0dd395c3be9c2ec6e2c92deb9515fb60bdfc49b35b6cee825320bf6b3dd4ed02259d50ef80e78769d21f9be1cd1cd25ff3fe406d060cc207f3c080b1ee6618fb1a8635ca82d81c05feea0eeeb60c6fbc59d89243d86eff9f4bb50edba255cc222e790a9cbf0c352cb08220eeabec5764cb0afffabf8167634fe07fb94edbc26aad22eb5fcb071fdd43ebcefe8740a8819340926f68f6544b45c67a8868919905f607572605416c518666c186b2d03de005f82c46100e822ca1f33aba354f8f416d598f75584fc06418ee47c76b48511d677dd97937882c189cb19ca6b8288ee974de45ed6b15e061c4944e343500598e8140f405378a3427db31ac9d2231070d637a31aca032edf0fbe3a81f65494034525309aeec67b5fe939bf11b14ec4481241307170b19bc4519162e0eb4a36a6863008b919dc1bfe5d1f73d306cd5b396e2ffac426e42b8a9603f9953d7caf8c5b83ff997737a0bf25e827ddb6568eda24f20ff70c6cc942495a8f97e39fb6f36d08549a4a11d0d48b2d3d04092f85fbee730fc9f69e95c28966d5a4b6d2360c0ecc8ad86b95bd9cddcf5a52d1a907d170dede06fe68a06fb105ea0b0de47dcc5c61187248150126ec39d70828db59a85e71155b9898aa74224424a0c91d28b0bbe94f00af51972fc028c9f076fde35237ae197422767abbbd7d84ae046ed7dd3a65884da11d3a44399ff1470379d1532585fe8f052d46301580389f5b95fb620d14de4cb09917d51515a6d228a8545c405f9a81eb134bea68e3d8c4b8fa20d27d23e669dbc1444421e51fb580f7f5707a00ddb52b5cf266658d7f9495ec20f983700bbbf31428f84f2bb188cf01694c7b042e7fc35a1280bf3f8ae8c362f15443424ac47934165117f9fcde1500338176a9af84cb2b9195bdd904ea45e3c7b469c839e769f0503dbd4b1d418e6e7e29632750bd2afad462cc9a39fcf6d98c41b3cefeaa29e10750e54bcae0a8ec8a4a185eda385baaefca00b9bc31117931e701e8891eba07bed2a9f47ab228891694f99d304f52ccf9062b5009d0b406c1e27ae550424afb58924c45fa432cc727ab249063a24cef6ddf7466f3c1dbf3b2db9490e9cd1e6e2109cf49a9140b8dda049a29cb93fc57c18b38e4a6ac7b8009801d8d864248454996ebac1543011c5947ca810de93794d7a38f080411cebecdfdaf8b8da98690cebd9c569c4ebbdf43a8e292496c5bf2fd2a424c23e6d263037612b648f727e74ae97b3667845d812d89bec8c754d29627a0f8390f8377f626c6e89a8e522e118ab82ff9d2cb6d5818d0fec1ce08be92a783b8a15f67d47d2573097372f1630eb2b2cc3840209c00e28f1c114e796bd886ed0c96ec50ae642361c6c0c54dc157d40ef8dd1a51c65c59fecf615ddc5bd4c1122b338b11644cc9631f95eb717043cda3203f72271ddc6529508e9bcfe37e5b6222adcd12e33e7425f835eba38b24c8126f8a71b9e4f1e8215b056e79d42202567332fdb05d2219837ad0734b63e944509842566380227156060dd8b3c05a9b5099460b752e2bd39e62f8902549638676463ee45b7982b0aa818ca37bc80faeb72c186bb88e1eb97405aab7bf74b53a6dad071fb53d1d46ad6aff1eea33943511b60bafb42090437d7ae4adcdcff1180e0207b075ba20d0efbaa48179dfd0a2e54c61efdfb056be1f707aaf9f61245f530a83b4bf6104333528ce228d1386492f1ddfe77c848ba1b320c9475d7793c506a5664153f0ca56bd719bad5a5b0d2ee0fb3fb48c02ad5acafa5b076dc763adc24977d8bc1212fd24fe99d4249a202518724a9c10a3370dc01099eec9f9bc0f2b957e1a424bd278ceab7cfbbeeb6054e105d3680105c4b6cd71bfdaada3228de27906d9116475d23d8c3f8079ee70f1c5991d1fa1136fb28a06e143d1058d763d41a3caa11c94e1edad3bbc253ee8a35ba0ce111bbbd08aff460431913371133a086e69b519a874ed0330b8eb95f7c38bccf5442c21f035c39c3e7c0fbeeaf2e42c0c408939b42b303657a2df34d6815cf9aa682de62b20907d65b680c9e1b7d5cb39ec6fc13153f69fc5c3d94e794386a133c390195634657abbb8ac478e53897c7643403b39cc593d5af7a5a8a0bfe4663598c975c044c2cfc490bfa3a69db0ce3a75f4dcbb2a4432f42fa7082e450d9c9a559a813d17f8a5f214a5ff30c1ee00d1070ce5f711e800507828c8ccc7616c548d4e8f91085100065286e8a87a727cdf87c17da324167a8bb86862539a9ebe0b95308f433a4851aee5c8307b2dc226c477e2bb44466a6184327973e60f83bf03f55297fdae04745a1f6e41250bbd0fbef2b858b8b6146eeffc09440f83d7b454a2711546d68da299399a054dc0c94725c15b0d8772941b32f19d7a2e83ea2aa668eb024b4bebb449ae3864417a14aff26e3f4f16627ec871806506ac05a83eaba83a6f3e7b1fd301b479ed626bed14d83c784181f2e519df54fc07dcfd262fd10a94a86126f82ab87b88f9894eadcb70199d52268cfccbaa180a0886b554e90cb04f41a254f6005f06912dab09b0f62696e0ff4ca9f6a3a170e0e78294ded342e7026e95d1eaeedb577ad7c511a0ac20de98c0576c8c3086487676b124729a7377ffdf19f42e2e5027168034d86188ade3c46aab9c4edf88842a665fc48ca4d20f96d68ef4347b2f546e430e17d31eb294de6e09508ebeab98674d011fe3b06ba936d8e8b93b6b130bf2f6db262852f3e2530bf06a86a2745c2793315cfb847bafc313e58ee670409fc25cba442a7a3224ef743b50ba13f4533dbf85c32addc2487e8099444a633edefa8032968e3ffd66737a513bb87daba23fa7a4a22a0bb98b9bbed01220edb47dab0cb4672c48e1d9766c3fbfb6814f744f111ee36b5ef6ed7c800317abf00fff77d6b879caeeb555ea23c086cfde0737e705249a83f9dc1afe8aaaf5655696478e3b5f9999476e17f66e843392075734ffd80b1ee5f827bfdfe544ada957979a1a02feadaaef7d3b626dd9856dc7243c3708755db6dc149724181d86eecc9ea0a371b6b3eaf36176df44eacf09445a42c90b36e9dfdd252b85c5152e4d9b5461930b089b677a9daf25bed594d2bbc7fd0672b3bdccbab5922621c8b26976f03c35b3218cb851c0730d905ccf9393b8b2391f58000adc2cf5d9e48edfd59d6baf2b99471ae35c0e7cb0cbbb6673fe9c36df068ba068c10ded0e5c26a654e9954261447f4bdf23b0a6e069d68d7c7d6fda4efe3abc1c20976b1aa0dc8571992861e995dd7f6ebcd2dfea47cd0a605cccfddd7c2f9d60c0290f0d79a9d4d64ea4b51dfc8278509b12a1469338a1ef44e306888518683db00ad658e51f52da0a8d5e72372d009007d02e11a438d87cda708cbf4e9801b494c13ad03621e41992e9dba127550a925841e6bba5ddf2bbac33b50a1c28f376ba023ed0d83942a3b5ae3281526f5d833de3e9445bcb8273ae9f611d7a5f26062b8b25ed223ea189dc641fc85b26920187a79d4b014d014679de8b52169386adee73178118bc53c6a36b223a3d248f76c2b9259bf651a7b5fcf04b7a356ff112588d9441005be7ac4fd0d55209a554d3a45e244ec355db1292c6f9e824c945c7ba28054bc2d450edf45e294b1e98c3e874f1a251803331c620408c0fb4d7199f750d0a981d2aff5c7641454a98de03e26a61a4b6feb5f6b09ed46a491bdb7dc32cf0daa0d9b0d110cfa8ef2ae872e7625418d7e5d64e39382c2631fe560bf727a25e3d8af23bd4a3976490566491ccc045f34f60b8b663d012f2afac4846e71b1eed2e2baa2bbac903f60c830b02096c1621938851d1b32c18d6bb8d871463601ddba6cf481298c74e11168277130c9838b61973c609a77b14b25cc3a8253d879803df109a2ed0353f10cb0876eb7fb11a2cb2730755dfec0140673600ecc9959cc01eddbbde50f11a577917469d31cf6ae447af02da239ec3f4a363e9a813e9c8bdd6431191663d21cf66beb9162bba739eca53aef1469a53aba3e18a6a439ec3cd4bda1261ef6b7c334a3633930851dc328cdc13e477c610e643d89c97710c96de2dd518539900583600a3bc6dd0943218d0e833019b6c3526c47ef08c542309ac3a88542cd61a40a839ac30eb1a01d0cbad8611004030af58a2f46fa833c54da8805ed7771b1ff3cb9d867954e9ac3ce23ecefe72f6ef61d8d697c630e687fb4b5cb93d9a572b4f04f4c53f98eea6b65156523128998c543e942860c9f65b950f41ccdbd43560ba7de2bf65459c52a85a9122612c23db12112e6e3c7fc4eb065638d406be4daa2096334c414d051c0f7d0508ad88535463bfabaefe9b20ef8ee7adb8af5f52057d2d8dd1abcb01b7b753d3e5ed236b1728bc808db303906c33a8daf47562c22a31cb839c839a0efdc05f9e75527c4867817b3d75544de1821c461d204083f40b022c5c550ce837f3fbd9cdff5dea15ce66e47b5ed327331b473f95374550c500c9c6e7cb34c8d6d61edda76f2fd2861ffbef85e12cc9a4ca77777f4d3c2e4967e3a324c34df363af76a64bb77e7cb6a3c418861574ad1bdaeeb8a44f26258bc5906efe86153d3b48c7455ecbaaa4874d50bbbaaccae1a4723edaa704eedaaad5d97a6c1fa2e56f96ee7ccf255df9ddb9691ae5fdfa2088bb023243d96794254d81026734cc7e677e4c8434ae68eddef41ee8611c2f79a7d70eea3399a536da3639085b2a12fd91b94975e3a02a67afb3bbc90edcfc6c43168d3fa5a30021de8702fd681e2400a34c869956a7c01115a910eb453ddfee1044516ef257b4219513a3db5466e5a252b061d695574676f44de9db05ba27ec80befb4710ef4a675d3828fbf6e10c8a2e7d6bc56e39dadbd8dc77c6fa46b1545b24d10e1e3f1cabbd9771c00f2eeb4cc4f07ed40072ec3dac16b7f55c8fbf65145fd1875a81faf93f7fde68e7e73e3f6e3d9cd761c00f26e6499f8a3c2ecbce550e5601a1b1f98c5004ef58b8acaed5497df703dca818afeba9b5bddcfe6684e64b1e61ead8cc114b414de6c9d7da004cf313647d822462963844760f25e6cd8cdfc38f640a18a20f004c136f169e253a240153647d8627637d76e22d1f9b6ec5e14450c6366f8306ed6e2b945d8b16b45300dbcf136479c9570e3e5b35f4701c9e4336b80e6a2361a599b678f43b4fb0f6d2ea28cd0be7db35064915db3aa19c84269160aed9b9d81a9f8ee261671591b3820001e05440a4101c1ee75ec5eb6023df747762f8b027209410111223fb206b878641689e6a24d0dd23ec4c7ada688375c5abc82be0d08bcb43b9a8b326218a5a7d3cfafdae1231611dd404d3486a14d51c524b30c26e5e1766d3cb21f61d36c8ec0e46647606273842d24943216d1a9286b17afc4588a9aa5c5bac4336ddcfe0d70022010810458fcdc8004ad3e637c9513e67c4866e0dc5e1420d7035375dd01318c7bd89beb3d1e984ca766691812ac67405947b288b06a388ce33d1318a09939d22359441b82b7b0f6a176432350f8f71db0fbe5c8a16de265dbc85fd608e58e95fbb5e411fa7ac561b8fdd7ef069fa5fec45ef3d607980c95dcec939fc020ca18e39cf33be09472bebe233e731732ae2aba222bf87a0f0c74b6883720bf1717a8051e0e388896677fd9fb826532252b21cc6557dcec4a40568b7d62e054f60c0da62b6dc770b8d9d96e8162979d05a6c100ed2bb00ed2b3c30ef02a632e1303e2300d3d55a1313dfbca339767da91205ade9938a6b99efd31bd9adbb6d96bb612a6d91a8b6b4ed19cd89cd99ca339e79cda9cdb9c4a5ae7ce3c9edbc3c5ced57e7d4e30d06bd39ab8e84ff6b95293a0e63578e71fdf9827cd4bbe314f30359fcd959a04f5969af356cb5d5a2cc354ce5b288bc1d44c916152596139c9d054b2fbac70a947ba1582b7b2986fa9dd22550c60d7f4474b7f5650963f2be87b72b33979eefce15436190cb40c275ec3390eb37fa6a0f39d05663da019e7b5239846c69dd81728194e6a381cc6606a3b28c5cd4830098944ba8d5ea19c7415d3ac9cc4031f89740cb25e4efa64010385cf0a6afaa3cf0afac0b8b2ecaf0caca379727086bb3dfbcb48cfae73367a85fdba8a69b45fc720cbe52d9cfd04b35f7fbc6a9ee6b25f77a9fd2408a6b2d601afda49f6e9a25d95746784af7687181a357878b53bc4d0a8c1c3abdd2186460d1ea08662fb8765506eb17afdf44fff98fa477599601aecd99518d2ab6a32994c2693c96432994c2693c9a4612c635bdb016a3151a321b51a97c683e8ba4a2f6da4d2cc156d241ec8ea1fa8d33005b6c3525eea0b8229c89823d926de108cd0cd5450e851b2af549d9bfdf49430d1abbf9c5e3d93c90ad35f17f4dd79d359361e26cbb475308cd2d3e9e7b83bbfb2f19896e98c0c03ed38a80777fb470e34a7a96856efc0141898d53c30959d32d3bb2c7b6bb8d98b434a7695da3f2c93fd65abc92eb53dd45cf6de81d99b87f4ec279b919e623b00acd8ee84623bcad036f1b29b6cf60b87d2a79cf409a33d2baca0720cdac12b4ccf0e793a7b66b2b0876e765907da71179dbd83200beac094135ec1244c055ec12bba7f9866e5d97ba85728cf60b2abd8aec5b82a5903ddec2c75421d78054c92dd95b3b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0bc6f467a4622912ee948f48ae5a4ab6c3c4896e9e361a9af0ef435e9d5bb59f69cdceccf09acc3017c237ba6859bbd7b50b8d95f4fafb467bf9e6dcf4ae77a85f2d26df4caf4d2554ca3f2d231c882f9cb1996bc602efb09b2fae78f570fa8b9eca5c35426602afb7c383005d4ab259e17bc1ac25cf6ec0c9fdc2c5bb14ab04c664d37a164375571b39b361e25cb74e62a911dc3283d9dde9d93613b8e643b55f62498a6f4ec48f42ae5d983f48a3efbb42912899e6235da5269875229a654a2512ad52895782895b85229552a695bbd646c8f33d74b2ffd59e99d8a643bee3f9d608672a92d95616a6e08b8ae598c457b60a02b97883a674642b15c73f3b4ce97a6ed86ccdcebda8680eb5873fd022a2f3a6f5a0fd40f784b87b7646d1781cda2343b123db342a012da3bd4503339a653f9b9d487fa347c570ed3bccf69390b39dcc99c29640ea43ea621a6799797efb2215cf96cced21a5c70eddde1ced64c71b8f25d690d571ed62e50390fef7cb55e226f0c58636ef50163cf8744e65c992d6119d9b0676b0f09b5824e8e90a55f40bb914e8ef6de501a6ab33fe0d50eafa6f59c2a502727e66a365b023fd2611aedf2a41ea69997d9453cd02af5310db18cfcf584469f7b64549b7859f569e289463a57feaa4366aecbfc6c09d3c45cf9a884696254b2376130dbf4bea04513f77bcd024d458306a4a1a2a19aa951e33c7cd6a83339543478787f86871aaa191ad67415d77c15a7a085f4c8c3c9a23b5dc87aef7ff19454e1728d270a6372ccd5ddcc17605666313b87fbe6a31cbbb939be57d939b331be5772bec3386302b2342b39053f378c535028b20cfca302b50216112396840da2f3901cc087019a837f57c0c71f72e4b0612326a67bb9ddd3b9d0be29d0200df4f37eaebec800cf34621a85184c62724c3705ed4e5aafe04dbd3a1d029399254f99253a63a278558c53b65f0426abe0dcc6eeb3a25718e96e57bb33ee8c1c5f471845a52bbafc5e71972fbda0dde8c97df274bc0e7bd89934e7d3736513f9446ad1a9972b6e8b640fd3e4905674aaa592e674648f64227d7af54e1742d8320d318d5018bd82222c9b224dc36e27650faa88ec9935f25ad86f5af34a5dbfa40e6499844c4226a19b846e4b0fdc30033274b1c50c3cd0ea1b1b70e1670b3f22e0820e3768f5658fec913e5632b9baa01d76e59730cd2fcba8e0cc3acc1ef0f21f8c10c6d7af6d87517a7a8f4094ff53070ceb558dca73f2369220a05a006e8c0c0115fd3a7c271d3b85acedd84fbd9ac7fed2abecd8dfab9111f2db7933229ef41d9d84f4514db23d23cd2886ee3caddb4b95744d7b6f36f49b35728229ecf4bdd9944eb246284c612fbd3705700b8329ecf208ed6c5cecd87740ad669f75c8cbb9a3918d5e09199dbfb8d847168b319815a2824b902d903b12043be871c28997f32e41e084134e38f1a478394e38e1846c811355c8f5cc76447e89fe722e9133c10417f8c8cdde93eec0cdce1d0bb5e01d8741337af7709a7821c8f983c18f78e01d760aa2404d3c29b27367b43b8de0ddcbcdb89ba9ba192bdefd7033196455649f8f0b4e9a0acc61d893d3919c9e9726b1fa4673be0ed428ce039ebf1c9e3f29ea7c22a8a33f1e2753dbc1c57c9ecf5501900a7854a0b3c4124b2cb1c4124b2cd1759d903f1da0d56ab5fa7441bdb8a0be2b68f027457d3bbfc18e3d92c5030192d7e0cf0a243530c175663083f35633856501b18044e08115665d6ce0634505a249cba002e1a48a0a4417cd01b10353bdd384137f5258204e00440a9cb040e88041023b1978c75d201978f70b2403ef4e174806ded10b2403efb00b240307f239638ab36c3c9e0744501f0a52f097e4cf03f59d400617bbc10d3e5f0a6a430105b59b90e4043748dd77e41db12d048689c1c56a9267031b7cbe0ed4d19f0dea912c860000062e3b3bb702d5812ae4e454212706e6652789201c051ca0172105e028e048027a12c0134410410411441041045180021460c711807300214a85ec0a6080808076c009b1b93705ed4edc0be4b288d62471342e6af07a9a6ca19ac3ec1db30fa1c59aab4df4803d542eb30cdff47b3bf0bae9a0bc1a86061adfc51ba9c46c474516cad6e2658bc82bc718630c0cc80b61169849956346a59a99c9a152cdcce450a972ccc8a89a79a1cdd5a0f10b637ac5d95061b0bb9f4a9523767f1107c34e277e681043c4610205185c8e812326f0bd77f84a4039e6c97e3f6088cd8862f6ec973d9d288dcfac89ebe934832f876fe642958ae3de89592716f6c7175bd48f26e295ffe1dec0364c29e593b2bbe57b0fcb5aa2c5fa3d36a8a18b3ce2bcde3ef88a5042f07d8c29486811793170b107667f76e52648b8bc1151bacfbeab90dcb711b1dd674b44dec5c0538243125dac26bbf1cdd1359272ca1963d4228490d439a16d70923a23b7c30049833d1f9344247e97b61eefd20d4523c5ade65d6a7adcdd295a69ab81576e358fe383a4cec88dfc68f933768ce24696c832b76d24c3e4431823acb10cf41d46d82a98f351e6e65e35110f7fc4db08688dc2130345d6016197208c11807177c4cf88e0e7c6ff1099c98d3d14c02dbe02b875449eff3605704b7eca6a240b9beb6c8d64814a003cdf0868735e0b9ef950187124040568c1e7bc561f0acca6ac40644f30159fb243e141f05acdc154bcac292b68f13beef24f3f2989ce3194f9ba7c3a626e9f8ef77e0c67a3597354affacec5d0ebd929777722228bef4686616f07dce962e77743dad8efbec7f760ec4262580c73d7af2356daf942e2125dd00a8987f2f518f0d42fa27794f5c7f8c95c7ca923024776c432f1ddbc4e5cd1dfe846cc7618b8a23f918d3c2e6882b71530ddf873a18d392e2e29a3f7ae463ffb56f671efe4f21ecd25e525e52b31d340291fb3cc932f5271ddf8139451475f57cebcd8aa29069150a2849f18e6c7c43c143ca6d731e63f7be2fac27c5dd7fb0bd7d3c5aee99a7a25df9799852a42645b6e767774cc02e58f60764b6498e86219961136cbf0bb251ecbf0976020fd7306d115e531ac66fc3280b10cb7fa82dc52f0392c527aea4e45cff5b5c649021b446998af619457bff48ae1d3313551363affd42b0d1b037def3015be2159ba25b61f7de3a7885f934c2a422efc7bdc80dc91a5cc49d3102d420c6a452e3ccb27e29b2f96208a618869002d684286212627f0ca71fb7347a77caeb5f103f42ada24d775ea7805fade69d70cf1933e2968915ea18048211d4d375ef2300d33e1e74ce0c4cf2ce99e126ee6aa13471f2326a443374884b4a7527cd8e1c6634773518b53a0d9b10b919fd73e3792889b64bb2143ae7c33c134444e6796d92ccb8a8ae6c4860152064a4f886ce9c4b2a262929182424bddd77e5521fdf9f9d955da77dd98601a22268c08d310919135c13444524617601a2228b308d310a19a134c43a4b461806988904819601a225be909a62122a21a8855fbc53222145a226d22a969d86714a3071260a0f14af4eab24586b4fc009d8a8f2d318b52a0f1dd7847808c5108b6f8b383b991483f5edd42f3c43366698053f133333870dc98bab1b6b458a1d86299e8033417fb03345e4616ba70547a1343d61835ad393e659ad78d58c3461725bb9d05ae4441b9946918c2c72cc3475dd48f4edea872a31d62ba8d854823d27817630decb1c6df678dd7f1c51a31368d2fd0fed35182506cc1931a7c5fbe1dd06b11bbde65ba710618eceef716d237bdf4aa61777777777777777777777777777777770dd385b0bb4df02f4d35acbbbbbbbbbbbbbbbbb9e8e23edaddddddddddddddddbd7a3a629ec8061aab017c2a02a6207dd88b6800b37a70aa4711bc006625d16dbfe0c2859fb9104278be1766e17fb0405647955e296d92cc46edcb41d6e964a2cdc1f84335f884bfee9ae30721ba9496966cbc8ee91ee42e1df21d7cc734f77ea0813ae23b1b5cc3f7ccbb01a5e59ab35163e27db95dcc7d97c80fb763381bbd7a79b9dc1cb3389862166c8b2ae2b204b8a9cb069363b09bd6757e0c532f0ba716ca08285036bf695de756ca8ec88ace2d2678d10631705ab2321752e404e1b478ab79635c226f0c94cd1520b2f10bcaad2c503f9a6beede74185c70e107a725ab8c01139ee8e0b43806767712ce862a3b9fe6e9189dffc3d331cf0fe2e98825193f7da63c56e61bda3f53ea6b952a0fb5fbc1a27723d548e91c70e1b5daf110c337faa36af906df608e31e66a5c787be38d9648142fa7d27374abd4373b3708c2129aa461095ae8f090146470069e2414210744d83abe20139d6f7a3a2ee698c41ce41bdaed3670e3398a1e8db26e2d26c7d07ecafb885958b30e9ac2636c01a48631aeb053c5095a4f06631012e2020911e8a086d63b6317bdb627450fd36817fe29e1e960f2744452939fe7440e310d33e1aa1caefceec8aa12a61aa140e53b166a423b167a3a34db2531591cd34d368a2ce255b2e8c137074f47d41e2d0ac89c96081c630ccda2e66fae76f8b783b7d32b9245fd90a42494680e8ba7231efec13ff172564442b156e0dd80c72aa9625553a9a6f6e1c5255021dae7e1df13f8d703ff7ca69022b07bd70b1a5b487cfcfc8cefced6b4e98510df38eddba239f8b784895e751da7e1c2c38e836e935e25810a21843e2d62c4b4c7ed31da6ebb66511115cf57d3668c6f8b25ef9e1617cef939e7531e6b05b42b643ee5747ba47c3ed6f9ada2b0c76b15783a4476db6cf7aee8f05abd00562fa01d7302003eb66fc7aa0fbedae314e8f3b18f09e9510a54f4d2d46aa71d8596366d764bc42f41ba5601d31d62ba5996c46463a7e2a1e50a3794f97aba21da8d16c53e2648a01550a257d476a5d3e65ee92b2c371d4e533dc114bc4a45454be10a8b3dd140c2031f9c56472f8c67b9e92b15156d1147582c2afea6a58229f895af5854b45c7330a5f297051593e5d68d2c2801a7d57197618a631442480983987d4cae5dc0c73c761f98ede2a74561e7bb1df5277af6ee61d9b74c647a0f6431d1290c92ac0f6b80e660ec0215fd4786d9eea1e2e745f351cd47fcb4a4cda73978927d4cae7dabafa739f87a2ebef074e11f13484f579b52a01df6d145df017f7837f8bc1191599a77838fd920de0d56357773b166ddb4aefa4a4063e57bfda0f449c9cae9a5bc77c417dd4bbdb3e13e96c37d2bf7b555e2dd783941394850710464f9f079e243f53ef37eaec1c0b8082c02612f810f070707a708bc023f07182e90c31040108238d0a0756384371c19e2a10309c8d0a4c5673ef68592fb381b8f39bd98b417a4b93fd1a9771c2c87eadcf7fabd480c03ed1ecf7def8fa757d126f1b0e8d593af8b68eea9bee3dd070c42c42206a312490c11fd2a02872811997c914444e2ea9eb82f48124a0ce9221003f00661bd73a7b884183b72cdddb703daf0ddc663b878624cd56d2845d1bb8fe6de0dd0a4bbbb9108725d419080eff8c911cd3dd10303edf807894ebd23900802e5102dc2db34f746b77f8a6896013af558aaa7fa3bf74edf4f710c137e051f3f8528319194121361115e5e6641fb8d49d90d91d1748770b4083aa46491942ce38bf1cfc658e57b5950791d377ee6ca337331e014b35a3c2be014dff81cbc123d5ec5abeb3aee8e1655662e6297984d92c92739095b20c3ed7e6509286fac8a59309392145c84e1bec73a8499bcc682bc36967669bbebba906bd3444caeec6ebe62b41dc4fa9217cd6cfcc5e0313b5bd41cf70abb22b458733d6a3902daa524f493d27718ca3b2a63e5dd8ce83daf524d95616abb8cca30457a4a45a9346326b108ac8703472592edce71aa992baa3b7098c9cd1657f470664e57aa5ec33d1de48ade3894993093b7b5688cf78304167af1c132f09a871b8feb3e9048c20679e42ee46cc838ecf1d1ab264810e01bf0454e37022c73806605b1699612a44bb248b00cccc226e5320e05ca4d8722e5324ef2c134a43bdfd663bb26e32987c274944321e3292725c074941b21e329dfd12837dd08949b64dc8894cb80da4924d2f6579a275521af46fb8102b209e1fb467fa4a98d6a562356b51986294eda98b884be772f42a4c640101f64ad7d8831421efada0a2f31177e7610396fcdf369d724f37346157f80f31da6bda3db1fb3556dc7bb7a08f4bd0a218ddb0fb8f2f0442f166f170120d715d9b34c5b1bcd4199a357fd43aff8d921dcedb745fd88b715e0aeb42a1bcd7a39c13ed6920b5c5c817f943c298224c134f2f0129e02ccf28186ab6219781f9085a3270b171280c214fc8e963d81f73cf07490de8d23419e8ec6797260039b8199196b8ef93a373e46a689c76e0ccbc4184552d20be50f9480832bba10230071c4376e44e9b28e87cb7fcca23015b577239effea0ccbc4cfd163a20f444f070dbef14e004ec5c71b9fdca0428cdf1e1cc25dd165bb23eef51424f471e6760ccb64bfea104e74a5e531ca2aea1d7b8c3f54d4b3331c10e8296a41df19e8d2c2860afa98d831c80a027481aad629880951ec4910c83af111d7e191e855e7e38a3a161ad5ee94e89eb8a27880e6e0e1219c690ee6886aa036ae8e5300ba58133c589ebbf6e0bb4d2ebd7b8ba15db4f198a34ce3faee63ae2a9d740ab302cb60ef76d021a9c037b0730fdd6119ec14b301a52fdd13f01a589601073368c1e99a823b1087930c8239ef6224601d38431783ef2e135cec310df4c516d0777fa7487b97ba0c74b15766492abc2d840315592359d8887efdb2464446504310705a22fb73c183d3e2d607e01863b4b2e87717696cc254aed7125594cd6504052ffaca2fbb43a5a26c548ca0e0576c7c02535804bad855ac0f98c27ed38249287d867d5e97485467aa0aa6b0c31b50fa4b8b6b49af64c020292ef6531928182ea6858bfdaa825e2cae906160520c7ae4a2e26287676066d8068673e0f8818b3da2816201d96d7a9688369b1e2cb0e8c1a26749677d9b1e2dbadaf42ce9b3931e2597b71e4b50d470af67d9b16be391615b266340af9d5e75d76d7a9634b7bab0b0e9b9a2e70a2d9a8545b3a44ea7ae1e9e4cbb215d8280922af07250ae7d8a3409026a237760eaba44d9c4bbdcc51a914860ea7afc8dd481a923f4e65e8fdac6c3a64749737cb3cea04dcf921e2d9aebb982516c7a9034c7994d0f4fcf15bdeaceac4dcf15d407450d4b7ad5ef680f4c5da73e616437bdd40359453a95fd12e366b3d49689eee58860e5ae56bbec510cddcc0e21897aa76b9e7bdd54bb784b2527f73aad1d87715d46ed1e16f77a4a6d22de0ee5e75eef6bc5ede42ab9d77be7e77a33e925455cadae7d60ebc0ce691ceebaaeeeb92ebed7e85d73d1f1170c06bdba1706185f740173ee7372af7397ddf7ae93d07117d7a589b0513cb5dd491ec5ba9c643bcca532a75035d727241285106d37de7aa0d41aedddf7b6c3ad88b7f5e041e9a3bf8b72d227d4a44eafaa78f7aaa1735bd49a8b8441dc7a943efa8c8d7289722949d7eda0b8a4c3adc77b0da3bc441a0a09a5d69969f3746ad3720c16c13075d2943a31c61c086b7a90ecd0dddee5e648e7ed07a9f9bdd742dab61ff2d2cb4bad90d24b177d96eae893daee5dfaf9aca02dc43442b6f7edf9eb5da54e95553cfeb9a4f708ac404947b9db516a8771f6eed6617daf1ba0bbfbfdb8f1e8faea407b4e92d469eebab482cef749258a9222c3a4d2ed032ceee8a5c70d01a3973e6be69defad86c69d76feba935e44b1db424c337fbd837ab5fd7a83d1abfef5d9a26f3544de18b7e78ae6b73f1add2d5f40b74fa93d241777d7d43be346fa8b026d309abb4e3abc5aee04dd4b8865aede6ab24bb21dd4dcf5ad7672e75e8f20a0dd08c9bdde7590cfbd4eeaebd205f452678689e64423f953cb9c53c42fdb78f4d6e3dd868f3552c781bbfdbadbaca3779d1fd579f5d53d34cb44736dbb212fe74edbddbca5326f3c687328d74d4afff064ced2a3d7c663e6078af1c9063779abc92e37a76da58d074b106036381b39542c839e4c9a899e4c9acac6c3f412a386f1cac6e34cbb0cdcd61ea53356c07782a6b61aeca1cc32f4a2de3b5398ad26bb33cdf15f0ce80c007ce3c1b1b9d309f5e3b6bd0013cdf16574805b8c7d74de6ce67c561f4321063a485ea6684d8b5200b7284c410eecb4b8c346358aa7c515702a0a07ad594b4960b2a435e5a3c209661852697169b346d15f84200e405130932fb4a6add067093dad1e9c2869cd4c8b34e0b4e6489bf5746e02185ef49ca6362ba77a411ace30b343a545ab59b0832b28f1518b30017d99a2f5a47085244b5a24309461496b6adaac1c44879ed6dcb459712cd1694d9236eb0068d0d39a295ae51e325c21d49a549b5565069cd63469f5a5810c530cb5668a362b8e181c01a8356568b346c154f802c8a4cd1a850e17e8b4b0f6e17daa68b3aa5e20d49a2bdaac4054e1a7355d5630862afcb4e6499bf5b121c94f6bbe68a3aad2a2356b5731052f785ad3459bd5072a845a13a5cd7a230a40adf9a2cd2a80317e5a13469b7500423fad79aeafbb5618e5257a58369ada467a258a42536498aaca0acb89d65a5b5c502f3000b88a247fd31ad59bd69cbc5dd767157d54b1c73a379bd131f8d1e766332fb24682c014fcfcdc14c0ad23600a5e1ea1fdb8035ed7a390ebdb8fcb62194ca190ba0d5c69e3af1bdf5ba2caadd8a239889ee8c915889ee84915889ee8c92adecd15e145c1642d46c7005805590760d6914469aa6e438d88249e8e8c6fb0584de6186e77254cdb58c73bf26ef4314c4aecc8d371802158a6ef35142fb30cb3cc3bf2ac409f7cefc8cba18269de7b65b8ef6fe3f19ee8958ca2d6b26d3bc026c4cdb3950911115ef5e5bbc8761c868a0d1564a9c0ccc498580663997e0685f95c604c335f525e52767747861d16ff7844eaa2b8d2c62688bc316e1f6399d7457128a45dde77e5fb8e8e92673ed3cba9dfe310ed77268d8641df9909d8b0c7e48e1c5fa41414be336995c281f6b13303282fe109fd6b095038726769524bb57e53b801e95fef0dc8063a791fcd654e4e59230b79ef1bf9da0673f4eccafe904016f3b353d837fb3bd2abee157cf647850e9781d7c071b819f623595c2323443ff28137462b0bf91060ad9417b95b92ba9b9b84a26a78b8f2dd922d862cd448fb8aac4ac027050d2201a691cf504650284a19ca881452c6cffeac903bd927f030335036dc4c0e373b0659291665437aca53ac9194937ed3ba6ccaa45589dab1d08d955f6bb646eb13ddc389dcf0881ba4b9ec578680728b5bcfee03b2549ebd089b23a62ac7b66dbf361edb9319a679379b611abed95517addde26d762bd6d17b9ba715b6644d22e3574d82f2514d429f5273b056ca2508e868c6ed8895f3660485a94bb336a4af58232af6084519599bd24dd6488a3d92f20f944a324e51b4a75494d32ae3a3dabd1c9b94a364541ea1a36bf22b21f2fcc5cdce1b112c862bed4c73598ee654cd659f5104a3674fa25906d8d15cf61e4d055931d9bb999b1db2a80dc24196123088fc4096dd00bd92cf5eaaa44747e72cc6645688cc6e6493a698f04dc4dbccef469aabc92e12cdb53d8265fa35f0884ef531ac4ffc64360b09d6ee112cd3011f9065c3864ab3c123e25058f9f28574609626a01d3d5dfba6c911d08eded3cae167cb554fc7f616de6cf7b68d8879356debe1f2eca65ea9a87cce39bba89d4ea7ed64bb9b7bfacb0bf6ed84b2425eaec19c3fe31ba38880b15d5fed28db758ebb75989df172eda777459a37209aede1ddd8fe828279cb5910a0fdd0ce2ddf98679ce55d8edb7297baf5389de769863d9d46334ecf4ed8c6a3e50506a6f27d61969acab976f4c236f2c4b6596679dc7ec0db728de5a7ab9e0eeda722cd1b8feedd162b84e346848aedfa6ed7ce7296d3e73b986799ed725c96bf08e16f3fbd83b12de78d88eddcbd53ed5e6c8bede2adf1be54e6c36c56bbeadd60f96bcb51957e07747161e9e1e9606dfbcb8bb6bde52c6729d2577339cc69e5e6602eb71eda85b8d897c78d08ed6afc9696c38d47cba9f2e52aafbb69e0f6f074b0fcf497f34f7f697882db0f19ef561f493ba1bc6815c6ee802c55e52d3563ada5a519a6a5e53b608b6963396f3f444177c59ef8bec9525fcfad97a310062bee44d16409600e7fda88e8e1dd60cdaade8d1ede0db64520cb08133dbc014c23f6d4f20bdc5c6d3bf7b05522f0aec0ab75515c212f3c34dbdd300968a772c272bea79697daf22c83394b55f90bbc30ef8dc81be34a2ae8eb4b96d359de52bb9bdbd2f21dbd6ddb7bd497f3c6a77a73b916e9fb627b78375858be03b654186baa2a2a3ee8bdb0fbe1c24f39025a448efbc59df371f72e6bd5ee802c5ae5e63496cd12819725e34df574a8bccb713bad8bb72bd23d3c1d2a56bb9bcab7cadfaedabe55ee6e2ecb7734334b75b195aa5c45e973f44a8507b54cf90b1596dabdcbc2c2b6877743f3d12b9595cbb58b800ad92eefe19150a257bcfd64bb1c36490ce99566b543dbc3bba155977355396f3c5cbea349a7ca9fbcd5b4fce5a7cab68b77426de5e5a70af3ad47cbcb57eabba7530f9895baf2775fb8c27ca5c2fcf41ddd72aea7afa89ceabc8bca7b783af8aa1ca67645ce39ee8eaed8e514f45d0c01912b46297693e92a33ae65873900ee82bacb76ed2ae77ad2b4a3c040c2a6571c85373cb9f0470061b9f6cec855e10dc8e9fc1d8d7ab9663b14112ddfceb7c56edaf91ab97c95ef802ab56b394a3dbd9c5b88d0be11c172956f2df6878a3d6d5dcb278b56bbed2adb4f2f6d954565ab2c2c36e50554b3fc928a15b272162b0475ed45fa6ae7da6db648ff273b0f372018cac6e66e4fafd90e007cfab7218061ce1b02b2c33ce38c6aa76f2a1afff42da31affa4623b8a69676dc68cd319d7ea0cdbbdaba99c6d3769e53f4acf7c191a6bef21434e4151402e7fb32b7f16f503fb76ec9bed46574588ca4d6c52b9e9a6daa95496d359586eb69f3696afbc371e44faf2b19b4c57e9c162bbbe2c477daba7da3d714f2f87816969b15d91bedd0520cbb3bb9c7bd5f2edf496cacd9d564ea6a31c5589d43cbe2e554891be99c5c0bbfc6ee572fbd140b09b6eb29dcaf995e56d44b065f9565b7eaa5dbca7b7d81df0f49473edcb725ef96663732b2c5688cab1afd4ae485f156bfae46ba237a5769d913befb2f1a025316e9177319b2346fc78b523f14efbfc9cf5881cca36cdc16ddb0ecf9b3540bc23ec3647f46af2e75cede0431f7c3320a2a08b59ad399be306e8d56593d4c30a5542373bf0001734c869021a526084160c82083929b8021590c08235b4e099807f82090fd0cb337a001e60ec0453103e5aec4291d6f0825af77b178450ca5ff2ddc85d30de2e123172e3bd2e84dd0b4401792820ef624fe0a106d7a928e56d3417dfebcbf3f04e4a29cfa97ac53d96ae0480f68ef4f26ec6f66e83790773e5bc1275e5e84a972bb32b5bae94a22b5964bcbb7678b77265cc3b952bcf42e39d8c2bdf4c83bde51dcae95dca95f28f69b2bb5cda8e3bfce4aa8abbe42417afea15db9b6ad453173303658c91eb95edb84795a35771269e46cc0eb61e0033605e502e2d2716f915b9858abc49867c8a94295579526596d9aafca866f5b18c3c26aa51f66442981ca3d18631c7ac3e3a155f10bddefd808373af187fb8913227fa0fa25537e4621a73a2bf8a6554839688918bcdc719e315b51f146827c1d363b852c538851debd57626f2c48df192ea636ef4583b0ec83bbaf3dd09e7b1fa361ef36a95c45d0552ee7bcf2c6676639957bb266e7f543bd27d5ad65d60c3a4763b465b2a45d61a5e41c5a7bf9c4effcbe9f4f2d3b1d076371772efbd39e1c603c34c54ab2fa7d3e8da68746d341acdd16864639aebb08f84608fd847d819fbc31eadea6296cba12ac2264279682bc0ddf883dc66b01821965d5cd0978d5ea9f4ae25a319e3d699151e73f22c2116452df77459e2ca55b9a22ae29494f0ef8a31c618638c5154b915638c31c6185d5c48d15e2c232fd2c0857f3ec83fb9810bff627c22d1bb8420eb309907ca3df734f39e0ebb0c218410420821841042082184104208218410f6dc1aef301aef68ccbb13fcad4123c676a70b217c48948004da03f9dc407920cb0689c78304f978b6b0dd1161d0165da144f2441e1ed1df15cd758fbe419a3bb2f5b01121618b961ddf9120bdeadbfc5e3784314a795d22edf5d008315213260cc424484bd1655f132641a0cd114800bd2630f7381b27c80ac2a4b9ec0fc2f79bfb2cea4711cd6d356da3a26afac2ab9abbeea82e81ad441006821d3bf57e65f5d4af0941208beb14935ea130a7b2dd9019965b50a8eaa7a4b9c7c35b8fec4669bba7e4f14c413b4d5b3ddb836d60aa985e4578be3f3f586e32c34cacd6dc0f4501841046080f451611c2f864074b780115bcf0d3ea77fecf0504ee4a1c3c9c43dbe5e828ca269bcf7ed37a289b6b9baf897859188f2ebacde8a26701ad0de99735729d7423a28f7ed3ea9e126d5e7b48200b65537af6cc1ad95efa4deb1dab463ad59de976efee72df1768179fe0a13c7459b87dbeff76fad8f300edb8e789c8f7d98e6e0828f26e5bd1dc6b0ee52810ca2434daf787d3dc53c20776114a7e746e912c4a5a5402481fdd0892e8a3438129b9ef464021fac8b2ed1ecf7da33f284a273d5e84c91bd0924591ec4382324274ec9885e2bae8dcd22cca66f4762cca08eca38f2c14d9b173aba3475c4664f6f970b7e7e7be2b898df96cf292ef7565307bef6197ccc1cbb97ea1264140dfb90f217765edba2779b8bc216074af1e2caeab478b1e2c2ebcac4d0f165740785d363d5af4f0f45c81dd1e9eac8bf9c9f55d52fc7c0da3bca22d42bcfe5ea1307439096b00c271390a4f904213f1469d4f4c541f73089b58b944a2c0b959c7df5cae87424c75a3fc42e6c4439154c525f0920f0706f95c28dbe7caf7f37e6e3c8cb291b44f238112768fe7ca167a43f1b0e65d8cb11f79d8037b6e3c7661d707491b0f29e3e788d4e77ad51c7bac60562200ab1df632d2654fcdbdc4f44ac6d3cb6328cbc45f36c9e8f1f1a3aa44860c1a7befb55d72b9e7f2a58e1aa80c283c61cdb50a6608f1e376ce01ef7c6a6811e3a56528065494e45df6f834d7120453fd4e36e9b0db12d4220459232c6b091afd401b98806e5c41bb918f895b2620084d40d00424b71a005c53500d6837f2b9ba8be26e3ca26cd16916f9d43bfa91a39fab84c404f42670b36a02fa999f5b7d7c030a78477f223b5b74b47082db4a80464d9aeb192dd0c2ed16aae47666996544f58e7c463ecd35d6a3262d413376463eb7dfb504596619e9d3abeedd26bd122579b29fe5ae300533b4a749415e22ac059d8d7a5a40c5183a5d161e3e301fb6f1b894ec74d845828476980665c89021a385948d01e89a01432345c54451627640d5e02dfc40898cb31a244ed0931f9f1e16e80ccd61cea8f6c9d38155f16e603bcd612fe861810e16d41c76861b1cf79f4e98900e0a0a4c4d79b27db1056d67d884483c20f19096908a20d2483d295d74ca935ed978367646f38ba76303da827aa512dacec02b551b4e67b1db17177e13822cd5108907ccc22104120f64e150425ad2ab219e908ac0ab21b8b022b422242424d462372152cf06d406a1d306045942cc805356b48039a01bdc354e62df403d477a5041854e94126b3244931f91ec19a2670826d815a7a01d4b410b2d96346b46a6d5c002080f102440ac00b2d31c902a80e800a102480e109c1d5e6eeb911d6abcd69811f31a40a680c2910ba40323b8f01db6820bbf64464c0dd10eb606e78b1fc89af104b26080302658d633ea993a5383a15167dc5698d7facf987a24a6dad3e802c80b19940489921f885082b0209670b5d68080072005e3e800fb003f918c626c0d4e174f874a08a857433cf1825743704102d1c5850722288824412809e2072220825080080222080322e802220888a00b77825872838400a30cccc1b741486a810a4e48424a67088fe6e7a7a7e70708b25e82200bf5f333a7a6511fbae003900f5ef8d0051f8256495648564a563f582df101c8072f7c085a255921592959fdc087201f827c08f221c887201f82564b6ae0d4a0e2c2d7c0a991d32b794ddb36938a8a8ae913a592a0ead4145412d49d9a85fa5381aa173b35c8349297a8ead414d49d9a85fa5381aa1775a7eed49dba5377ea4e0d1a499348250cb483015ac2a3446707c98c23171e666806ce3644a27112090506856525c346523404040890234070580608143e70e15994ac64d8880a28479aabc11912a35933c2b8f02e412e42245249c57456517103ab0ca9cc01aee0ac206165c98acf0a1458563acc65e567c7561c4c4cb509151c3d48680d4ecd915e5d2a43bd5209b5ca1c70204b35b4820466e110c2ca12c8c2a164c5a757433c598102af86e002cb0aceca0a0ecb0aceca0ace0a8e8b5dc159f951b541252474e3643b8ec576cf308a9353223d59226b4d07f806bca8d67c80658e442a74606aa7392b9a43c25343c5d6e0b00c7c4d32e3574d028037186867636787470912c87ae1812c94ce2ca528dd78a89c6e3de2257dcad06ceca884de8d4b3584430838940cf164082e64d8488a70f4a886700861082e0cddb89d6a08530d8d54435235a485a1211c3dac634387657e7a6ad49a254a608a07a6aea841526b766aaaa8d1a95151093d1d351da8f980500d0e1866f84016cccf4fcf7d590259289d1d243c4e2eace2e918e2c9bbb1f302ac8a8ba327c582948e6a483507918632af56a9428421c4901062c818e2897c72e14b55082429387a522c50cd4184a307470f0a8e1e1c3d178e9e215cf8c6d14395b00c7c03d12e252828284886cf926a9dd033841e6831b4069f32f420d102cfc9e6a0a458219430b9f0409440204b80f4f44a9a68546ec13013703ef0c6a8c1b12104593540b0262768e8c22f9840c575a2e3e449b366bcc756fb840b3a630c2969ee0cbcc2f103e6a0f6ee0607778484186219f821b820c67dd22b21e2c01c7c4f4a4a07b2844802a7c6587287204b881e70ea074fc00287925e098103e6a0d08d325c789494ce4ce9d0944e479fe08494ce853b9709383b171e8646adc9812925387a7a5806de56f6c1d103593087c7e1e3d38445e58a2b784e5aad396f595bb35283f305da6d3b3b9b15cd9a71f8d932512f00f884da56675c1a35e2e0b46276a8353aa85ab35393a38409385a286182cdd30cd82c3356546698b293addcaa303f0c0066cc807941019d5a0247b8b27065e1c2c28585eb0a5d7481842f9e5c5740c2174f7e7e7e809e38e902095f0039f9f969e2e4a7c4bdca3039554fc7b4dd9b6fce673b4d8bd7678c337e36d44ab2b1ecd9e8da9c733ed2dbb64c9b28dfd1246df449faf62ec7cd4ab623954828b61bbda3a49748b6dbb49989de1f6598c87697bd8f3e89853120618e2f725d63d0114390189edc1e2e47410c575c163ebb9ef5f97ad6f04229fa4504de4cce8edeaca1eda0edb27791bed7bbaeebf044e07d59966598edb26777d12edbbdd1b957d316e93bb2d975ec326917166410e306e07214c840869efb4a70390a6350726709638a5df57494ec157f8833d176db63f617e355bb99be321375f1a6bccb71b177396eeca1877783f452974adb4ba548af7a3adee9f6226d89c06bfa8e46b94efae93b1947b13625de922dd2d764bb0bdc74326c91beaf7494a7546a4b3d3c1da3c7936a0fefc666f98e6c8759d3afdac3bb616aee590de55d4980c2f0749a7421723c868944efc9f72eabeadd88d7c6e3c518af1cefba2e8a7d2d7691be700c538e80f2639a7b4f9a8032558d60c51833cdcb9ee50851f0b409b5de9e9529a01d0637be22a042b26bcf4473bb5637d145e7ed3ddeccee80d29a4e2fa6d34baf9aa3444194f204f4fabb2f22a00fc273364671f477bb1ede0dcd86818a1f1e8623173ec6460fefc653bd1b714484bc323e1dd276379db43dbc1bf117b8b9d7bb8e9b135d98ce94e798d833f0d7cfd1d3a3527432c94b1dca977d79e5310933593b793944bbd06ab14709eff5a5dc78bcb328478cb17b50710f39725c2f06210a62a8a2d4bdcbb1cb71bb7e91be0cc5e52bee002e47c10ba13b7946ade3eb3746a278224271f9be7b5521448cbcda710ff3bb678b749783c7b5f1b8aeeb5ac56aca0be86547b68b57f6e86e84407b65b6485f78ec3dbedfc3d321b242deafbf5fb68777435aad39bed176efd0f6f06eb43535c79f2ffacc61c915c0e528cc21e7ced29c57e477910891ac7922db1979e276fc227dbbbbb9fda0d0dd8c3d3e8a627c89b1e5298ade424497dfd19748243a8ba8d6dc93afc93039a64311ca32d1b985783418fc609386248e18378211c3b897d0bd0e74afcfa17b9d87e6fb02ed3786c9c715fcb9d77964971565440b14f22de756ac28234e8f3f7d8122c1a259d407e5a753d74f4c5ca48e0dfae45e672d507ee415f73a5df3d23491b6f5a07c5eeaf48ad2d3ebd9242eb20a1d978b624b3eb6c4a4391a50fae83cf58118d65148adaca239b9d33e5cef4e28f5e1e0dd0035dfb5185ff08e6596414d5b93ddeba4cf937ac53c48b609d1ed86f0cfe5e6506c87aac9ee767adfd01d3273b9b90ea58b7b1de547ce80f63b1427f7fa567ba8b926b8b9cd76508c7b1d0addeb1d0cba3c543efe49414bf77a162d14f12e97168a2ca09047fd97fe4016ea2ebf609ee4e5b4890f64c1d89213978acafef29b16aaa2328b4a80bccb6de45d9e65d626fec51a413dde88cbf4c9de5127973995b54df2f21d9d55daa4b98bfa4056c9c94bc949c949739744725df2489deb720755afbbc49d7b5dca80720f8fec13f59bd685b279797171d22cac85aa8d04a62e97dacd24cbb2b9a3371ed91f9d7f4f9efc40acf64f3b69ee5251d9786417d579acf6cacadf90bc8b7d43281b97a38eb246e25d6ce3c0d4d54d7ac54d985cefda4cdaa7b98be5d71f4fafe0e995d1cd92a10359251e98bade16d38ef2aca2bcd194955b2c757458b3c7caad124fca2f2ce53c9945f969ee3aacdcb5e9b96276b13bab1da8242cf5c5b19e750ccd8c400000009314002030100e074442b15834a02b9bea0314000e98ac4a645a9a87490cc31042ca188300000000000000820030000030f5ba3b6df8e2a98bbaa483c84a878344d4b604ffc14019da9479c7a96fc80fc692e36a5f9ca7b0045075062ccc4d8598e78068ec1d7394dfb2aed10356561fb0587c09cfaedd38f052e3d2e184aea53e1eebf7ec97b837f5ef25f77b45a1110d4d31927952191c89d0811658e1a3bd962c010e0c9fd3951df86a8258cf69750b521a0e9898dd2329eee3a1bbbfaaac7dc064b15c93c2b082c7ce29c0e927c30cc9bcfba7a856e8b136d7949c7018b220e2e0e8c7ea63d7a17ec53a9555a123b3e39e6b62fb0e53b8186b9f59157566ff1dce0a2387490a9b139880632b89b1f3e9bde1c54df9c466c8164567bec965828b1bd728e6b8ccaafc998bf0f592f2155c4566b1ebe79180c12d0834943894e6701f3705b71603592d1b188bd132b4080fe777ddb3149e901feb4cb2c53a4e5506eab5fe9f55d14d990842b1b8b9fd63baeafcaa71f390bc2de396b30eaed052f1be6598e31d1418303f8ecb1c2c78427ae8a3d95eed9fb728a0a2b5876a32e9fa5119400b1f501cec3a5275b4eea20ba9c5091447cc29b3682af356255c2dcaa095329427dab4d59e202723e869521ed670c0cda76a90fe59da341d79249f7836f8fd2f4db7e924237fe1c774911735491ca16261c1446bdb29e4ec1afae4b96b5f50c5649f29bc752b51c3466c4c82c78101c70ab92b7b394dcad4fff0428da5e40297227a4829f6432119d4e5b9b8fc8a04df19bee011441ace7e6e85f3cdbb7fc63a371d090eaec6709b04de9d3a6b320aa27df9f3fd949682de2069a96114f901b706c739213b9334bca03c2c85871aa136d203805004ecf46fc34144a81036ff0df49866b0a126ef2b169f09a2f2175d896e636ddaacb1cf1a0dd6ae6bad3a61ad4a9a2a6ef2312d7dae4bbbcf00c459c0b651198e1d6251c5a9fb01b716c95fae1bc1694909d12f3c18cdc1aa7017533acd4b8afb8ec49182427ae18e7d1a067a00e2f46040f1b5fd1fa6d296f2b44f092093f5159bc2f46476d617c9e8fccab072daa5455b9c55d9a8159155d3c3ec2f3360519af234f3545a58a84c1e0ceb0c8aca87dc9494c6b4a596f66e409930910269b8c9dfab7cd34ecd5ea60fe991842601bdc8c41e78a8450442da692d13eeaef5774bca3be4368930031a3b91e6ab4f37dc345377f432def9736211f8e1c9a1cd233e5ee8d9d820fc53f2cf1c63b2b038e610b6226c49053ad42b200472f2bf7b5c3e3b7ca2a188e5edaeb2e93aaec504df8ee601bb242fe9598369ce2b2b48745acbe2f62c867a629b886e5b627bb7f5cd91a552fd8afc7785b0024aa305e75be86464515b2b9a2f88ca114eae87648486f775a4624d91fbe8ce2b2d9f42f0a12b7c30905ae5ab2c459260d101c98539bf7b7748e51c7a739e496afab937b38d00beb6255b2faa8dd81af65ee19c38faee8213daf16552d29b3fea20526f070930695bab5327adbf54b09c7128e797d6fe520a6081a9e3304f2aeb8d49a348df7717c8a3b5d805c2f6a004f410918e0af4fd338f58002074100b2e818aa8704299e591a49fc245af5474c3c7e924626f1dfdd456bf4dab2cef2e1a003aa6945d29e80eda6e89fc44b16d08aaa63864e0fe1ead746ff2fc5cf3c84bcae60cb50fb52ed7bbcad9a0db9491a43387ccb60228e27e6cc05abbab2110180d07b6867cf44a6b6d66e3615f9f727e2b5a2599f2f87647d43dbcf05ccce06670621c90b79f7dda98f1bab7e1aa75b94e2a1468819eff1c231691d845ab10872486ade8e1c99e0ef44ea7e221c2a9309fb8f910a9753546dd1e41d7045cb7d7b57bc44ee493000b504ff8070cd63c5e7cba851a96835177438f52b07234102d16f1fc464a42ccd4c9d3104caa5fe98a433180ed97384845c72cf62cdfc716055f479a0b8701feb94b23828dbe54a45f8da02f29dfdf2e527765a00df4e59a84c0ba9c4c33abe55cb93032efa2af9d4e3b6ac7507f45c3312e82ca3b1dc2f23536e19d2fa13a9a86cc2127ad4fbcc3d17d484b934880b896800d6a8bbba405f75b58da4b4c2626728f625e00b9699daace5f0dce48e4f09bf50c61fc19d9d4d2032c820e0a699d9bf0beca1d8dbbdf63af31efd3dbab6e6ac0932b2f6658b0952cced7c02fe59c705650652faa6ab2f3e12fa3ffa6f5ff1e1e16f5d07c4ef3f6b3c22816519c3ba88f82e1be7461e14f13c2e7dc5bd6696d19a30509855c3e99751a77a41550d80583fe0aa5aae687840e8e3ef98b77ba78963bbc1d65dee4548380fc4fafb34201a5351097bbbf6b69982b53c054bf9f404681c9d296066095f9c577a6b2759674a446cc80ab4b1b1bc76566b413fbe37f8eeffe516ee7edc57479ba631470d788d8b7f9c01e2bc6e0fd971f4d65f71c4489f52dfcb7f13130177af26d6111f0624baf955ef51d6c15838adff79478d25dcc055127d23270a63fb912635e48d4848ab2435b4a072e9289dada1918657ac5d83ec430161ee6aaf00aa2c4e908c475b2d121470c251cdd8f6a96b35ba805a0f1a0d91133f61fda395e40b11eea86185708f90b3cfa029f099695407e4ef8cb97c4008a7190f5877d1c31fe412e78c09f46b2e1c0b492edfccebcf2d7213c35becb7109b56e43a367a3eecdca4046d77d5a60ad396365616a4e1a81ec7ef16efd769e193728bff38c372b29df07c45629ec3d37812d4e9141b461a56ce000d6bcb3bb2a3a214ce1846d66e10fd307c51f7fafd6131ca12ee79fd2dfd37db27522be7f626e04d5e90f1ce3776eb7bb4d95d43b5d078c35762400b68d9d6c5da5d106ab81dbc30f5cfef43df4e9828defc52984bba11367581c2224efbedc761a0d41ec3dfb08a9abf78a18b0bbaea8519768d47311dcab1dde8bc5222d917d4ac0039bb203c4140bffb82c80f2645b275947867f37e9bcacc0ee45871ec2978b03838d64fbb1911c9395ef6b66fa137f8d8a9292cca2aff128c79d5f171e1a889a9a44e6cb4a1596c7b893345c48ab401b960bae40085861c843504cdf95a779790660f73d90406a01f74991002688770a82d0cf426efa04fa63ec55dc6788cefa26c8e4fa678cdcc9816e4c8deb222725d004cdb824cd647f31e04336a73163796752db03f89be21b9b1aefba2fff174a14b2592f863cbfdc8084abe671282e67786701e3de91b605102023c83200893d5803670c4a299a15320c30034791ce8dcf79d4289a35050916f21a60c36ac917c5676ae78301630706aa591ad8e4ca3547612aa1dbf1c5f2ec9b0468340b930d852eb0ed137d1326243d94383fa228a3ef5d06fe582c68b4aa441b6dad283abc603c6fcc6b586f877dec7106ac876da41220be5174291a34da7c05cc7f82d10a25d1cc0d4b69d5904bc8efab6fdcc55a8134f7287d079bb050fc802998c9d32d3b75268d549cc047ca5a7121ddd0180e66510210fce145809147502b8d2f09946c81e3fdd0adac4658acc52abff36d6c14b4081ab78778cf8c60b8065f7c05c4b37d562ceee8a450d9d08a61ae6ddc33394eb7877a1eeddcde35adc2bb8afaf60fc13b7680ac0cf235a3f1e8e9e13c771dd698e03a76ddfbcb7d1f7ec5e883f0a2c3c97b3f9bbd8c5bb67704de7a22f6200ace0b9f072a4291f2074a891896fa74bdf5746fdd2d6871bbacd4a741e8bd1c2ff5c5501b2aca568b72ad716776a4d73a328f8d25c02f85aeac99da3fc62db58673833f92a48f5dbadb9fa1e1c846380b65b895688a00d850b9cd4315e65f6dcef35d9ca7d739694956b5f92099307b61769aa9807bf369c4f843a84f1045d933110e29be671c6d0b95b42e44d9ebe07f6b86316737b1ca64a973a3aebf292a7cd219d978f8f4c702e51f64e447941ec9c81730214f701052f22d42d252bfb7ab739bf14cdfc91a11b5d0c7b9d109677df358f6fe9b6c1dc2c67723b01c7248e4ea63cae43359ca85df4ca0fd794a82707f6b0a8261f9157c71a860e4c491601839b7ae97214bc08a73e30272685ed92723e17a9dc61ab33901489a5d044875736b7d2cc253782fa3838b5f4750eec39f636ebf4374b512932016dc2de2c2a0436616a608b22656510e7c7758a6b77f0da539e4700916b74b5c7bfbb9a3ef7b7013743f7bbefd04152754b1b970e05e17d92ac1d413c1dbdb8f037886020a923e2e90c5fd45ab4db8113887b6670d7441ad25ff994b1f051115f773d6a8098c43d392e665226caa357bad37ad72686b7dda939a613b3cd5d58977615b830126cf65001839447043f43e169dfb3b4c2780246be902b3495e413df65be42c1aa48514a67bd57199a94fc48964ce6a78439938367f8ebc5fa52a1cbb5c312a112f49668f20be5fb30f5958390ff335715eaf17d23d25d9397f912cd823e353aab11b747b0cf4eafee3579ba76b27ee226076c6b3026a946c04280f74dbdad5f3051bc8cbb0e3979dcc7406f41a7acf352f364377d513d9a9a3b2167262cd423fea873bb048f5fe7393c446f00955efdfd51b9a2821d644c9814d015adb0411a1f5c0a5d118f86769608820232a608a141f0f2c868588b361c7e7202cffa4216ada60e0afa64b0dd99ade0104a896ca85482cc2781d4c057b5008ff62e0cf3af91b7051478528c7213ff494c07b034139a047d01f2a5f3790a74d3e941f90bde0f83ee36e2c00f97e18ffdbe193700605e1d6f50a84187541e822519bb22b55f87375a33e0171405d6d6602dc1d6813fed2513aa1ebe769f183f13eae63190f71fae8f0f30350cc973324360c0de61f0f121bf71f0128aac6a9fd470d7fde4d2bdc41193a8dd370edf097141bef8bf15184d9387dc815306c61ab8e03ab421fe2ee3f42120dbcd5d04134585b43ce439036cf360b2b7aae89e928e282f2c101e4f13755bcd68086312941dbd8246b0206b10c46cc6a3eb01194d22286424c695a35063e116db2b35a8b5be20035c41c0edcd4f20edd4594b35e8f5e7436ac8526f60e9f2ffe924057acc91858ce7aa37016f6268db87469898297b0c1d0ef7add98b239117728a710bb5a701732a926af519dc9883a1e1efb64f81206254b53e8cb1831ae04415b6425c27b7b32bb2829ee91638135895cb94911d4ef4bfed3af7983ee5672a3a045071c24c7e9f55819ce59ac155b976447617051bd95492d16580357b4b783b0ba35dda963e43548360abb009e9d1a08a2cf1043c6b74e95032978cda0f927955f9e0f2f87d910189757045644463f7f04e4f0f838a6a595254cbc36daef2c10bd37ffc9ad35cdd5628b93e0e32ac5f87aaed00b8e10ac7b5c3f2841769e6c2cfaab9c394925744d91f3d0fc0f7c19ced00b866fd291e18e505e04b87c5a005ae74c6a6abe3a6f5d6b827d3cf8b5d06698780f2e413110c3ebabc9e7cf07029896d9afddc214fb37394343764ef2f933d5ebf6aee6caa8ebe87901775303852370f1e6ef200096eb24e87b2c514222555e8d13770eb2ccb591c810e7d265058b510ae64aace07bd07fe658571cfedabd4540588ff859b755685ed5be4627d0af67994fa93117d4bb295a27a6db9beaf54b71e37ccdf57995c7f538196c79c24a07819510456a20cf0a2a524f7296ef82b68fa297fe458fd6c2da785307b352d5262bb44d4bfd3cefc164d5fc21da60245a63cbe962a38aab89e2932606496251c4014192597ea14ab238b3636d61a95d83bf5b421173271fd35ffa7404e263be157f895350a138c3696c584209cc5322f0b8ec6753b3f5e8cd7d9fef07b2724226e8c79995d650559659899056a4547d01f6f52946e64316c3863a2e90bd04a410042f58a2823879015a7401c12f284f94d4026ef94d21af7201fa97224188db6ae8e18a56ff3cb321962d957345e2f585341119496aa1fbb152857833135a0a1f13c71d294e1af4b7f11a5c2d5a23a1d4bf83feccf87fd267b1a056ffc638d20db9ed7d056a064fb5fa8fead59fbc5c3ce196cc5e77adfe0458a67cf84bec6191e6fd79cc82bfa3d2704a32a87199926659a5028b8dce382b4b225f507a9e638d274493af01dd6794f5ef0f3cf8ef8603752d2a91503eb53df141e89bfc1bf33fbd0f569025c3023ee3acb153fd06458ab7ed4c29f7223955857c2ed78c6b15ecaf939a6efb37708d670590669205fbf3d3908f362e71fa0c248159199bd8374435be84464e56a1d97febc04f1f596b20f36be1176d383a5599dd314216154add8705c4fe3ff10b207cf5a0b111cdfe79230bb5e6e293e232abffe4efd32b356a494f15e2ac3f939adadf6ebc5e38abff6bb6179439d866c69907940fd1c1ca5dc69a3b1069c89d59ac3d4ee009b86263117bc195a38d1b301610bcdef957fd5bfe8c35b55e89f1445a5b14e1258e0bff29203015deb6b9e4baee266160dc125b6a98c43cf2139bd15016efd32fa5a47284e0fb05705ca1dffdfb72ea79a3132981d49b3c0ef3e500a32f4b1009f6376a90f257caaa6882fd4586332d8e7649929c98d8b1abe4fceb579bc702cb8b0379632d9e048774dc19ec53e1e4620b3b652fc55b2af84c93db385a82a7d852ec1f17e3befbb045dc03a8f8d78b08f6ef19f77be5ceebbf14ca9b78da123656cb332792e3beaf5924fa9760ffd139a5b736f7d287d67d436f3e80f57e953e5c67cb2c60ee59dea394ebb4d33648719396a67b5165004c8e50a59dc747e39f040dc814f194e4788e081cb571e9c68ce209831511c595961915101de5bdf98719a5e027a03745167ce40ce2916ce5e0b96efdd82d6ab07b003721d48281cc67383c905d61709530a032c11819dd058938c5165cb54c190ebeedbd884444401c05edbd2c15e083578c29356113d570329d3cb89a6fb04a36dc69306901e733526e1935b1dfe186133d2226fb31296a564c8dfc850327e431da7acd936a7f014b0449974477df494d45adcb5577d9ccef7f24d602749df63ca2815913cb240edfa1b8f95562b4a926153d99d7ffd66d0f505915f3197bf0d537d50bcdcf93c09293a6a2d5c2fe8c1720d9e43755ed490c1efe92ca25910bc49a0ddb1b77317b3df2270a6761187db82014f40167044226e2909b07ce3ca6c75cddb9bc98c28304ef2da53b9fd490db0a533db2bfc9838762b3b53f57f6b4a77a96ebae7cc909c6833922e1a7bb21118f8bc4001ac959f49ddf4447f27e2c2c2f656c437363866b881db4d9369f4eea83a7c71a26343c45fc900c084d0d47fd751191e6bc994823e37173235b137394deac96955b482e773ac485183704ba2f54a9f7581377640908fd760c3f9d723a944f0c0632e0dbdd552b5c1c06d04e028e43707179406183b87a53eb6c85b392808dd3e4fa816e45066cb92bb6fc5b8860cb00909a92a5af94cf2a6833a61fa2503b1706bd866bb983583baea2a73219409dd3b12263da9f21fa13addb8b0595179fcaac21c8e1183ba79644c62c99c6dc3512bc80608c8dd15f98be0cacd963dec3546677813d98a0810a3a7a1ff3bc3dd821fb55ac7dd1983af8ab9542af825835fd7797958c270be77ffaeb9df57bfae93335ae56592cc8b8f9f4a2b69d2558e2b441896105ff7220897469ef3916ea26d0a181727f82dc3edfec8d4888ccb8d96a09571c944ef86af1184bacc39d5d6fa59c2aa57dc63bd975d9792ac221ec8ce3cb13a6c7ef99136c649e900c1d09588f255ad83c61314554a9c4cb3e508d93d0ad0b846900fda09cf6b3e8875637a022a24cb040f1d81326638a5902ff4ba85b35bf8ce9b06e6ca142ca016d3ed45735764ddd611a083c63fcc151b9c78b162f80b857b18a459e90a15d464b3392708a48490539a10368e1552be5846cff8f86645bc2fbc1e4c9d178ee21d8f1cc416d71d700480c0aecfbca8edd4ebcb4f6d6203e635a12e728c9c554dcae800205f4bc0fd12c347f8d3644744f805f210a309c76d0466bed3b1ac49be20ee193026d1fe7d5dbf01cd8bfadd04e456b05f4c143e43a2256fe2b178bf636d71a6977b22b3bd9aa0eaf35f470683b4f30598f06ef5c47c43b94937cc8ba741a558087d95aa9eab4ee43c126924740fc580baf5b0aea427b641e671460347627cf118e8a181db8829bb2f989659d5e6a82a56a4d65da108eee2dd84fe675409e3515043e3156e0f2895c7b7424b75d2591b9d522e097d37025402426cfc1ddc506c1373299ac0b71419d1594407fa4421137dd673ded746ce8b595685097b081c056cc79dd396fe09005f09aaa7787bd0454f8d095ef76e0e85ac8f4d05edafb8906f5d532880340f48f0e83c647a66996ba441219453c9d94d937651d7e532818650a53d6328a3db8cf089acf8c2b4f9cd1defecab70f8270abbe51b9581d9aad44b4b9db702cca87e8cd13beb9edd236884c31bdb3081f4ca9a054f8edc691a6f45b200868004fee2df524baf6110d6642137eefbd236fdbcfc9b56c33cab6ee288012785e2c217a24873aa760d072321a492a441c3cf6f5442a45e3cbf7c368e3392f42a5aa0acc220e2ad840449a7def04ff17cec9da22a9d68ec44976417ba48842765e6ea40c752355ff48520d7051083fff7e669342dc7f898e14feff881cb226b230fe004a6a7d3250ed8e6268923d9799169dca728174dc460b18ee5814717a63997eebc2201efecfa72d5be0279961808065b265f7e42c47b462e03c0cc585bc7bc4685e5dc3f90175ea6589ccc08d25df019b9ec7a17809b9b726601e6e55c20c94fc3865cd0cf2c212d9de2ad7e1d116beb0cc4d137d99fe81534b791b2fa43a146b928312e55a0b3490fba777091c76216ab84e5a302c28c7017e8785fe37674cbd6ae78fb9b72d289ec395ff4a2407d21dd4b1403a205cc56bd8d376ca8621ba32e7fd894971758f23d1cf92d8036f2bf6e48097999780b93f1d829dfce64b9948682933c4faee1d298edd4cc6b0304095fd092b240840af44637b5b45ce8a3c2a3c0022a96776cb2ca32b31a127e290f16e8dfee851d1cfada247e678b6f7aa98ea90bed1b2bcc201ee4c9fd7799c1f4b7f5abb1ade5207b6fa64892a3e9cd9e602ac5f1c63f1c70fc28e62e5f2bdb958e0f84bf54b12c312d96eedc3c49a7dd76b7e47e2434ff83bf8442baf8d2aee947b27555038165d28b99e269ee7abf70759296b55f400e9f8824330c05ebaddcc97f507a412d05eaeed624037ca39e51792f28ebf3f0338acd78895b4df6d3c922e1dc6b28a64255e83f4d66ce4aa535214007f3a76937d1405f6ceaedbabbabef2d49fe6aa235e991860ee626385addbcdc476e50dbc78dd66d2b48c144a84f63ab8e3163c141c42acc4184264c4610284f60e780017b405a5cc8c11c513160422e776529ed4aec343ee329e1849c5b2c70378316c6cc2d956d0b754b56a87f926077d852979b03045a5d832c7a205626a6c78e947e1f832aaa8a85819980220683f9a02382474abdb551d2194faf483a3990ae2b5d3b4ed2ea255e3bc8f2765795eb63beeff7651d2a33757ba47083fc487cd6caccb95467275be2d63261afd8a7b75e4af06a506ffb673dd59819812871b541970e74dfbbdc2a17261a271e7dd7240e4384c2a606345b541d6a42a206ffc0457faf2f9fb3eca0d7e20077fd749c91ec77ce3e5a4ba90a6f9c8854361eadfb8fb7f9a803d1a28d2f7261ae293ee29a3f2d99fe27dafda36c0f12086a8828d2056b8a8d00aa9884f1d62d7d51f030fe56112ecde7b7dd19188b1154f7b0a73bf342ada7b4cbc15030698b58d0118b08d5c125131f59becadffd70c1882c931591180cf01581f19fead1ec023c70a61e9f1b14d5e679b850de07613f5f27ee578b9ba718895723e6fbbec3ef5d60c17ce7b00578ba04e0dcdfb37d3aa40b9770407b3d608b667032434ef4497001d8a364cfa31ea7385abbb50ee6eccaa5ae32065050186d65d5bead9d0df6ef86ae7aa90f9a6b86f731adeae494270bb0283c04c6b0872d79035142aaa598073b2eb280bef86cc7da71d57054796e69ffa701d7cdb1256a6c4cdb4af14f18c994830101552cfee777287057b8767b750146ae830b2a02119c3d1c967cd2a74f6ef9844cafc772fc3d91e245665aab80a9924cccdec14af8a40b252704e2813146638dfee09aacb2944a59747f7e71096684d6581765f2fee7cb163f616bb35b1bf048da57cadb8b88154d4376094373f6c407a65a9f06730fa2462e1725e804fffda0393176cd1864c460eeca3a286042749080c0d8da3246362f3284f8e6e7611399d630d9db1d390924e8c082e11a0bbe7608634a0488995475d57ccddf7f872058465e2162f3fd55a112701a44de6eb5330c67f3197134f06b87cc44d4457de77304c8fa4d4388aa0150701fb7116c21fe3e19fb7d27dfacccbae2b8abb92eb4ee4c72406ff445307c1a9dea7f4af0042969882718737650b587eb5a7e7a0a00de8926813cf261e0d463b0d6b7cc9d570c8ae91506613abb17c5905e5c4897807acb284e892758430997e7e55884b54b23ebd0a80802bd02ab12a3918534031fd038331574541068c4ed8c0a42009409b5165bdc232799a3655f59c4b27708a5c54133339879ad79265ce403506d5a31faa7b3cb5b3051ac054ec0ba9bc5d49b4749f55a676f9bd7579b9bbd4b36ab856b5ad1d2efa0dc14bd331f2445df38504681ce06455de16d3e1309bbe0e2b5dea84af94e88e662364d333110697f937b46567474ee575eeb33ece55e254689ca022cc6815cb2efb63be568b8040e87c098d5ca825e1964050b2eefb654eb58f9b0f9c3d9619a03db55eb1b7aefc84190664ae8ca3c75db0b8bc23bf00b1fa6eb071e35e06aa5b636a83e726a1a275388940761f9511d600f349e048b14dca051c14db6599c16222052d0cc93188e7377be5ce4f4674ea8eac026540eb0fe59c83bb15d84f03d8f1abcfc6a4904885fee8a104270d56e11b0983806db86b87ef49fcd1effff3cbf93b0df79c2d16f868caec0bf86a842346d33d31f62bf7b65c6a00bd07ece0b099d3c05df6bdf4fb8d69bdf0590c6f775fbe95c0a1f26ad82e3d6833099cc1f3c96816902a6a8efd9993a621e156389628506241305997daf4d221625612321419b26e41076878604dd9578c3eadf3a574420ca26f87077ff005d62a777c5865849d66a48a7bf731a3908410ae1cd5f4a625efce39b75890d9cdfcc843f6acb6e78b3cad2a03dfc83ad0e390679f7c059ddf52bcf06cb875d998a02b2acb381898879ece1ff7a626f18aef8d6d91931020e25a0fa1e0b218a24325a233135a342479a94026981e901aa0e7927170b9f9b1b1e14cc4e283234f2ed43f7124e1aaabfa95a4a7c78482c5a7695c114d773a26644557e61acbc8ab5e3749c984f7881f8448299cf0f563ae77784ea26387ba7313299fc2ddf46ef7607c49aec1d1f14d66d8c3da8af210747feb0d43bf4d13b84aa3d67b35e02a4f66e2331f8dc33904a6a614425715900860bedc901414c559287337063c0230b877e56e5bc13daca84121b7b5892d4676447f80b0a05da5c8c5dec7eff53e228ed3b8a0869f142f6e1ca64f8e2461ad00cc92947918723d3977fb94a73f9b4e7be8e775180bbe111d7e047337e4e290215ebeb0ded5f500fd21b2beca59d349a2ad7fb66210f1c5f6c36fa8d69250ccc15aaacb0bc507e178323e7ecad6fbfc4424602af4681130c32901f81290a3ee605821769ba61a719ab5f20e9d307f52726203eb0508019904df3c92c7401bb2af9520897582d186f69e71c8903765f0601eafbfbb04c25b919f295b7c45c0a699c0002a4fd4419f0fb8a8c109a1b6314b8b06b88685e86cdd0a1a3de81c08f0520fe2476698fdba8307bb1c3c6606ca4a1ef4f8c68ff4346803c9796664875463de711640ce408b925ac61198cdce67fc7b59eb7073162318972487f91dfe6aae5167ce1cf399dad41661eb2f19187b60986717e077e25b30200cf643160c0001071539b6c31489bc4aee4aa2248ced1a7eb62070e9569d7a547508900fb00f56a537cd4c10a76fc1ae1b7dfa005ac32c01b8a0d3096e42c4a512beb67b6ee803f2fa330a38b204c6206395b4cc2d07698762274724e60ff68a3e9a6819a6eef1e3826e8f2ce5063fee5404ecb01107ae2dabdad623a8cf2a8d4a1b692805c45213a87dde9a344a09209b33cddb2d21da009de71669454ba98dc9a3b40398d908f9e81e1460e8d0837b2c2c42593e252398017079e755c854690475e314cab116dcd239a15231744f2914e3dd1ae017c12816f552ff8936d89c69414ac6b08d1c010da94f8998ebe706b36cbb9a3b00c0dbdfafb287fe61d8456df971f8d0194268552fcdb34b2316913c572ea2e491bf78615dc003ad19f3d7eb7bef86bf040fa95b5959bce05d06abfd7be672e2923317ec799bd62314c5cdcbf4706aecfc9bdb10b6671b685ec8480899666e18bc37e89f5e7781b8e89bfde9a9c086dd1acee00df351c18776db6af97ae38cbcfdf2794f0243d75e5071b0a25db96dddb0c845bb42c031fae3d3806a5d9ab778eb846c251f5941fcfa3cf1b0a631282dd8c05b310be628b475d55ec7680187efcade648f7f458a6aff8739944dbfe4047ea597fa43f2747d6a9a6ac66d306a97a4fb063dc9e061b4a4006604112cc6806f6a10a12d1c997110e14a44f1ed649cdbb8d2f7bacd57ed48adb563bd95b37d6243746bcc6515ae7a7123a621bcedeff90e552b759ea194f9dffac95bf3e6bfed276f4dfbfc62abe6d364f81a85fd396920f15429bda3e9b8a1ee59e1987d2755fd09e2c665602f09ddbb00c5bb5af7eaaae923da39c2019fbad023d47d6b721de28d1fb8c4d26e4c086000b0b11260da6684890e5406d09dfef6b32301c52d05b9482c75b2a254c35206b83623a96fd3f2af9add27a6b3661c9a812641275056cf61ab22002692e86dc2f353f24443f784f2ed302289f23ad0f16af168a13773137aa48a22a66ec72ab4e10b792aaadd7bd07abc0a1f2d7ee3fa6f9c79e6b6be23c9e76f2bded23899759c5de11e15e80dc48127756cae95675058f2e12db4a06d5abf0976e5a33c320e884b9cf85abcb9f6712f3d696e70e5286152dfcaf4faf93e3a304f03d1d2fb58411e304deb5718a31be334166130891ecaa4a7458821ffd529e64af3b40244fa1139b1b2e1c1ce0399799b847794281f1fec28dd8a94e47a5d3826eafbfa2e74d5ba3e2764a45d8f37ea3c922788326a5fb5774050b1f4ef0b6f3461d98d5b93adea7c861a4e67c251a2ff5b705b0f54aeacd0262995c3545078a310e63f8a1d1b4aae9ee7888a7edebe6738fbfc230c89f277d96d11c73db10706450f1fd0a963459d633c266028767b6b5ce7a638e64e9448eef02edcfcd217963e682a5830a6400a4745bb184f94b6d727f3e0be8d7fecc1610c37e03ba01d33174e325f2c586cce47cfd963ba4475dc028af424475b6cfd160aba1d31cd1ab2bb1b6fa6f0dda6d93700f897810b30e24ab431fbc0eee07482f99054af8298541fb80527507f1bd31415937df880c421a048e1a36ef60a90176f607b507e4e909fb5ce6976f739a647fa27d36285bab9c07f1b03b2ef59f0c5f787a001ac42371303b7952cb90bf67b8e95b57f92fba09735357b0aab82207036f8c8ea713000555ed538b93511fc933ab9fd11675114f6240b8817fc14cb68104c1aeac68e150052d00417e829e9de9f8e16a30c0d181610d4039810dd0343b8d0458967c43d607288dff07df96562c7878342d3daede240c66d52a4485fbe161a8379712158f94379e10e5a2421bbc4f711070d9c87275a8eb2c67106b28f4f7bd93014d036e8bd8581c71c3548e29cb307f4558706df3d77c019040df6c825b712a037c1a738c50757bd468a744e24ccf692d02580a00f2908880f4bd30abd1de3f24f045c630ec57311f5cb8d4855bc63d9870a1d4ed30deccb0075b283c1bf1e0976437179c43c8f086fbcd43ef01bf609c889010dc4f2638aac05f644a4854bb4dab95dfd1cc2d756c77ededa8d7f7664cadff9598ef44af0ca0d0d10a9c3c2bddb0f6b75044d0282f655e88c0af694e71594cf086a76bc56e1777826fa9f10d60df3ad63d8d63afc87dfadfd40e697b27d0e82aeb035dccfbdbd15a2ac449c105ae9c66db296084a94ef2ce92a95215a07fe8a97aeb930191336f16e8a32e1ded619649b45f43abe6d9b475db36305df8b8f36357ed3ac72a443aa050dc8546e01469c9c0534ae4885b6ad831ddf17ab54e12a0c4db22569b5083e1286121724745c305ee01f53247b5a267a597b9daca10d6cf9ca633d56de5cc5abab1f13b6db42214ca2047d2d9fd5dce55de13182eb919875206346e96ba0d65fd85f4dbd3990a1943565fbfa1a7f3f6353645164dbb6133810c65d4dfe195d38bae926349c7eba673bc7870d0d4a3b4955481839273858214a3787776c850dd3386f004b7396439014f46c8d19cf3a02a78b845537f978d245841bd4c435ec3e89b7e4f6d5a486e4c01a3e607ecc9c0876ddad5254a2c3cdcdb84c299ab8442761c24c49ea7735115555f273d1f1310134986f410ef75b3ba175ba801051cbf645df873f7d4a15a03f9d6d521e74bdb3385516bac84192060f37b7aabeaff1c9701f14df9486eec1e0341424636b48501d49b4e41e6b96dbdf3b6947092b6262135e624ad1feca9e7a47fc0152e5f15f0fd801cc98fc1422f2a455cb8f49185bc4ba3d78dc0e5dbab8022fcafbf78624f881c3fb8443faf93f6d3b6f781f6c48ff0453727788a4bbb76e13f3a64490c2bbdd8f321dcea1162801687e1102f49f1b4747fb6bd7d55b7c98409815708fd4a88b74db043751b372f2e2072def05b460ce01871087c7a1e4c4f71b61d27969d840e18c200db902c13c7c850a0e4f2749e6666d2742e51da305e51b40fb5821924b729f3e3d1db71a15e7f741422267539e2c8b891440132c7ee5ac96ba10c8b69cc82c6a2e4b2f8f0b10e4eaa6f6d397398db612efe4b09393b1ba6e2c47c7b7a929d90f0f13abe57617bb5b152b140ccf2a13992e15e0508fc9d3466ac4f382223b26fbbfb91f7fc871b0b1ebbb3b33a94739fba7819a551398dca9817a18be4ff03a9ff91920dca561c0d9d50919cac6cce5f779ca5545df6c0738d010263036443c21624be0b16e221bee116d9757230083492008d0df65b07267cfcf71dba1a90b51d3637396c5de8172a2164419f2ee17fb339650dda68d59728f0d647acefac045b243d4c9ca05f483c3e3edbc3ed9812ca50f96c69756b496274942a46a247137732d8ef7eece7819f3bc3645ddf17ac762ed837d53073fdaeafe11e31e52ca7a2e15a5d5547f8990b985240a2dc9778e3028dd1f21d07922083d7c6c399b8362656c37a7f0bda7bdc3357c76805ea4acd5cda818803b287a54a698d4cbb7976d319afe29c2c41e7c72054b5aa654ebb5f7b5b6594156be563da4fc1b633ddc552c5009738058faaedd8ef3bdcac01fc57d67b54f46c5d7f66a7b93f6285a6f2c90a18fcd5fda61da62eb53126b92a39daa68dc528e67934bbfbd8a2436ceafcabea5d4ef0baa411a60c518b2b69e449fc454d1014fc138b787778a88d5c81b5f48dcac86162705cd71b7c44a924f4bd5ce531a1975c8980e69ff4b22b58aaa081b56ec7f2a2993da944238447b368068406ff14362a708040c98aaf4970abc52dca344b94f50554a7f74ffebb2a9f1dc47703e83aea7ef108ce4e9f3c1e4ca35534b511f9caad196b5c614d59545afc25d4e7002b91ed186739e8c62f1c786f2d95d67e64cd34e9c1845d933c524fa1d3af7e2c117c6d7ececa694a3922c6245334248166a7687062ad5404157e8640221f6175877754d9582dc8b52c839e885a21b3541b5988e7868b5267b36c927fdfc19861ce86c42002afba26328626b2a3f9703bba49f318d1de2bddcd41feab52946a9a588aad482946e791c2d606207321cafd10b09dd5996eeaf76fe4437726c0ed8faaced6eaafd5b4e395100089e5037f630ffc4550397bd818d9204f80e10e3c25f1eecf02a1d2881cdf329d85f14204655f7685873943b148a51905d275db92718b21f4e8be4100290bbc04f5a029285c77da1a0db7edcded0ff9a4d644f580e9f064cde273647b6adc52bf8ba8ac2ca477131e4366d96961e9b165eb07b7b4c1c6f0b0a0a2febd8364f019dc1f79fb35022cedf4422849a9d061e272389b2456d0574509c29dcfcbd4096eaf32ed3615be1c17fd9f343a783368525c3bf2bdb1fb5e0d0394f97decb62fa8fc0f23a72d5b9762f7f01ec30bf4e65133f22b47659c8957d29fd35612f3ef57837237b1392cf7cbff85c97a58681547cbe3a84f0430058b444f6a12c1242919e4cf6d95106877307457e2018da838e6100092f3b2d5fce9f47db390ddeb854cbadce683e533da9fb3b074a0439f0093cf259fa38d0ec9259b0fa88631330a296a3bb4f6a5e967ee779bf66c7c07c83026978779f2be6198019dd41af814b4ae9539a837d9ead3d1a3a24a077f7d908a4c0509a6c6ba08445de45911413b0d5661617ee4c64cdf147bb8cb53c7ff11ee190799f956f1e164c67900b753b715160ec0343f73ef9bccfc3bc8fa37a3eb61102d104f4ffa38139af3f8e3c2100522144ccf2d3713389a1405b18b419e7514b2101caa227a97006831f1ee6d6a8c692d419e3d19e08ccb6dd3b6571a145d6fafa093b2111500f89f87558c1368e9d25c79a08f1e55973346b8a3655007500442988f6e7e8bc899fcde07519345b99a257e219ef770a635e9e8f351891aceb3a778a224b2e8331f829bcc2e7fbde973e09b38c8432b67b038093344457b47345d85fd7c6d3ef0b7fc5404ef978fd01e564e233cb7fd7ca77c26bc8bbcf891a5c037293912c41dd6c84441c129e6978113bc5162b2a8204618c89e7a7d9687dc89ba2061edb7a683bc28bd01da61db755e5a67d7722daa7e343937e50f7e0e81c28a95a1307b4ff23ff0c38d7b828c31558479d7fdb4ee01939e898f831bdc747ccf4063ec91aa3f8026fe066495e8b1ac64cbb844e1ff2ef34994cc904884bffa1539829ffc79079c20a06d815490c0556dc9d97180b35a41676284ebf083b6458bd172ef33fbcd707d1309a5d358478564a4f643a776764d957639f3bbe0d50a40319ef27c328114b0509184106dfa7a5513b808e8373c3b5d544c26e982c695a433d9057ae15effc35c44a95477511cfc62d1048a771075a7c8557284a10bf5156574aa1707c9e93c89980925e05538d8c85a61be465a68c2ccd35ad35d76f95640ddfc085bf93eba49864c8ed2db27e370805bd639724772e3a68be38e381774b5ba3441e750f65cfd01b695b1453da099ac9869cf7dec4ce204d7105fde3183c4d243e61391ecc253ca32256bdabf107889c07a6c2da98bf8bf8102d5a8b879d7fef597b6a47869985cbe2e250128a9b705f9748131e9d82c8995164aaa1d472b5043d3316dd2dc3bd2d89c47884be47f1d30eb62e9d0d391f9b353b8b5219be8fea1b45c33751f41f97a48d51b13a365b9c85950343434246967ed76e1655419e0529064df9c13d0a15d279e9cf24ebe590cb48721f624ccc013d58d198ef7916504638cca2eda7d2d022415e04900fb6201b6d8f7e742867f2a5f3bbac2a1a738fb7aae76edac25a6b41a0cfa1d16ab61307aa6ade1bf241904074156ee81b64498d1caa51804db3d3b6fb9b84fd0a78991326e5f4771e1d67026c8b10948511f5233640ee29fd64124ddd36fae85c257f13bc518a7a0b203b85758cafb0b30d2cf8c7d96e8989b8f4594208af8351a09826d31a946e385f44043ba0d7d97f8e2322f191ecbc664887ac673f821cbdcbcd666342bdcb25c279cced0881fd7d7bdc1cc270708307c7174cdca91451829a622c825f6c83b7886d8dd17cdd738642cb6be28a6f63aebdb600e178af47da289a64778c089d6f9c9377e1dd1ed2bc517d9ce71fd831f6ae79a7b3c809b4a12452103a73bf506c1fed362abe339121fb395b1e4d01491c6c3572fcf672f9682da72394bb51a935946362f98bf810b3e9f27ed99368e193029e258f637583a9fe29578729663a080bc45bc2687fe036a98a853656b36e29d6d8f5c00a5ae1cf764b83d41475074b9042233e7c7bcee071e3f4bd65ae913d6cc217e261f79d205c94be4f3306ee41361b9cdf8fa1ffbfa6ca6a54a0a7113a00678a8e3f25d16d4259b7419467d70cc5d19cfdbf0b45266896503ad1b759473b57a787751586c7ee4d9545391f393455165355d1c882ab22f71dcbbf81e2b7beba40f119baede05e754cdccf195a8804bb2924e0db243a90543d6a8f2a1a702cea3c7d65d695b6e6c466e9abb5c26fbb4efbc1feb211803e77d948d079d87cd283948c291efd07f568458317c59676155f152a7a8f4ba29187e586110f757010f3599425fc77cd285eb67d37c965328d4b11d0248fd6290eaeef13a3a27beacd5ea581fe856552ce42ae828d9eb06d8eea7ff613ff6a2ff3a152ed51fd1fb6645837cbddb5c9f42c22f1b3e3d45d4171daa47eb5b57c32fb11c983863941989137873ca24cad49001ca126ad98bb7b559faedbd993b6b051e0e8e32f1f64ef6171877a5c640a06d8dbaa3b28e3238faaee20ea027935b4c070ab4e61c0e28e885c8cf286a06a58c5aeb9e519cb210b299fd1148c2a0ba74a7faebf553fb5441d174049001211f2556518c2e53c17100ec48795bbe91a1135adef350f1ca92c804c3687960f02c8619e4e0214987db195db11bf2153d540575ca24a1cea81e76b03c5446b92a2a2f2bba5a80c19a428dc8a24bfb3fb6c06a3b875cc38fd15a2ce8817d39949e691e4bbffc4da9f38dfa6ad2bc5e2314f55108c3556ad48e65ec5d71ae6d4f1535c964505c49f498adb9d3c42e6550b46dbc6797d45a0db215c3c23375124bbd71ecc629cf43b88d99d51cdadf839ed6f148d605915946bac8865d54727044465f20c80db7504bb863f62b43ac6a4d6d10e1757a7eaefed344d703e4b7cd26cfbaea5775832005c945e3d64791e3741200283b82c6bdeb5b29db3c9c1f1cf4f54cd84c56e54649f91390bb1c33e3a7f5baac1513ab76ec0053a3bc443045158dad58e599a658f10df9cb5bc1c7c84f9ca4157313548f96c1b3a5029069707b230824be5a8ae48e32f7ebb40142197a79007cec3d0aae12d1a144daab5c3d98b1c195828ceaf92fc6a17a9a5495f1f32e394937242f30d1200df92a0cc548a188d740a9e47bff40c75d485e4bb87466829bc926330c8814bdac03d89832a55d2f084cbeb795f315d449fd12883308a181c9617493426d5fd1a35f881f0dc4b1b397967683998e15d265302fac74aad7bc9979acc54306551860640d8bbb70a8c694af44e7c957a1b495a512c8e0beb43fd5b080bfb00e0b57679f141cb2028082572158b72d17735138a4ea8ad370147b744d497b41626c711f5d1033d59dd31ba60a04d75d456a6294f7752a5091840c89701d7b1507e8c7bc2e6323b54a4dca78f5d5b68703a6edfd8280891adbc051066257a94428558c9b797be30a54bd3d98c5409b8d63d31a8469979d010bcffcbbe53068137ad5a3f702a6a93c7cb88fe6b7885cff06e40c89ef68b8c78b578c78b007a57c1bfd64dbd81a150b38565f13a95a5cd18cc028a886056db67c7dafd9f217b353b9538ab05043b695b38a4ca394e1685a92dc7f2916334fd62643df0e3c004b0cc06582d9f692e19421185f1fda4974b7c2c638ee03f15d718e4056b58f430ff7cb6ff8bf85de8480974a58213847d5c743c48b4203abab9575b8a9ac7f36619541016c448569c2c04a47abc236623d0e2cc81b93dc36194aeb41eceb4aaa61040265514ab1e89896aa9432066bcdc63f02a11cda6cb6bcdcd9bd9524e2a39df2641554357f899a3a4cd4ff963783aa473e9a620f568fddf0497ea3cb9552d9e29ffe2b06ee2cb6b1bedc5ddc70e6d54364a12d4273f142fa52db382843d2fd2d0fe24c9047784362c3be05cd4ccf5ad1b3668d1d5ed8665457c5b988f6c5c64f95d035045aa0451ae0c50331970a2b3882f65b3da2a9371ae55922d4904cf4d1f2d72a379ee965092fea0adc222b9f507e741791beacca308aa0826fb1f23d05fc36eb03c361319b0d27cea4e01769eaa8dd2311a54694ef41a97c96c9ec3d77eb89fc3b8a999cd60f9512e4f5b0c8083011cc01708fc8508ce1b50c18a0134bca6941678caf82683d14c186df428a01a521aad1622d3434e4c20610e3d63545a18d164d01e5d1a9cd61599c266e5138ba0e059cbfde8356c67263ebd5f5e48862e1619b0e4cfbeb0a1aae8b92fc540e8025dae8f2802ef80c0d81446d03bccaaed9e241e856416d58671886fd2f6b944eae285c79d8322b295b2f20607c6c450d6b23f60b82e5148e8c5acdc8003eaaf2d5bc676fe2d6bbcb90eb9d912e451e48c96879f14a97707d9c8e228f265680e0409672546b39cfc93b0c83b04e54bdae074e8c1bee042ad444e03f664a2608d42ecf307a6eeb527cbe5cdec010fc1bf92521d1fade7a0b625dbd2a86d1ddf9d0b3e2fcb3d3a9712f37bf37a922e8fd9343aa61b75ff71910061fcbec76dfbdf70e4410193a099c0d4c9175412a0841d3c658311ea4cfe970d769a7721ab9bfb05d315023a548aaad1124e4182d8c63be87722fd723c9432e8ae89804c6b25dd0dcd5763599ce547edc7c490a1ac34eb59062bf4ff2326b0eaa9582512515ff38b2269d0be1676727d912492f3e95474cc6384b248adaa6e55348e3e12fac713ea83c3bf64661c602e498bcf658d2962bdbde3798a4a0216fa1031a83828525d1694e535197227642d9b5af917542457e670455559b3888cb2bf718781bfa42f01155bbee5d5f52aad8c0d5d1977b4c13d62a0f9842b6afa064a92e8e49738413e18a5a88019ddc5c3cf6dc434fd0866304b709ce27c2e8b98427e070cb2c815d9b520bc9e20e8c446dc855ad70edd008f95eaf70ee49f57eb0845c87355b64a58d4442f7fdfa8d8b1a9430050abd9778d92d4cce0913390a3e076ccbfddad8e60891cda8ce318374b9f7f85106d7c84ed0d8ef36594ea637353ac946c2ec0c87c31ec498e0a9985bf37283f582b15b9239114771ce40754bf6947d1af8ca804063dfed420a62008a48d997d841233cf89f4d42b9a059730bdaf11f6091519964e9b870d2b013b2ab6c536a43290369e10a086376b9aa22db5f2dd33e6d0fe879e8878723e2d9531b6581eca6fc079fae82f6bef00a5e794a1f8b7f6c61b80c937d791fc2ea1d77851459c5680c310bcb2d8059b33d1ffce70e87f4978757ea74e0bf85b3519ec7373da899a13ce808df483adcbb3c26bf2416b817f82e739cee3ad764de48e6cacc3d02fff461a8c345a2f73f4e523b18e3d33c85e59a6fd4f57e70d95a82b0135244a1395481812ad7576345610974221c1bb19f22872142c7eefe2b53564e0ae9354dc73e226dc2b091a89ba69556e800f15b5a1c16fb7314871033f823c84fb1cd3495be7722b8122692457e108ee00f565f4204ab66b7e68a6376b871cb6c9076b28ff835674b2524ec82f62b879d67d25125bea78e4b7864112518f898ee6a1d633df1348d3e785d5323def642a4df570b026f167ccf51838bc1ee27f198c2e2804c3b4513451934ada98e15a64d09aa499fd9e35edb42d5d9a4ed96d6062843e938aecdca7fbb07a2c3e99ea7d5f17ed3dbc0cb01bafb316591150a4eda37cc66859bf824d115bace090b23e85397410da615dd50a9237017dcc1b1c0c6aae2ea4a8209a8736d6b22ed2636c53544dccb48303a98618583a1508cf12ac658dfdf685275f709f8153dd555e541502d01d54f4f32ecb016da2e255e1bcbc6b1bf611cf89ac3e394b87f3d322d1aa8ccf080980383181c8b1a09f0532892f611a60fee3d09860446d7920bddb5c777120256bec1ea259f6385dca78811932fde8434a4543022308d117517ce198cf37804172c02230440c2b83e49ed9fbf38c10540123b821ea2415019913b0937dd960cad281c55207a7729a728e72c21b098ed4b148b6c0613de980b4cc2f3cdfc69cc8d1c7c66b0cd9d9a378f7c75bfb2aded6600cc3ed46d20fb95431ec294fc3a6bd2b09299d38aed603c2343e1f53f56f3dffa691beaed29eee1e330004f5c447e81fdfff9a07fff9b9576112a62841ba2163c89362ed880fb743e0b9737c1042533a7f0be103a81a24cfb9f067538549c2ac36e18fbbacec005c3fa4e5897d0ab0c6e1b237168dc3dcdeb80abc8670922591ce51e186f8a7ac99e29b2aa31669a9cb635bdb3b3dbd5126564897fd8ad128b45f7dd9dc842524238e06f0e0715cf0761277109ad57f47d270892cce4b768e7d3436c89700558377563737eadf1f8e80929538c486bb594571bb14bd8b48145e53ceced539888f60c6581fc11a4c81aa2d803f1059132dc7a03ce532a95d21a745cc8462742b55e70a702b9c531f4faf44c6d8c6dcd450160cdb40d43bbc9ae16c4bc647efc8368212bf61f801a668c106b8d22515210862078389be778ddb495ad096f21d131c17b741745d9d47e843eb0801c7c5258eeee5f02cb9c67a33d16fb6ff9e47946a4f213e48f3b151a6a06aff635db2e440ad412964c9dda5e0c3aae43444d0f5287681635cf5117b982a9e02cdd0fe933a1a5eaed1995be00a45010e232ec4682654214a80eba7b3cd72abd06a7e6daef1229c06dd98c1d2f66dd4d27868030642fee88d0c1d4c5330412418051fcdc9fab8275f13d242bdf5011917d2b31043a1546153ea4ea86ba016cd86df92c330e8306df4077890ab2c77c5f5b175ba807524f5c9cbc3126e3db28f5a7ed16b701e8308723390f701b5f19dbe1cf0ac8772c4277b265ad52b2309270460043a116bced18afca6901c3e81cd2d365bc1d78ba93e98e7ecb9d91e3a63e888403cb92aad09bb5d9eb0dd98d548b75e17cc7c544a8b6a82a232aeb90fde5bad26c9220e9377fa7355f98ddfe263e04c98a0afebf254db8957fa782fe57643af70db4c67e0b2a77bbf98773c5c51ea60d1c440ee1ad48701c8cd653e2b96c48e795118e4a3962dc49036f1260a708f8cf47f21d4cfcd4e034de9d4acb030e63d812026aa322be3eea19ef2395289743658a331e6db43f1385268c2201be4cf24b11ef99d36849816b5c2c12d0fc88c1fafb6270361f0f0bafd20b4dc98c31b60242e5e5348dc35a47b3f5816cc03011d62596246896c03e077ae9480f5f73da17ae38010c0246555ea28f03c4190f4a0639a0f1a308d924cb7ebf206e5bc018c5832b2c31f864c4c2151e1ad466a2600b1b7953a69b9206506150d1b9c9115656b7f4e60ca9008accff7285910a868885859d2e1c5a0a8138c588f655d971ace9320d7d06e0d65343cb0f38b3796cb1865b3c45858d007e5c8c8e3201a886e5e3a486029848f6c76feb4a95f11e83a7e57f3113ebccb24c8cac19419fd05fea690c74af854e514f04af9c9100b6001bd205686eddaffabad580678fa25961979c0f114493cdeb024cd605f7ee8ef33312c5a74367e7c2cb3946c61c1e8c60eb756a168670bcb56d0b23b0f2b04ca025310f3f40bbb976b5c8a914be7676276d6a5da52a47abab6e60d54a5e7f5cd27195a400977f6c3802edd1bc054ef95a610f5c4a0f9be87eb8e77902d5ecd78f147b1b2705c25dc4dcaebfc0560b202630273e3124ebd022d69ea93f5c40d0ed961f4f9e15f58e323a556f084842b0c83d4a12d012c4db46ae19bb6a5111dfde6934c5d297226e27f069a8943ea36807e419f5f98ba1b2206e4c573c550ada94e46ee01672d1f51aba54ef063402181b6496c9726eea185166ae6b68cf45842f9f9bb6b63b35ecd4f95194705fa262f67983d2dd89b479e0956b3203ea4b683095a7cafd36f67362677134aeed368253182ab81478f4a707619e2ace733a8cb806de5c7752af0972ed845756d4b84e721913d79526ab3ba46224778f2ce68e5988fade68fe04a47939144ac24a519112655c7f2ef36eecbe779a0406aab8343c0030da9313a6c10b250f52b6a7811c59d983d952071da91cea79668b8c2f9fd0b613105e56e40e59364eca50228c74c32e079e076003d6fef076c1b4a2a0b9ce842ca6401ad6d59a7206255f1ea464c86443ae40fa057a79a2387c583255cdfc4146ab7e1b748315d138bfb3c28a2adea7d745c6dd32a77af3676d4e5d548320013a0fbbc3114024643d7c7cc1c99a100ccafba444b68f70dad97e0afc497a4d32891952ac75934819e95ae10b73c25d94b7ed62e43c01b2dee10ab89949f79d5d7184b647baeeaac55e9274ab486e6073e69611559228bed4fac95ace2086914fee0d1710881b315077832b860294b1abe5772bd359ab428053c4b13f37b4d0cc97acbe36a21f9aa4d8e0d0db4b3f5266ff522f82c851320e73cb2e32231db9c5ab9654b2822168a915f8bd270ca2e5cf40b4bca0f275ab3691370ace183fc93b9f7e953b18cbb6bbbc4b49526eafed4ad9e02c577c7f38b4e891ac1334227694ef1c593789c69809c60f9601a557c7d4a95016046e88415968a9ae55ae4631ddf4fbf6299dc3389e908979025d2bae391e2463bc7196c5e7ac7943a298d4ed2f6831f8b9df7124d8865cbdb437d2829d2804acac7195a8f2a3f47274618281ceaac05db3e564a58ea017c6a03fc9cfe09683f0a6476ad8ce5a4b4a4cb03637e34ed228496ab64812bc4a3ad31fc6851678d16a5d733ff400a42125365d4bb0df72dd84bff5a5b01492cd0da908c1391f30ffdfa43ca7150e5898c781ce706d0443974bd93364f76ab1612162a69064629b9c2dd0a03344596a60f4f2ea2a8881d1ef236cf8dbccda6ee5a2b4bc58ac1eadb0800d3191a33a948a7fb3314534642744b36430e74309d1b087b8c3843b560636a871c3db89d33383a5a27f424377a813733a9dfa8349be04715e1f652a616464c15917bf05421f3aae52585f21974208d740deb646b70d896897c70f9ddddf6ecc2544e6533dc35f1e8e3d8df3fa43f1b6d124574725c18cedc30623a793c6ee577edd9925314ce9028f117ce00c3001d132d0db34c7e3c72880dcc04cc63ece0765c3636fab004705bc49c6bb0565caa3aed98c3504bcff2f4a0c9c4e6c87f8c91e680f14ad361975d05ab4576c47904a9e95a2bed6cb624d4cada0e918011b6a0ab34c0f6567838866e4e2eabde13487ddb116c155f984c52a7c3ecd1508eab163c5bd8e1915843565a24dcc740589118bef14a98b607060bf4aa250f932869606508170767708dfbac0a4d174d744e802b7ed398799848bb8734f32ebcf97be86e0e253bad285955e689a08c68af2abbf34f1023100e4185b5268d2b801327827e331fd5a3c0deb2cc02977fd8b1851b13ca1546b0c3f1eba4c8b00a0ac5f9571d91fe3ba437c1763bb3b3052c297cfa6061766798981442883a48a4d6bf5da4ef30e80e67c215f83768c4355b513492400b5c4b185e7da9e4760969420465ee7b295c113f091291115208022f620cd1e43d367962e41ef2451b5ded2783865c25a728354850f23c1ea99dbbdbf61eb8f273a449df914cbb54e687206e2dd9cd24b28090b78034a91053565d1e7d1cb0f6694955befe3fae0b5ec07a5a120ddbfc28fa6aee9374b0fdcf7805cd2a00643564efa95b1b49eea0f19f3186f9abe56a0b3fb46c52205a3ef57e597f0d1b2aae01a0c0a2103c82b82f8e6ea214490d1fc293cecad755c67be317981234429c0b98d50aec805a3a9fc960a9b21ab2b464a75f71ec184f80b11970980e704ce604d532b3159e69c9e936c361fb0bd2bcff04276957887ad087c28f0fb9b22372b9db56b3065d9adacb13a7b51576988a2f29d081e19340f0555c9dee90f9234ca69db000054608215430085e03743dd5a7ae3e62c39227db6aa58accc97925b2c780c86a17d0cb9a69871bb9c9c45924104f027b4be7f77f4eb39c6c5a4d7fa4caae87b24307a71e97a01c473d81c2926e4d1d684785417c7f3e926fb2cf54a7d00c94665cfa8838de7c29913874dbd58c38c9bd3671a62b41c88e58ce202faaa67131890eb3f664ab4edbb4deeb8ad3d350b668b2259670d6752acaad840729af439ed239086902ccdf0ba24e4bf138d36f2d8f80513133d30eeeef4d53c935384536ac9cfee3b62ba7757620f9e1541a67e9a6dfc6da27953c4cf5ab349206e28a0f09eb57d4112cb2af9410e0c403b393be343b39c99e33e73ddfa63f15a675cad477f93b932287fdf58bd467af8dd405d89ece4810b36e91f3fe5612c94f222c4f1f696df0db9c86b0ded449770da743ae89bb3762de3aa5be168d21b9251241a32605129b4dc6723ab298f649c5a176683c8302afe181f1e670b6bc6b7e9b3ebb38cf790f095d991070a31e3313cf9546ec6a41aa1e92d85b62ea70ca9c133ec4ea006028a44bc60477df2f35271f2fe2ea442283799e277beae0c02e22488edb5828b4ca122348b4a4c1e24919185cd58385598f4bdee258b3ed061a85298339d34d485b0dd1f4bd16757f46e9b138bdf004053e7cc30dd5a3d8134f111a914edb60b7491bdc7fee1d5fc1c7a7b5aa7bf6b7b877f7c2337e53c0eb70056235b440725704cf7ebce7ca395fc98a9c7bf4a01b6e91d0af61041556fa1c41c10b411f296cd0749b03252f75a22d86f387ab2b88e8a237315aa0136577f005e5bf899f120ca80fff490ffd80068f79afe91d79b86d5008349e833eb799d967de5005198fd8679a9236f46bfebb8a76f8e10251a8faa27aa0c097a3dc9cd37aa7148871be27a69fcecffce87d5f7ebf03827c216bc98373dff7394c7e2657f29e915a46c0eeaf9aa33a48f1175025fbcf48b28b6a86fb496987ea63486d126d7e66f1698b9a9f6d381c9abfe6d27d76d4d1322dff5001c04e79c75229efcf1c9e70156325722656e02604b0676d3ed299d490d8e93844ed72fef151d2a76a296b2564dee72ad17c7e7df1de6d4e73c1cd02037418392867ba4f0b202e4a0908a46b89c110c14b8062c384249a90c0726609b456c5a2fe52c7d957a083590ed6db7b039484bec89586ce63e25b49b3b45117bdcb42fa02113924636006030c1b3b3957720158fd17bb80777fbde34a0c183ee48557e6d006f6924ecc6cdd34a1ef41cbdcb0c63a4d491d8362121ea235562b33981af16872e24f5fde35b56772142c4b7bd7016fdded3f686e7e63778baea511f8d14ad63b8944d362224c12b01f2df4a736da4321ab0a5ca4e8c031580fdb9b6bc0bd3147317c8082cecdba0de0942798326153811cd20902c92d104bcefeda4de735d49ed878d4f2cf8309cfe02bec035589d49807588640466a49031cd1a0442434903720d3d651c4cdcbabd6fdf7b922791042c32b33f3e2a221ca5216a605a1db5568a43cc9573b82f74cebb5a40da6111b54256c16792c5aeeae0a7083b71f510dc419db64cb98a8a8812a9a7af6cd0d139ca790913ede337f216323a82cf5143ef552c552299db2460ac985bfae99c5b1e20c3a0d4bf006511c84c5ab28bbe9a63f4992f00638c5a9c04246298f0144170c1fcc476fa24f2f9f6066fc6026c42715981149c882832b09bf3a13b7e83306219c8ecb8c93ba051a98d18729c7c8b1d920543cfc5f15c7ca8a23254fc5aac99ce87b51308e3027accc9a4654127475ac2e6604819e55ca9b88606712d846eb5d245414ffdcb2bff80e4bd217d8151b0e1d6bf9fc53501590b3371a3e2b24368ceb127e6ebbc1d40186e034691035b987e158e42f7a78de75c07b3a845d27164540b2ac5214d9004efca366299dff5d076bbd1082b6f33b0046c6100023b571c3e9cbd7c02b1320ad6049296848a0714783707cd160e9186611923d94e163ddddff72d5d4865d5b2a9da198540e2c3926d0575f6580255519aa42a86b7fda7b8d40d40a211407a2795a0871dca6aebee0fc8293085a568a96c8e548fd2adfdf97e38d97bee5abb106c045e3eef76b6ddb224c775805f4860293165f6292ef816215e1ce4f450da24d6e670062959a134d6abe61b80d6f909f0bb54c2cdef5085e3069cd81769bc2de143ca6c0b4f89f514565e2b235fec0db2ecd19e960fa10322545c08f150cd823894b4b255bd0a3226e98b654b3cc1f0e8b480e8e83568474edae0885fb9e1dc4e0ae00caff2256196d2a270211b3d5d98a75d6bc0b057705f56087148fbd2eebae8bd246d5de795db6499bbe86a52cc30a09d2d038e44ae29173ad1dc84146dd40e572c603d67a9da937b538ef20c8d05b18439c8cbdd98040c9a5fde76229c44892e1e6759414c610139418b9eb2e0e08487d3d5dd9363abc9546a00cb42b1155d103027895fce8b1cce6b7d9369f81af0c3299fec8aa782eb1b7e4c0c446d491c26f6c8b882f22057bef09f295fc516cbad8b86ce38e54714609818552e3910837d9ce3b3e0c04178d07170724f8d024eac36d7d490a40fd58b01916e05d7457aaaa41346d79e67d3658e4f81f8682bf2e0f90b58182d9ec6b747dc9b698a2065919732f874660378a53cc06b8cd928244fcbc27695434bce805d22c22869b875f733818463551ea752eee479bf81c56c661c528e2ba6db6a6951c3440ac5f7f7582b89521da0633b00217089edd33443219e8fe432a5116507910b7db919cb5b50a92a5b34d2c67d860fe4b377825d05359b80a1f4a456c71a125a6b099a58ef2ce46ab033e1671e7a3d7fac9bf771f25156608274bf9b7b2b0918f7a880dd4fb050c1b0f63d78dd1a3ab6b452ff01339c2d3b801ceccdd462a5be6897b237e01fe732d58fe6f6acc445da3414c2c373caee3b67c98ef40ee4a26388c8ba40cf741100ba0db510d0dc5e5f94a2c5906f8b98004150ceeb54237ffa06861585347f625bcaa54f2757bb53a69156073f6dd6df06b2f92006194187d0c2e6cb619d042cdbc5631e75ddb3f9d42a6932f25b421bd93a82457c7e3fd56d54d372b7d39c892e2a9a7b085e174a0e8f3c830d641f8ec1bdeab33cacde8b2cd39fc9a466fad09db509640e0cb90302c4c10df3b01de755f1eef5dccb7f59f617e86a77b77993e340af0f1235f3d17b67f40254d794f340b4acfb7a6fde2d19a85de7206274c83cf8469760814fb32184da9e3a187bc5a811e17463a7445217ec931ab416a567748e3cb912ae749ef19bae390f7ee3d41c5a30b3abb82beedc67fbf61a6d730de1c0e29aefc927ed11703fc987548d9cdc186ff54c636e862e8b535e2944e5005b1744394092118b1ddd94286f01d29c559335806059f1fa676a08d7eddc04092287068f06e725b6540c87141d2cf8992aec0f2b2ac593c9adbd35cb30795d8a76b1b94bee7205488423179f91629bf9f7e93c7de21fe1502db7769c73bf893c3fe8a6ad6895cd1fc8f115bb332c0b8e3cc4e99041154fbead3b540837341b034992c61a05699732ad7edd8ae23f34c0d4219d4780e42b1c5dfaf6fa7d74635837ff6b4d6bca9158385080522bbab8083ef1c73ae5caf263c2ddfcd115af4647e0a510c84d4237874b6e3f9367354500e0fba8e728ba81a4c826450be874e0dddb50406779750dee05908191e9a1fea8e162c0f622a31b7907ea3a9937290f61a2adabc44deae8d19c45da70872e11a440aaaeea844abfac86d6a0f23a021cac48cb541438169b46b25ff9671768960fcd19fdbcca62182b0628fef5a9ea44d47323880d76d98f1cd08088ec93926786fe531ab98653bc0bac189aaf4b15105540b3444acd1e1b6d764964f386bcb394d5ff9e87969213f7f2cb17bb9d779626015777c6f7c58cbd6fb9f7c5638c4b878a9d01db903dc58d8cea97a4ca0bf94c51db6f352bb3ea34a607abc293380a11397e8b2b06011d5a20a6d970161c5a8b2cdc2f8e011184e39441ab7c90b904f13895b0dfb674d3edd1f320277a2875cb89b6f3b13f2b1b9e70e32d22fef970e3045b1553192db76db2016b4e45ae49a92505acfddb9c31a8759c616c5a5a6ecf6e2e48fc47f143de77ccc72ccd885377899acea9cd4348bdeb231c699e9a03e97b8aa001360bff0a8760fa6e1f1a6b0c96e623eca652b8e164fa62ff188375682750492a89ff332ba483f2f4cb1a48df2b2da140414bb9b078ac33d4231d9385ea2697bc258d0da70561353df285e376b15d09a400fb93393ee207da7fdc7493be7e82e85ab7c8cf5bbe1570f7fb2273f4a79b1ada10c3ea29b3ab04e05d4351ec8e3b2cf9c0a313dda1cdc456c8673fa8f0d50d0e5c789cc143afd327a1c4fbbeefd533ea1cea5ab5f3c0bcda8343a8ffba01b95ffda22c7bbf8551c325eee72ee52b670b2ae6a4e7e859dc957dc823791101d5bc1dc53ebe53161047b79101643c8517a04576585d6020ed98036b0172d197e9ef7ff06c36a859b9f42e6c55f100551adc7c51183b8e57fc2402ba2246aa3f5ce26d19bc63573e356ffec87b930ae2db838f80f924546b9bfce8f26eab5679b990a0bc4a24849aa2157d775ea85115e9f7e30332042c8960bb01561dbc70bb1148e48dfc7a7fe62996ab1e04fb52208e5a68df246b8b4ec46ef4fad44147985748c2af173350dafaafda53be692d3200c24cee724c0f8a8528381ba875a9c465fe8698f279c43d4939b1922bbe9aa6d33d7ca32e66befb4804d5a218e2d8efd7df0890b29103350f5efa5d64545a88fe6d0de7d9cd5faef97dac4b44dd32ea86df17270eca9df2c96a9c5a96c123c1538656ac9ea9ad4be3801aaa7bb09bc56979c11745f23181cef969a9a6fbc0c5017d40b2629e7b61bb104d4c330d501f764892b3a632f27ca97e979754d5415dc63adbc9a548ddb387dc27698c2005e9e1c0652d5cf897d9e8733459b1f10e3f39adda8a8c037c84bc25157cedb51d6fd13df18fbb0692c3f840ce705178f0717de81f020f36f2673230b8888e901afb884179dc21b57f0caa787378265d21358c6be7ac59917e4f1a9bc3b4be9962df93da2c1222471a5e40598a52d2975d79084469a8719498ba46bb267098b14a450c549b2888048cd57910e5ed99e8e31b56c49b1a4af2cdb47d775ce46814002610e4bee9d123885d8229e7be05db4eed468d9037bf6b0dcaf563a120cbefc8d4f5721949637d3019d81e16aeb3fcff10fbf10d6987629c6da88e830fcb7277f6c584a2fd95da0c51e345004ddd11f9c8ded8601ff493f5d128062507af620831b594feb4c470f845214fde086527adcd7580b1e88be64edf8febc05f4beb5092649646bc2c91bf5e1d952a5821d7839247e59719088c5a74cf05d57f5273a095a7ceccd7886e198eecd22f6eb9570d204d5a35492ceaa872704f5b05625ea64546730dc2d6da8850072d45bcea24cc3cfab7b11028abc4b1b0e42f912578b02c174401fe46d1537af883456f6d4faee3d14660dadc20223f0a642344946727fc3adac8cd9209577d1fb2555cce10adc6d9ebd0fab2c7cf9e4138da1029a9c1b621ccb6c9aa6550b3d4ec757f892abe1bd6c501d4022bbd12e02a586c08ceb3047639c21184b4de572e357bfa7f06af1d8aaf28c744a4cc1a13cc510190480f4a67d1a4a86682435ac727e2fd811dc23cbefcc2795efdbad39bdc364d1b46bd3fd39d4707fa03e369741b89d20ba02074bb7c0c6b814b3481eb092c41e017ed6dffe84ef992b8021303e488580269456b43b2ba8d1d89c62746a023ac6505f9978b261c59ea9fcb04e02f4015ba5c5b857161a8df12fe60504a4df013571224c167243859dfd2fe3f584f4fdc3b5f0a1f206e3241e55bca1099024e017cb9510b3aab82c63b8c3fd68d1094e6b0be70c3dd2e24c9683a320d83d3cbd2898166d86937cbf2f69e1523243546040fb5ca7387304067eb06e2145d4de0d617364f075fa1891ddb9d007315a9ccad130bf69ecdbdb90e1e6494fbe57e0e3c206f0c099f7d0fbbcb1d6c6b93ee83d1fe90df14891b741f7de86bb0f364f02e7b1d9544c27d6dd6d023bbe6fcad6287b220027f430736fef04e21a84f39d26874fb52ce8b22cac9a0c3abddba97d061df631a859e752d637729faa1c89dcd0e43bbc0893bdc3895039e988c33f83a291d478d3ec0e1f3e354177e50d03ffc81d2d51a1d0142a9352e9e486c27d1abb7f555458c5d51fef0a5d3ebd4569091207b1d6818b192c6a608d6583ae8295c2f8000531e67a25916bd6faf76d1f209d79692b39d5c201a695588e0535db7b9a810ece1d255797a500abce5c8287548dfc65c736e7d8ce20c492a9deb16b2731eca97f0188d18447c062b33a7f8537fab28226499d7f46b4540ac227ae99da88db9289cba1e5b684cfb2e2e2ed7e4308b5171e11039f647ecf275574a214c1a64cd8bb28d59c760a439ce56e91a740c04771ca60918f267114c6570bb18bd8ac580e5ed793e9caee14994912dd2e5d63e19190ae32e606854a651dadfb10bc50d4d31c0b3b0f6ce1b96151ec28226e973278bf964a076eb08ef1ff740aa5a168a47538a3d01078e2d681e3e5830496f57ed1b9dceede5c4479add33d8b2840eeb25883ad2bbb2867eec7b0c87ff8095b9b63bfddc08e32c38c2b67702c9f850a0e4121c40b03ba32513e9309fa0f98051cdc11bfc15454a1cc9c0b8fb1d2c9d4cd603fac00f0f0a40becab74c1032b4204e003a32623136278cf4dca60f17fd4fcaf41c9d4e67f3eb495d7a67421c76c08e55cb1901c285f36b9c0750b32eb5a818102066b8d307e72944c2c550a3cd65f8c7ceb5f173e3ae4d62cc27386cfc55da93832402b880c248c19efa30050cfe496e9018afbc96f4c2036ca1e7e1482b9b84e03c2d70511582424251d6a27ce5724b86a231a27dedf3246e0e48808819d3ad18e51e1d33435a98b0a1adaab2f25ca1bc2cc770aee7e2fa03e65ee62e3086d1d0aea39dfff2dd54d17a6039b74d2519877abd59a66f4d9f6698fa2c8adece5e94984ab346f9adf6646f319493f6ddd2eeaf4658f23d1161844d9bc372928dcb22cc804e58ed6d6113dcdc25677859b711b3ee808cb1f7514aec3dcaa6a12014e0f9f558cbf00f316cdd6d4fa3683794e2e86ee30e071876775b72bc36916e3a1face6a5d474f77db9f2b2ce1f800d2198a4cf69120ce2470a6144566b773274db49b97dcb01a97849a6c37638eb5013d5cf488a0cdf02cc80199ff98261b00cbd4fab6d203241b9afbd55f75b5a5d22a946bd84411c7ac604aea039bb0dacdde39a5015c27d82c6af3f2331d3efd9c75af95e17c5351631851e4823c88685ec13e17cf1bb8ebd68588c3b52b615ab617cf77586d4b833f1af915062bb783802e17e4d47ea391518e07a8931e39d7c48261a3648aecf3e40f6b75522848c8e343371d602a3e6368f8471ff490e00aacd31476102ff4eee093b2048dc9c198fde673fe1f75d559a887435ee643609e2737c23aa3848733cb3463596a3aad037864685281751b1b8ded8fdd5ed04684c42211981d8844b2ed2364c24cb0a64e3d8e1bc4624fbc8ef85a43ac8edec9ef126cc79b8b699aceea036e1cc0a58037aa9c1fef4b13a4b0614f633996a5a911182cb65f85699937dbb8080729f97d72c0fccf595a83e64ed5225af763d1a48b202952de239f0a30cb374e0c9a7411248552a26814c3b575efa17740584551d909d1ef37016cb2eeef606ecf17b6f0d68f9e6dbd55e16c3ce448cefd813a5519ee42717af5549a8a8358599d6d4f745a3377e587bf3049f431641eaa4820ddfaec34e2a2da641fd7f62403dffdcb085089eae40e3b96ee5e891b57841b0dd095c4d93deb2b1b07690c301889dc0c9f50752155d6218dafc3e1085907fda5587258ece263db6c62956808b3eaf806294a7eb8d276ae30e2f0b564815eb9176dfff0b9542c790579588d7cb5cc565e36437eb15758559a5823eff07130164fce1f5a150e18339ec98da2afff37232c86074b16b81f5425c02930c48b6ad9cab70b195466021d8bb00425620b0213c9e15c9db156de42f07b54b13247b964bc08ea18f8ae2ca79847ac7ffe44ad23d6ceee09e3259fb54fbe3f9318a773f15bc119da5648bec6bb0cd0c63cf1035402f641e11adafb24a1ead184f56005a5e13fab468a474e8e1b85a9a4130c865cbe57cadd4ab86985ee5a79017bb86985ee5ac91d2be4c6f95eee56c24d2b74d74ae2a60e7a4cf8a5338193ead63f700e804763749648292a3008c4309905770147030e6a6302e6c3161eaf00506beb1fafa48ace74d31d6ae9ae96417fdef0e097cad048bdc5c00d019d4a860ccb76be8a4e9e31415bce13773ee5c3dc17308b21ad441958ddf2e244e2e82444d987a6fdaa911f84f376c9a29c3d07bbb21bb8978d9c163635db533db162b39822868af928868304303cff0d386c5bb277b806702b6d626a3138c800b9cce46c19dd2358c02908c870720e2dc1344593ed70a1a9375c74e5c4b0a5d6c694694f7142b30aa8be25335c88099b33adda090443c4b45a54a931f032c67098500db89281841b0be2dac74e10e7f211d4a982805a5dcaac8c1f7c00d2380c5681f248f260e5ba82b36a3a161ec481a1c8f71725c0fe11b2c1b774026fa7f0384361bf22c031e55b16d28de490c3383332ffc2335cef86818c8ae4a041b4f4fd933d83be616b3db949e6391cd3c4bbd0332f8e7cea848d938ea9f78d062335008cb11b954d1410576a6c3068864e0c9edc488a186412bae10e739772f73e56367af12f42af77848270746a732068b0ca6f4cc665931d37b02411c8dc75f69115c19267eee7cf1a167b8c89577bcd79a0d8bfa1022c5d9aff1c8d2d3b7cd01a752281a0afaa0492541d521642714019093ea0a3cc16ea3a36b090f768c362ac23ce7131169caf1f5494ce8ce1fb3eebc34674c814b3daf16f200b8b7e1c3e8118c6ba7c2e79147ee7bd301b309d0d45a75f30026a286571241e8170820c1afdcd925c6c71a74994e49e1f26c5734887e17b51675c375266c5c9f9d590619b1e3e4d805100666bad654d8a1e0c8ac5b574750a6f35d017d854a3b1b652ff38bb9cabc2a4ebedf542ec32741e3c8af52c2a8b51d24636d4cca1d20d26648d775ae5d2c80122e80a6614ebb12c4d496001f49d187c282fbf2aa2bb9cb1b99751f674b4003388c7b069e7b7d4c6c1484b2582504abd857c14c296da3848ac147db22e8a479fee75f5df9526b7451021c0f56d8329c69ef7c20291f94325d08f0c738a2be4bf806703a75e0e3c655e81e2fe7f1c4df3e73e85b0abab1deb740105716ee106b0bc76dd3cabb51c998372a39b497474bec19bbac4c8dbe2241d7ef6e7644439901e6bb58186323332f691bc2e5f210e7e1dd18cb20d8fe6993f89f8ca575e4f7fdabef6dadfb818fc8fef82408a175a84d06f3e84a0931e1e8970a117da35318bb92238948cafef68c24d8931509144f7832543e4b52e29a13822128a33b1129471d29009d1bc947090bc1b1107addebc2a987d092c5d7e54caf821c083a1cf0e30e1935bff302882c8560c511476eafb143218d155fee461780396c64cc1420d1cb273da17887feb9be9ccbbe10105ed8792bebdbc71661bf123d4de9148039224d8fad87a6ef3126ab64b367ccd59e1da1074a99e70534ab75097a5c8097d4c4e249fc500980fc2190c363c1c6a8baf99d21356f4eb5851ea5b2d70bd0f14ed50eb6c8a07b8aa65b4ab2666f0c40f8749a061f1707ca15365ecc097d30191a2bf2bde3b1fe5b42187c36efc98f4c63d42531d63ba084e32b420b9975eb0de51e3044c7c9cbaf5ab86edae5085015f23fcdf905c3554440f5fc7951943969402e6bfc9016a646e81f8a77db5c993c703979cb1b6fe0d8f7a9a87ef9e242dcb6bcdb60f7a30760b60100c176482f0356de2937c6a238e23b4b51f02a514c8f4e18f5d3b7621b796e1c729a00b7cf9d64e10590d370aa9939f26fbe6217c15482d84581de6314100fd269534cb01bc54269ebda266fbdbd1383987a64d2f645b13b65277f47811db094bf805eff803da9461f2647e9de12467493d203909bd6ce2104af3427989fb1543bd4e62d232debe39c67b7d942b03ae0c59d0e897cd08e7345085317d8acaa80a5ba4485022f26201574abcc0ad08b0e1026a583cdff10437ba012293db9073c4b795525db2976f44e7c436450569d11f96d33f1df0085873319e4323f6d1f8befc1d8308ba2cbd08d0171fc070c417f2bdb727a02aab1e9a166d267e459d0195ef674f945e8d5416919fabadf2ac9c615819122e4e715171c1245945b7366ee41ab4f7534fa6dd86783e846e16de11261681f68dd89562abc69263c2dd60a558a38e2efea6c89de9c92100436fd6284d9a2998a453e1a4edf6f49b1a4a5eacd29b107d86913c106db2c349e846d67734cb2204e9c5c63634db44494a16e5dfbf35cfbfc618a8b4c5397f17c5aabe044df39961eeb65a5aa8e02c0331bad996e669e355dd02650d62c043fdf45572ea5fc37324bf218f7d129050b2fb2568598dee86946abfea65e7c2756e11e4ca5aee69e21805828209840c3f852accd5e810c1fed6b60ff02ea40efd964b95c570950c67c38eeac80d1b21fed5f1ba17e55922885088caab14e2373f55ce040a02718f78488a1a35e38351171ea8121c6abba85527659d651f089dc5fba15866e36ac60bf22c5e07f6a2598ee19bdc442da211bea341698df30fc006c9e95a0367e31bdeace8824fdb1a4f4ef9b6a690c85edada67370a0886dee580cefd6024b222144b97284a3557d2c08c391363df27eceb072ace176bbc961dc47ad65bdf798de72673a82d66f6a169cf3b2de60f006eeaea8b08997231c51721e7a1c6a724eed2bcc84bfb36f3d6e70a30f08e5730a45d466379a3cdbdda699f4056543c0d254471464f47e54537a460b9080015a4148041c3acce2b1d4eec812da5f93b3d6501071fd5b11a431682f468bacbbcc82b3c068a8a349afdf156018b9a5d74cca54aa82c6828b1a3b31349f481c46610bd9f0bda1492ac5761edc2b2fda13f081b2a304283b757456e045d9f2174f799aef7b5f9492ad0ba688f8c0ed0898d506b16515f88e4ce75951e6140a6209240d86390bbea6291ec5b925291e0a3988e65dbc183ef4b0adb9dd5f731f70eaeb248b1a4c51374361b064000d8036b850691cb75b5711bf42cc648b8ada427d30d33880f4a4e86f8e77053296bec3591080b2c6a0351ec0658ef01004d740b1e055dee7f9ac7bcca3b0e46c6171323d6e8f0c4d13c25cd30a7260c973006ddeb58012ad61c395deebb405d3ae760fcca7ff0d9ed1bd36ccf88bc8bfcb5fee79fdc2556d1e5cfc7308f9808c5cc9289ff5f808cddbe769ce099ea696f471b776005bf3bc40d6608513fa2fca7be8f9ad1fa2ca57d23f8d75f3924af24b1e810b09748b05b90adedd91b156247610ac0791c697877fe1413aeea201a85f88df054aa098548851d96dd0be752224852407e88a63080ca8585c38bfda80ecef1068fdd0c85f05a4ee9b74bc098a2af580c73d34fa65dce720b7b7eb5408c9eb9afb04e61ce32625bfebfb65e70ed279d05f5c1385733425502925cc6b5353792c8556438f1ca60aae655c5857466d9a9823759bd664f8fef02b7d2b27e513262d2c62e4fae0abf27bbe46f10011e9bca1aaaf7265ec8780a83f040993d832f015136d4d52b1a4609b6f44e126fff22f66dc177981ae019829f18b1476463606f60e48d8f6e15d45566f8cae420fad98ab2c01229b5d51f832e6fee8c34464987576b465b41b37416d7a8b5b3e207c6d911fba94518e8109d32d95d281f929f191dc4a9c6560571c89d3c853f0306f6dbd61f0c649b821e9d30b85a1d54e8208ca848d0be42ba55a2293e56b7487f0f40e4c6f3a2cb128d076c3a20c9e04006902e16df621c18187da0d9b312641af9e72ee2e3e880924dbfadd368a4c1ab6a4f01b03efbc5c2059ca4478bd845bd61e035699c00e7462e1ce5a7ed50f9121dbe9501025f96ded0c3fc6bfd90a1e26f8893b724f3d1dc94f261f52ece24aabfd702c738b4ade51b0488478eeead00ee442127f62240de269f6b976b831e71bde0746a8adeda9bb5f7b0e963ab072e4ee9097cc4c49fa10eef85f872da7a1487cb32f8d0b2af9c2ba46acd99be9f03f4df3aa9d2d597cb9d559e726b665c9576ccbdf12cb788cb806694ecec8a26b8556a8cac06467d00d5c432d51bde2c6d78bd84c23e91bf9cdd03aa91c3ee1fc58a4a3cbc56054e2c449d0c55305c15a5399981a229dd53793ab8721757c7eeb17b2a9bee5d6de52a6e8edd6397591e889ec1a5bbae822dd819c038e57c1958827689b5b710ed22054226dc80d1c906c0601ff60899454b9a6dad7513111112b203a50c9b0c4c0c138b2862f434ced80ad1a9c9e1a045b0d6a47704f35c5d85122a749478ae584488ec6e6557db40936b222c7ff1b1c8bbdbc9ef222c0447a4f42b7774d16569acc7ca47788a1ea2b3e029446f1c8743741d0e4aafc331ba0e47d774e9077da4f6ea2f6cbc15cf62e5182cbda3b642938328d9d1a18aecb430079b4c80ecb4b0b324832e8a63edeb7165f8782d10a853f22b62df7b57cb52898db8865264156cffc4888d6f1ad7da2624479792d2cf39088844ae0d19d295bb954b93409268743d0b878cb019f8e0a327432a628d48834de01c1c099ee52b17358e33b0353b251986e14ca3c311a65a4031a8186c3c8c1617c59161a0ee6aec2a2c154288d48aa374e53e2cda08f8b2b80f87d63cdb35ad556a13638ca76e51975601064bbfd19aaec9f56a395317de6a9d1853157e46328fd6627614870bbbb9244d7429de071051ae8f5a86af5ad04b71b48629292a1736535d0b80e28d913760b3bde72b50c031c6c8f6d9ad5f8f7f176567c4629fc7deef18b2353b21cab6fe5d20228a43b5f882bd22ecd6cb73df5aafb05c340019de723a9bf3c28a31c6537fb9468a6297a2225a6119b5c870f65d9326c99417ecfb176cf3e811638c9f80c3e28d7e7db016cb65f5fd215befea7b65ab2f7875add646095b3e493d5d45961f1b6c3f073878746b994717ecd6356da44bd0a64945baf4baa66bba8a26c1c7ae4962fb220a87d8482ed8510e6aa335f8adc1972dd82d6ac1c62adaa4c521ae06a55186e2e0582d57a48eb40c915f1138e9119905bb511c8a3645222bebc23c19c514a0c5164d8f5013d5a288ab297d79c91bad89d516f6bd8fcc215d7ab8886913f3b070259a02a715c195684d6b5042d34a4e129d8793db82a7298040bc0421c4101a0923c9f00f0bda9852bdb516385a746f515353535ffd46b7a8544551af6a156d72cd83e24d664886a2c75414a5349e7e2042a4881118e5a495aa7e70b9220f327ce441bc8957443969a52acb72b95e116b41486ba089058b7268b9be48164c323932ae0635afb871f38883e1d15a1c4074ec9b157191e12b51d7502d2e9c87e5d070b050d3261d90041f7dc4115d5844bf68520dcec497b8a2284ed71431c20e9258aca53594c915ec366f722cb48143201157e351b556b55ab55eb562b57a6acd6ab5b5be1b23f71281410b54c8cf00f9e1eb88fc80b85e9197ebd9bd04110fc51b233f77b582b844ad491cd82134120b00dcad1934aefec2b2bc1bd88e017836a7d3a90438dcbca922432b3c242c2c773844f8219171d145788ad103445ff90afec048f41519d835de1e4e7eafe53e246d04fce83a485ac9c9f05dd346c0af5c774749bc538e6d5af968c72957c1bef785445a835fb95052c1f6bb8676cdaf75cd13ec466b5c924f76b78cb85e115bba9c33b1dc3d2538130b5eb150c822fcac90e12dd3f5bab0a0ec4e716ab55624fa29ceebc2da9a23af0b2bb58826d12dd511dd3ed2da14accb1bbd14ec26ca4d7120091e5e46c16eaf0a89632386266bc9746b3205b82414585e235a48ea5880cb41d3c4e16ed5b8e413ecccceb518f9fd3569edd4568dcb86661d71bd22560ba7b10cf01bed6c5cae4b29d9dd3ae2924e88cfd1c49102740d34cd1b68bda0f761711f126ea0e925e191e061159064f89e90e1b779843a68b766d2471a8b36b11cbe6bda069a56a4ce14a48e732588738be5c6b67dd114a40e4e1fd6a821b3b0540d2f20cb45f196eb073b4219a7a4f3521c1c1bf1ab545b1e6ca66f59ef3d4ab5e812565ffd7defb11fc5c9a15b6033afe17bdf77df91ee1729c5740b9ad31a9cc154a7358829ce7b1137c581d8dd60be70148f7ab1b5914dcb5abc2d1ad3b8cc8512baa44b11d32a74ba347a98d6509bd6b42f280e8d0b23cb503132b4a70c1bd321998a0c2f9b6037fa8d3e99778af3228df401e9525bb0f176156913c56912a498071936136ce8ab02a74b6e0936d674c9a699349303500cc9a39a80104211ada15574c9c6000740c08d78651c09a01712981291d67489b21070398aa3655984ead28db6ac8c233e1bd4bb43f48baf4f2ea9049bddad0fe663e522dc0f109d05770dbc418dabd1376dea9b216dea217d829650deba86d6c4f8c172c0dc49b0f4db3b824422c1461f3c5c0dea69d124687a479a042fc64d1846be20e285cd920c214e4b9ba04bc245ae9870a53e81d35ca9ab70193643e19658d88de2f4cdbc621e7135eae12d1c09406226078d0e4c891d17c5141043580e2a3f8bf34e0ea6ce5cae4b43e4ea56f0b469127cc32619be5e2a534abd2356acc418ad10ddca71c57923c2b466dec0cf6039b41c9d680dda6002adc16e1aaf15a2575a13338c3d24c3d31a4b014c6a1ccc11baa64b558692499736fa6622bd680dfe6dc1c677dea44e86d4174e49d6f1825ac14471e8149c0f57a3ca9d240b19de83cdb43482ed77de1cbe5a63d2a522d88de2501c570325439b3ed8c631c6dede913ebdcb1188cc280795e35d7677665ae120adc1770492e045a223f5baee575bb8c7d2fdb6b82b2611bedee9ad1c39b09919a3744364eb5d3619d21a52a687828d96690d34890e4598077c4d4e72246b99622b44a76ef34892e1a108d31c4882a74a32bc8e5cffbab05bd7740da53514e7886d4aab3baa2e473c33c5e912a4a72e4571ea374a45548bd660d734091e8a6aba86067b2782158d1ece41658ad35a66f9ca2693fb14079a641c4e4e866759b99aa56308a3961d3fb05208b67fc0e25de22d055cf20abb754d697ba7fcba26c3cb20d8172c89cd476b103b2186d04800984103260031021800018a30a2945993cce7a400356c1c618017d001107023e3a828dd396082e55eb6d8857db7ae36b998ae2b14dac946b0bb75cd93199a7182263573e614799e5e7c030a99f08b6763877e2b22673472f6926557ce1edbf46cf00644268221b125e67cf6f8c59cd8cc9c339efcba2cb75d3a397cfce26212c1882787d208c466288e4c5aa357ce0f78ab55c66bc19be821bc5d43c1541daa0846dc79603c22e8afd626b6ad39275ac1708ca6fdd6b381a6d61ac9b311897e4d8b89b9b2ec9b966bcdb22c9bd7675eec6a24319af68b44d6b64552abb522d1af693131d9ad8c04bfcb49807fdfd88889dfe53d8e1d24f5c018e260580349efc13746233b5a88d144286bc4686076695cce3e311d2e6738e52ad9ad09fbb998d15c3b2e0c330c4f0e26c339a88cf58eb5d65a4cd43bbd83c568da6fe79c380e8137b07af5edd537adfa1683596badb5f6169665d9a9eca0ec317b28fb4976c92cace85b1c82b798184dfb43781389a0ce077f74e6bb88181adacbe7e7373bf1064426c7225bcd710895b14b6599855df9168740cfcadd5c8efbe213453c7d7428c1a3878f0934610127803c11448a29de89fe824cc078365c867127df687b5deea1d0c7e4f277e7725d93b2bbef6497788b529a65f44d816fc0795d7758769ddccf3c85a733cbf0c95d9bee096e26bc892d61e50c53fa2c3b0e85cdd0ecd26f4228a538577c19d4a5cf4fde489e0df7f9e7f3c6e9d2c93fbd332fe3607f6497d8ec509768865345bfb0990c9b992e04e7c4f006fcdc93ebf6b5c3ca145bf736156df34e7d6a2d7b46f149646dadaf1813d9b52ed7e1e89d2e510f6b40e3a890d48717e5f4dabbdc8723bb17fe4224b76b7583dde08da5b7bd53c19bd6688621132a3b8c62a853abb53756b8c02b906c229b65f0ba60e81bd4d9e952186d72f9df77b9f7780b1233c59009d469ada96c2f95fb04d6f30deab89c1ce4721b477577621c1757a67a278ad838fa3145c81c7183c621d12537aff77974c966effb98401398c665fa205d1ad1bb9cbddde3c9c99d6d9c7ca3af5a33ea5dd9057dde8f925afff9bc667babde1ff5f69e7f6ee35428680e4141b33d3663fff96c9d3d9f3b3f9ed7d02136333f0fdd7a4f15921fb603f46ac197af93ead7754aade35cd645274629bc317b765da634a334cb32680631f2c3ac89cd549fc76e46e960ad83268ce211132eff7bb8fc780a21d04f81618a7bb8fc3f9ec2050bc17172066bef5ac368aa83b1af0e06d21bd0669fafd9af5b9febf17842f7787e03ce19aad565cfe703ca3ecf3eefe7d9bcfd3bf2b9f3d9751974df9cd5cedb389b909cd5db8acd647834efae7b72b7ecd5b5e8adb2ec5595ddcd5e034272969dbc72f973879d602868fed8bf25ae678de41191ddbe23ad65ffd8eb72f6eb36131e119914996658761ba7b5ecf46e422ef6ec422868c6e01d865df14ecbe5aa566c664623325a4717aefea0c16e356b90044db0b7182b543f3a3692609e3a09e8eb3f2004f90fcc53ff007d7dcf53ef314fe11ef4153b486a1c924b6cfce99dd7c5861d61606d419c9555388855eba3b4e78452c6182584b33b5757c2c1be6cf58e5a2b76b3fe066c263c22dcfb6e2f0b19636b0d7760c1e8221e31d17f7fc0c4ce3eec20e92736b6bfbd9a131bfb5ee4d96032440d0ef867638310a821c317f16cbce3d8e97e5758c24e15e5d4c23ed9c347b45db21e1f125d8fd8a3e771f2f0f00828166fefe40ecb31439846935ae56a27d7b921c7a3382d43d0ddaefc36203259d61c2b95944eb9ca5f91216d123d3eaebc21364a6cde13ddb7a449f12af73181a4f894f8d1b1551434ae55ee54ee95725d935e489bf5049a521e7fe9e227643394d0536e6c12fcd80ce56e0fc9f670367b855e4a11c9456b31254a93ca33ad7c8bd6e257ee4bd2a498b3c46ecf467b3943daa46293f2887295d0534244e2b3b9c99149132b74fb2dd1f1e27d114697240e5df864140ba0df1a43cb59ae5a65b0a14794c73f1d57033efeef75f6b25b0f8b6207fd66af634779e890ca7d35a94f9df43ff610e5c6d06b255958ea6ef5e4da1c498ed9dda8bc3d259ebb5d8f9f401316e8923cc4340ec8135d92a24bf0bab1b5ead6ad0ea672d7b3b6ab94d4b5226ab556a484469bda29a17d5262e5f694c0d71b5fec183dd29af06af929b1b3b1a56d64f421bb981d8410426865fa418109995c828dad550458cecae47fc857c6ae27d36cfddd2c5bf5a2920ff62df226c3cb5fc62cfbf2cf04362671350890e5452d7fe9d275f998cd6d91c99c232ea955597b440e34bdc867789349b23c96932f2cd39a14b5b426af2c2f2f2b29bd85a63ebd089ae0e90f4df1f45a9762a68fe9120d2a1f2d0a4ff9f9e90185136e1143ea542c58520cfb1460f5adbe4c600d60a2e1636b180d8c30be1863742d9e8d03199ec67111c1885f442632323231da7b0f8c9d182f4a0f92952733c208238c30e2247e2a2c2346631dbb6559007481dcb546d55aebc927da67f7880cc3883b599e56265fec60b74626d82beec4989344d7ae64eb93ad53ceb22c0b6f40642218118c4784bc73a2fa3ceb3bbddb8c0c2d301e112f82f17997142c9745b6de05afe305dde643f4b552ef4c4abc595575f24ade52d6a5b4eeb9d52d6aade52e84625df0cae265e5f599d709c85df417fd0df8b92ed7fb3eafb54ee3c3c9d62936d34c386ccbea19cb83cd34ae4bb01896d1666a59d6e575bdba1ad42feb533e05624b58d992b73c28957b8f0f8b2c253623fbf0a42b1c2a6374aec6bbeeaf23d8f923a143892ebd78449e169aa0897a3422c628e34cb5e2902bf59c96e22d096482ebd7816aa5a4b44bb3479378b4c6e35276f77b33ef0e068aa08906bc6ad5e09d9607db976dd34b8cf1d13f4cfe61f1925af07aa9262a9a6813d04461ad7a36227e2b7844c06b3a3a2fc61763b4175e0ad30a9e0d9223021e3a2bc0b736c6cb11c3d8d812cb59d9ebc3f63617218eab71a37879b333103fd1c46eb5b60e183859cedcae6429a59476962d8d5b28b4b5c88e30f68b2e52cfd59a7c96e35629ea53b3a41c495607d305bb392d5c956cb0f1cf069a8a40424797e0e595e8d2bc3c8f2e6197efd1257af96c4ca07b9e9daaacbb3ec0c9af6397073481b08f26c94b89df49fb58782b42cbd2ed644a7ab06b52c55b8ba418091dadc9cfab446bf2468c4088a4357927df11235a1c81cf083492e5dd8bc43f1e0ed5b1e6ddcbf62c6fcd33587a778bde79b2d4ab59c4be316cb57048f6c1de801a109917f9b4b08ce4d733260beb80a4f80a53b80beb5ab6abdd2d34557ddf8c83894a28a2841b7af516998b248491671c4cb4c91676bd1b917030f16dc14b5e29a6b8ef741f154d8a46bcb7086cbc14ef54458ef14ee888f1d5ba3c98129802e60dc8c476fcfdf6312cccafe2adb31b438ece0b0e0b39c61b0e2652f79aeee13e59884493e229de42f9ca8d1bbbd15afc3b438c5a6b4f87abf1a28d6c4b0a96b3b2d311b68a1bd8cf4d9effdc40d3e7f39f23d0643fff41024df1f3283aae861079fe93f349f2e9c2b70fce4592e72f928b73a310c48eaba14dfae0b49e911b58eb949f092c3d3de56a5ca7ee5bcbcd10b9e5069adea6422488ac4224450792a68e8e0d529028a9ee01f2f6137b40fc07cf500249f3f6ced0b9815d837d3a364e3336c7c1cc4b2fec6689e469ed8d25628db81ad8e76d0e5c8deb9608923c2d1152154d9a43304f663f27a0d0bb81850740f664f673020aa1c8d82f53bc552067479a74a907904d36a749f349c2a0becd10c3d613500825c552b1acbe2f67cb7e4e40219414954b001f8a3aa128104585280a85a252284a85a24414e56a749e9fb164860e34358e194a6634a171ca2bb0cf045608262757e325cf77130bbf0991034575ea188d1de26af4e72d0c9c0d9b53854d972a19e4796843adcdaa49bd6acbaa6b13d9a49262e4f99ea719823982ea26cff69cbaed82b391a26373ba447dde6e6193b81a594a1abef017cf33518b88ca6cd880144680e79b8d8cf264293a29697899f238ec0d79fe82439eaff0269be4f9cd3ad5d7d0b309c9d48dd8c9f346ecd4b8713580c8f3463431428cd2a982d082f0821083d0036106a185f003e1c78b2e550f808d5393e6679da1a44f5dba3396ccd09981dd6ca8c16e3394cc50f26c7408e0fb798b09518a42690aa52a948a285da19485d211a5d0c6096c9c6c5061a3c6d5a8365290e70d6c9eb766d8c1d9b039332616f09067ece4b9cd0823cf5b28559552552a55255a6119b598608612687a9f9ff14597a83c1f63e36a90f2fc0c9d195ee4b9cd6002a10b8487f04288210402c217088380b72eec69c1d257ea3145f4b718b993a746a3b53943c97ccc7c8c125783fa8c45c8a73cbfcd5092e73b6b1ddbdfacd37da2b5f96ee2f290e7e71eb090a7dbd1e1863cdd2b6c867a34b215f64e3898791dd064f1cc79afe6749f8e3a9c13009a3c93e7797ec8906ad9b2aceb59588e97e7dffb7b3526cab81a2fc7c428f1609bde80313158123147d4dfe18cc1d908a5a49794d2fa96a2dcf256d7797937a7458e9fae663902f70a4daf94be37deea67534ad56aad8edc9791690242082185b549efdf9db4c69d1ed8ccf84429554aec1e11321e9a2a96398cd88a7a7a23707d381878196892c0ab588a37647829e3d290e19f4c8652ba243cde1367b0fd28e28d12bb9d27ba140f372746fd8d9637486b3c2a4e02934c22967bb981f0c67a4f36a171d6879d4d5fadbb9b5e2a8a983797c425e911a353c2e4ae99d058e92dea7a5dc0537547fa1487c8f0044dd2083472ca9094e31111964920ace0957051ddeae15458be7ef050f4e6c81594482412a991488bd02115ad2a55cd8a7afaf166d95e276d53299b24491776dad43baf68ad3538b3c810463c6f4eb6ad0cff03dae44897b678e48a78135f92c785922b8e401c9c8638d3e6bdd82f763f3c87d84c22adc1d82ff62c02a74d55554d2613f88b89176065024d9db393eba96fd11a912e3d13e5c26971c4114da3bb6b1ea64968cebca93e7ab4c6830b1627becf254d829f4b54442b2ca316193f3c240ee6c98869ce1decf6dc8d6e127c3fa9237f805d6b508621bdd0690d4a2638120fb6c2dbcb62496bb0566b45a2c318690db4a13536b04816d0c6c6e68a184633914c62a494d6446a7324b68cac456efa6da35eff7af8b86f08b5a9a1554dcb29d82c6884367deb4ac9ce93dde733419c285d0dda6f029b68f909627463f7888038d024759e7d905e47b1f5e07b13982d9bcc37a9d83d22ea6d5a83c1a53178d40719be627a830c8fd1b9844986efc96432a9d38b2e6d947a9d5e4c1dfacedb5c926175fbe60aec967449274992d89f492016ad69fdfaf57b930734adfc48bfe31fd9e29ad2ba5e110b6d2692790476e8c6b25e17d6bab4daa679d045ec2336f3798c168b26c167a1856c3dd8cd25a153a6c16e2e098f7e2f1ec1723c4da955a55651ad2bb5b2d43aaab5a55619b5ca79723062b40691504be97d481e16408ef4b469126ce7a6cd11d964da64c74e3cbfb0990b05a2c1524fd22a9747e5ba3c297874612b58ee320ab66225846521ac0996612b44203cc24ef0c4355b853694524adf9a6c72411938130bf6017100ade837581eb55a5b0240865c402e8ad80c21e26a50abd6ab56ac564fad59adb6d64fadf43aa9b5699c365da2bab02ef74c5b9665b5d5dd4d598fe7e3f19c783c208f27e4f1a0783c291e8f8ac743bd6247313613773cd529bd55dcec5d11c52a5af495d8a3abb0c95de392f4e8526c8ad5eaa935abd5d6faa9f5a45650ada15a31ac8af42d1b4b2bac3c36e58d22e6cf55636a1e4d825c64ebf89d78f8a0ffcc54af2ff7a9166d06bb72d1378a6362b14697e63409e26c5d73961ada550645fd40512e14758aba1485290a088a7aa128fcb2c874e58a9aa21e23164f511815efb05ad146d231a62c717da336455a832e97c16675c542523ccb5db9587d750b93e20644a66f740541758755faea7604a36f3cbdf18b4744bfe2c82482f188e85329e24edc7958c466a445712014345e4adfb752f45241e372bdbca38aa358c0f58de268af794b0436079a463d333c6542bfd871352c3c6262e5a2f71861168c45d8fa21cb5cb2ec5976b30c67191059f6926541645975b5086aad225c9368984b42066bd39af64e514a8720c10b0033e445635e3001b89aa23c17b6ae85cd54585a612fbc691a8ed8dd348a034db4a64990da8e0d3b7afad9ddae1c67ae78b79d72474c04f197f708023b487ac13d62c472c8e922ec27b17530f00e0694e38d9125e830e4b2b2324f574e57565080c86c200c44267b3e51ee728982636b1ebc8d64b4b48ca688e5a2b3884422118be823d1487459226c7d645d5eeb638d46a3c35b3b87ec2f679fb8fff068c7f5f9ebf33996dd619f63d731ec1f1048468785d292527fb050e64538e507199607a5056f3f0ee14d83fd794f0eba1b78394be2da911dc3dbe7d9dd7afb406c06aa80de98dcb1f320bcc9544fb93f3ea79eddce0e933bb655541a884cfe60ec2ebf01edc9dd746497476c660444266fe0613b50f2e57287cdfc0fbbfedb44161425437dce6f9f2eb75ce460af4b82b175a664327585e4342976774edbe4b48d8bc8c5a6e04d2452c12e2e2e2e6f6cc6053bfb743a070cbb75ce4e18395b24e142c9121d2f987cd12578952c1722ae7d74c90929a005ba34278eadbd390fb1a98722a1fba8a096754707dd95fb682d4208218412db11732814bac3243613fa3b621d5af8405c823e55be3d24ced960f9f69cb0019b5924e8d39a204b5ef877a491e0cc9d77a499007142d7056f2fbb7cded1e896351a591fbdb1991176b647d70abd106461f9e896e7826ebd6be5ca7b2e089b81f27206b605baf4f826ba5402cdcfbb7c5eeb139b71b9f5ac14adc5d3e706209ee251d15a8c31d65ab3e7d63929a5d97329a574ce993d77d65aa594d97325455131c6ecb9b1aa2a0861f65c6859567767cfedeb7a3856e5d785cda87ce5beec5139044da03579d01f46d3848389a2b78f2ec90bf194bb36bd1ce5ae4d2917e2c6eb5c935e2ec43eba4451198a6e7430f2405c7c1d11f22c1688e6089dd2cb1c5506ddbacb75f9aa32cadd6a4e79cbdd44390547117395e3a598a24bf451d1a510de82c86859967c83a5fe4e5560d6e773908c4b9796ff70eb07e82a077dde02c12452aef2bb3591650a4882a41c85ae82b7118dca27de40a0bbdc4406894022900824028940229008240a7d45e5220be4297ae820516805247a48c482432b786b22abbc3172e81975297f35e1237e02d17991e3bc50f419fabcc396503988e68d91530e7aada310cef1c6c829188a3746ad8fd29e134a19639410ce6e19fa4aca574237b6167aca65282547e8290fe1ea60404fc1d5c1cc3746be0141575e74b7d069cb535a2e0f25c6809765ca1dc60095bb819e824f6a60413888c42ef18f96a7e01fd7651009c2416476f9757a8825415dc6d65cf03d9778fb2106810f5b00ca02503e42b91b08f2687e24ef0874a832ba9c071da28cee198154f0e679109847df229431c232ee91717965e043bc058159c23feb3994f71cfe444229e51b50fe32ca27fe017a78b4839aa0797f80fe5982fa07f40ff5cfe7201578b726f25c09b5c0d18a0f166badcfab8282f2872541fdf3c11be8281be828b750eed6d072180d7ccae75579cff91b10cedba7dc1fa0d7d0516e672bf410fe013af56de2203283f0e7d466795dfe6cdf9af06014756b567f18f5ea7692d662e7e8d4c75a6dec1cbcc1d3254c2cf59c36d8cd29c979ae5748f6014d6d53a585a2a9c54c236dc2070dd506db597469735cb44d97e41022458cf0a06fba74c591c6a24797e69c129e7ee80c84c1925fd85a1da56fce9612c6182194dd33935fd89773bdbb90f7fa043b96669e788fbc73f221c1dbf3380ca3719f72be712e6c46ce997877abfae89cdbc2794e29b5b516d78febb17aaec76f419ae2ac71e82ba62879790affb81cc5cd04bcbd7c5152cabb9f468a72905e188dbdb3b0259c8cd45a47ab88e92929dd69bdea754977379b614f21e31679830fd29483a7f27483b1e2ad3375d938191ec24b77eaf11b902bc6ccc9e8dccb943cc568dc2b0c43f7f3774fde1774cfb55c8c91fa45c58847f5f49002b9f275d089e7736393307cd1ffa0f075882d71658a370d58524ae91cb6820489b0e21ff2f1cad4293d05e49291de087fa3afed6591e51f162d5f5f96977f11839e0ee1e075d5b72c0cc51be3a26a7d94f69c50ca18a3847076e7685d09870829485914760e0656950272658ab38097f5c127c8cbab41271b919d16d470639fbd5ff2977cafaaaaaaf07b184683f2f8e8de83d481b8a05728c777fe3fcb7591315a518915a55486aa5a250de11ff5f4d6c178364297f51510afb6cef440e0adabad888d86ebaaaa70a632128f420fdf68239f3be811e276886319e9270ba83edf65a6f7a4061605c368e4df75397f7212de6234bc694360234204f1f24e9dc268e47f58787b95acaaea5975acbaadee0122f391ef93a2a4917a670e5d97eb7b7f36c39e424626324816236f557dd4466d2f57fce3456c098ab7ce28f85bdcc9d4231854e22dee64262897d589146143a3469e5a9a3801c4e50382121fb6847c2854f58ad1a0fc37201017747cf3c9dd62eeafc70a00bcca09688a81a4fec55b75c19bfd818988056fc7a28e89c9a0c77cbec55c643f3219c39f3b6c01d63d1f3cc21e7af51bfd8ca8e02d46d38e82b78b4eb0910c6f167b5e3d744f6a6081c8647bf2ead74117a3b1ff718233fba7a486469f5e9ab4ece4fef4c7f3ead6f3ecba2c3933d6816cdd825711d9b22cabb22ccbaa7e58bf5ebdca9675e8a90e805baf88d37889eee5f7504726d37609ab5ea1a96ea1fc6a06c0b56d6977f7c43e5d58d86da3f589585bdcc999a70f6dc46862ac129ba9685cfe5c623a5cfee0132336e6ed93ede127ca4f4e82d041283f790f14ece0c97b84b083a0d32a3df5d4629f9ae387e6f899397e648e9f98e307e6f8c9f1f3f9cbf19b10ebc1f2e3a9d9e2adef3eef75ccf31bb081c8e40b47301e113787ccf4d5a56e8e97e92bb6f193411085642d5fcc0f5e124adb971036ec977998f221295e14469c940e241e123a5a8b7f5cb4169130491d1d9c2552496bf13ab0dbb9b0de83b0bb9b476b11421a4f217c144b2f5a8b4cda88a8f303fae7832875fa51c80c44091523a143eec1c653bcb92da6bcd1a4789c89e580f9861e6ca4f9c887e58d6db2f16f89857227fbe2883bb131a6d0633df966ed379114d0f479ac304ead216b4122d1c96f352d3ff7aa3b76cbeed94dbb6b37d55d755f957abdacab713dd66aad83a6ea56f68917485e21144c8994cbba2eca63e10a43528f8aa51596e20d5e3247aca692bc9b6a8524497c1b6a685f114f06e6bf4408618dd530c69b5d98d35eafc76bbdabe5b9d4b35bbdaff5eb52ebb24cb6e596455dcf73a93f8cc6b29ebd6e5dd775e7bb5aaf81ad75de97eda5e2f3121c876579f97a167b633b0490adf997b17aecd25b27466c7dac91baa147c4fce7a63801ccdbabe20430f155b119dc165fd8d3510b59ed18639d4aa691d3e9a4022ca421f74fa414ac108391d3e954c591dcffd41e489bd3e9c48297be5d833c2121f7b38b0a5728128d9c4ea72143f43d9516ea104fa7d3c9055be4e43e66e14ca10ab8733a9d5090f52f2a070559d0399d4e33e85b9404b94fa7930cd890fb558c821a9aec9c4ea714001187dca7680f744ea7d30a5efaf553a4939c4ea7197cfab44242e39c4ea7169c20f7e7091688ace1c8e974a2c1cb0b725fd62a3c23a7d3890541f420f7230686dcbf562084dc879415b6a08698d3e96403041372bf2184f377be4b59ee2a88a1090e8e389da690fb4f64fbd714e527ef601f16aaa0418678abb9ada0052fe4ce8da39e4e3cc87d27ca8fe5c6725656638c31c618638c31c6189feb86f175cb7791987130391ea9df61c8bd851e223f19a3c537d86037a7c40774f0222545c0e8a34bf0d0076c7984392bfc8d7e36b032ba7a75297ee2709faf07d15461bd7c703998ab4b11c2f86e1430f70860a4b2654d8f943e5a83d9ad87635ff68824de9adfb1bfa65f30d18948deaf379cd92111680399c028b3774ee3548f328ad16487ba9cf05d91b3bf9b26f5f3e1d89779b2fbe892e759760f36935df1be7cbd7ba8e8a34bf1797ebb1c36a3b3411b9c9d67a48e248ed5024da4c1becf96b8deceb54b127b1e3558efbd771f1378f7f5c10ae9a784e59c732e4608f160bd22f685a096372bdf2bb25db5d42fd0a4bdb818e304cbbd14d1648e08b5d030fd5908ab95c93026c36b9e6c8c3d5a832ec7478fd6641f1abe4a29e527f06efa6075ec60b991d3fa5ba4e12e1ae507490fb602341a5eb02c88631d8c73779ae59c2be2edc06e3177c52207690f58a1fcbab04fd4a6ea4d2f0487bc9da7ef31e3a7a89f22ea135b472776d6a785e1abfb449f80c450b84292ab31b3d599a7b649a21a3af18b0d7160b997adeb7bdda78dad67f33e591e3f6d9a144fc5e82cb746a1fb50f9ca7d881c347d40083d589ef20f8cf0c8ae5cb2e091c5a3106662e52aefb18255dee3a2a760f9bc682dfe2d794ae410a7e4d93c9b5a53eea9d54211ca370d2bf9a0c1827ea3458f28f61bcc190e25d355c2e44d184d5f1732d9d1d1319930799969e494e96cb65eb7b3765f929cfc2b27a7cac2adae5025a9b8505231698aab1c0773ca744e5b9686dc97290036235f008ca6f3b56288d36b85f64a3ba5c91c9871f16c482439e322f7b5fb863872a49a4b9af46c54577044545ee43ec55928409533026bd5a874d6a8e12ccf3c0c40021e7c48cc5c159a84f895dd85b8efc661563c239808722e92b147e4c6352fbc320723c0084221d7b5e634f90880cdd0cb91e40a18f25625c92de3e10980edb8168e4bde6a8b2a4995d344b02f01d8b3295d9eebb2b559cf6eef76657b59d352646c8a8ce1cdbe52314560ad6ff274a20efa0fea27bf40d853b07bf2024ba150b74199b01dd44f6ec9603ba2f440fefcd2b0cb603b64fe5c064b02464161589ca200d88e983fb7ba06b6a34f99c2d483d40c7a83207f9e83c15e039b39d94067aaba306cc2663ef686b2e7be9cdd1f3297e7faa94b316649c5cbb50f50ef621f4088d79fc464216eb5458d4b22f1dc97adc668ac29a85308b82e02b0779a46a0a9ca11699b8f4c5d9ede17a759b766ae8c250173f52a07b6c8557ce109646cc77325b1b442085c6dd1a43e12550e12d8ab9999cf39ae1d254c72bfd2c9b1aecaa97220a95f104af2263b1a910e2d94b311771e1116bc164d9c000de64349a503db71738eef56a7494d9e5eeb3479a905d72147da99efce5bdcc97d29b003db7173ac0a883d1480f148924412d8cca3d975094e91a93bcda278f22089a74412980e25ae67e795c06624126bca2c9690482c89434f3db649884b1c99c55b8344229108712515f274844c6b7d194bbeb6f624129985c4a1164500460046e25202bbd15a5f4e234dea9afb42dc4c0903322e5aeb6cc9257520a9ad4a94b325adf50c818ddf322e322e321d6892279ce9f0b8444b6033f1f3667d7a395d6671258e44424353024ba2665c644ab225f8cd1b581200000310c090633cc545b426ca312f008be9c880160600012e115a02b3b67ad3750972e039923c0f6c0795e3df9d3cb8938969e46202b31a5051b5011efae7e80c3613692ee76ac064301eec82b1602aaac2d0092361606060602284e94700cf3ccc765efa91c1c67acfedee8e2c498e2f7be66ff46b18e5dc6a86f856071391004404003000210c22cb8270d391a38dcf351010426821c9950e217d0f52b7074283a61b90d40e705a9778b4cb65dcdfbaa1ff70efeb05f970a2df2af587bf17f164628b8cbf17a3122cf61bd0a4411e5d9269cde52d27c12fe393fdd380f70347ea43e14834ef074eeb43e1b4f7f2088fde5d2e0447cb1d3d3c62a2e5f73d5a7eff7ef11442782e2d1782e315b11f21d895832e7a4d796f166cc5bde85af1c353feb8783f80dea1f7ca3be2f2933f1e74c9723550de0e9ab427a48026197f990d983f77ae24e363a56b7622e587c35b44b98e4611d7e5bac7285ededccefb416e1c6acaccf76dcc229a46cb68b9536b6dce335878286fb48b8ea5311af4c300217d90ce1042bc0999e20bf807f37cf4b96f71c74108a1f6ea2a290f9ddcded55f6fb9a3cb72452a17884c45c15b05e1cd9ee00c6fc79b76b92da6c21b101948c10846040387fe80e63deb5b5d29cd5ba5336f79ab53cabc559a0873de2a0decdc18cd870818cdcbaf725990a1d673d198f9c1dbcb9f4d47b6288aa2286a9e2e01fa880995a35c022ad84112ca2520e426634fb90bddd2c40a430c70427290b1cb0ec110ec2741212146327688851ea2f026c426633a328637ebeec683aec8c1c014948ca258364f2cde2cde4422bcfd56c55b4c0cca8ea52b199e343562ed8077d98809f9f81e1247f75cb77530eed6c1bc5b59f7eb13c8cc8dc6c9b7175008e5eeee0e05876a05dddcdc38d1a4be6501ed17899e705e94c1be1b68b280cdf40948ea530b54efe6dd74f5fe2bd2a677c5bb7902f6c42e0bdbab56f63efcaa0abff79bcbd335c2bff770140282fc5eebf681fc5e73bc3fa09862071430d7e76aadf5159a4e5ef1861dd253b7f4273553fa10aecfb2bb365992c5b86277f166fde4534f6a60311c24665a7d02b932ad6ecd533f2a4c83507fa75ef10f8a25c4a1d6b29e5a58d125698c0992c4662836b11cd445a9caba282c074d3f07234653610aa3d1b64f96d80cfd4665f98a55b7aceba281580eadaa301aebbaaa276f619785f1a0820424b03cd785e5700e46ce5454f5a9ac675996656599bd28ebaab0998a890ba3a1b9ba757261183dc11ae0791e8fc7e3715d3dd5319afaea16cb61e58aadcf75d54a5a9e0bc3662613194643f39c01610d709064319acffb7c3e9f8febeaf96034d5e73f580e2b57d83ab9322a4f8be5b03e957e3cf53455904c61f87ec555900d42f8475fc503abdeb21e3653df7bdd0daf0b466c47ad568c71a4c309c9d4b70fc47a3446436114891a90dd23016b292a62d62dd9524a299fab1eeb37da3a0c85ac5b315bf259ee529a93ec9e93e0739bdd73dbe323319acfa4d88ef7264663690d654a319aac5ef53620bbe714d6c32353693cd4552fcd75eb15b6e3a26e5118cd55311a944cb1252eec1c8c85d184f275d58b613a5cb6f016f3c6c817f6b981a54e6f6118cdcb966561fd30072b0ff663385b028a3746bef06847152453789bef8f765441f2fc289e06fe5dc828e21df00f045148d516105ebefb647aca99aaeb59c1aa3af5783c1e8fc7635d48d09c9cbac3769cd0807ea2237f7e829bb8913f78b377f6c667f7c1cb0b6f29579427a5df2ce548f3a13be7a94b27762a197e5a7661f5adae94ce29658c103e0987e7de8d2e473c3c1e230b81af5ff0d3e91444ea5cf6ba359eb658599f565cb77a34a93fef045a6b3358eb5511326dba74b409a3b7eaede1a349bdc4feba2f4d6a1a5dda6c2c620c1b4d32b3e545b05a55c5db11ad751152a7760c83e568f7732d6dbaf013167ec91b0d2760ad22d126ca22263a16be42936beb756137518e22893941fdfd0694378af830d7daa494e6c06e2e8949e48386ca407abbae8adbc833831d4d9c6fb4135d3ac94e74e9e344976c7e02c6fa6aad48742d5278ab9352e2a2a85f4e74c9e344973027ba746527ba6439d1a5ea72a24b548ef43353bc71e019a97ef05e2720ce1df4bbd9628c317e4689a38389f2e1e8ab3125846c557e432cc4169240d620565a632cc97813f700f360b7500b3da52d4fb1a845f6154fe44a55e46accdad2f43224aad493f8d2c1c81bbae2b574895ed45a0b3dc5a19bafb02d96a42e630f75a9be9ddc5eb6e41dedd85ecef4be21768bf951ac514a9f1f95724280c66a39f89e7b6e7ab61f595e3e8af8d21ac5a3ce146f17c8128fe885648a6f1cbd0c33c5a31d549eca18d8ade61703bb499bfc6ec576f718e1bd76ce5defbde75cbff78ee5ac4cebd2bc28d12d246914a594524a29a594524ae9cc24561f9d20c8ef3e78e1e383d67b0f76377cf0f5e8bdff708ece70761582e5acb6bad49a9c73d259e79c734e0a53404579621f36aeb404ff6a9d73ce790a278d8dcd348477d812f12e470961e3dc4d489e3142f837f372746e7a03103c656907254d8ad7b1793b00b5cd3382df92302cdc5e9179a5682d9e88b4893636231f53c01495cff776301bd4720a1f71ce39e79c73ce39e79c3327c7478352547027c9995970dad468b44988cf8b5482f87c8340f3314f3c9a1762cd3bda31679c31ce2c04e82f3788eb2069c29e36b089a8cc0731ff9285391f757294d3c52841ee9a346a48f4d2251863658ea071d97ab4eef2e7fa7c0be517b297e8be226fc8b5ac7b5de5868e85bc54ae8f4e095d2f32a8092f16ddb244a23b4c45243a4c49393c516915ab52a9501595c7363d239014afa2a2729882b797559648c12a2a2a532545d430c3d17afce2baac5f96e86e2f8ba048051e9a5e5e30de625e82b80bde2ec42fbc89f0533010f53fe0ad36e90721b06b120d02bb26fd05bb26c938513976556e754318a39473525aeb108ce6e594bb2eb9aa7a548e91e009488aef1b488a541daa0846dc8960649f37b3694d664efc86a41c623432a7fc37a50118702452bcc468604ea1c180d3e23b074e8b9f29b7688a130e263e05b71107136fc95a6bc66ecdee61f20b4b29cd6fce99bb4a29219623858a31e658410833766196f3b2ba3b63b7f3c472a43c65629fb76a0f692d3e45e5588e26e26062688897208430e16f07e29b96e51017888beff4d1258aca21bcc9ccdb7449c58302a27813651025036f5a06ad7c5e97778e7cf4a0b4dcc151b3c8778ecba3cb3d2e4771798bcb472e6771f98aca1b22935d7288ee925d973f55f674cee8b2dc2d266f9d03b2d93ebfdbc7a5e4f2f847a44b7f14e10d884c9c837df9bacb751d6847d731ec1fcbb372196f6981f04e1df0d7b49818d055f0a6236f2a072293413f407719ee08814020102802197e8b405601815e708f263208040a6195837c64080281361d197ed391554e96a05c97f0ced021dea0065e4e9132e5a0a7dc1db73411561a987237e09241df3e3401b19fa864ad15b5492c99a211000000005314402030140c874462c17848a809abec0114800d92b0527a4e1849410e634621460c2020020000000000002005de42e67a7c0b47e83e8158904ef708917d934feed679bfedf6735903b3963c8117774d2631510b17168b564d4df4ad60b0b58e2a03284438e274a9bfd0f733f845803db5686a613e254269a3a1bfa67152c11563356ef59f41e91be9d318ff1feeeb85ad5e9d9211ae789ac34c2893c8747e99a9852220e598b8574c05c15c6021d1c9171dd0b60595ac9b4b552945b80fb8a279d448193483355b66ce0d6200bbb55c058a93f95766e9533d58ead7347bfed320422e46628c3a8fd13575563ca010a003933db1b39b25088a1cb19a28873b62b05b9e226f138fbb9d9c74fe8e957b044ade90c0c86c7c4c9c8b167933bc93fc866dcf88efaa78acff585f57338121cf3bdc5fb21e74b302fec3e5b9abdde3be15f84f43b29d5a16ae2a0afc34327797ab6294d23614c9b02ee4a50c5968416bc54816730848e8adfe6931a8f188d9f8c4278efbc16009041c55510c49bed62e78b279d04109292f36c9a213ebec4e60dfecce0e2c02349250a592cc0812125ae1a52479de0583ebb393d4a9c87736f21ac4e97556fc4fe5653316c3dcf940ae18d09df8afb1e886fce07640d6dcfcc2f091b61f9b91a6b6b1453d742ff66658a76d2de5126a2ddd5353aa3620d29a436720ada54643a2f74c004654e2f253006eb38a64630dcaceea76dd4eeb52e58b529940662ad89fc5daf760c9f583b98959895a40fa1114a379ef86fe6e9071c814d8cbdde5767356e013476fa1c0034beb038f4c1564cfd4d045d8d060e6e72a248ca3199bbec08ace839daf49f7a8ae6986967eb79741a12bda939847ff48f56118125937bc259055a53369186aec40bad408dc0997295f8c7a3e41cafc39f126d9be972dfbc34eab7b3d8f43d80985c790bcd9edbd91e570ba1f0aff8fea6ec29774a97e8ae41c984e8d243746f8245506c5da9acf051be1fbc7956986ebeb0d8d41c87f205e2105935d7769e6266372edbc4370f54e3cc7e81c7ed580ae899092609470015ce255be911e533c5763c8daef6ec6a3420638390e6218e1fd3e9b7331032a452b7705f1048711f6c49498a4722c190d8a62523280d4daa16689cdc2350dc5a04cf244662161bbac08dcb09715ed871876517fdab7caf3fb56d08f525f51c7aa02571f6d7ef3e029462a943087e7773ac90f90ef29b1dd5aa70855abcd9770e2cc9b930a0d07c0c2c393387b8614ffde35f1507ec91c55ed32ae03ce4dfc783938283839adeaa661184cd6c7b9eb78a6117d943cca5742c6d321e1f69846ef1dd1701f88745080e913276803e4a642a24baf0125a9fcba394aa50d45e6c06702648e220f487e128da1513585401bc37266d488c378a78cc72722c4c92b78fc28b692299adb691961a4ed906d93157f4f43706043dd2be006b0974236659ddaab5503c6275be5fabd3057aea501ab22d08ddc4ed74dc63fe11208bcb9484a8eba181337c68cd664b0b44b224bd76012c7dc9deb79f8d71b4fb5cb5f2ae42b4a44d4f3a0d71c30a34432900480ce1f133c43e7651686121adfdc7eaed28d5147aef4064fda5e642c2b6b16ffb5aa02ee3fa7dfe780f7dde291bce5e0f8fd780b295b3c3fac5592b39b248167ee43a753881be63051b2b310827e53c7d85bec2bf5682bc7709c569be88887a67aff341d38e5144644bfc4c7f183f31f1eea9e93d3b894cfa91801feb9937d7165536b80632e2501abb5c79a4109b8de61d2b180bb4e06c370e501b5bd7b3dc596e4305abf2288ce0c07d13d0484f90b88655f3c4d6d233b79833489cf51c33e4e2ddd20486b47c5783ae1a04a2bc46dfca0c6c219d0101d4ca87f69b17f172d98617a0cf49c0f50e24ec312c0965efbdc79df3ae1ee40187b0a8b166bc5c6142a2d61f0f98ce3742e682aa6624e2f245d321a267dc7b8de8273a591cff540179884d17a8e44992aeaf8c8d72310b30b4c798b0fe147f7edebe20fb3f38ca76c7deefedd14907d8f75ba2678afc58148cf676002c053a735f9c53b1c845cc800dc180c0fb056cf1fa951732278ce93e8badb8701c74a5e1f8a16529238755fee92825bee560a1cbf2f4b7a9d3177f610aee4a31f507f7a841470b3c8fde62b9a7470ad5126fd313cf94c45a0a4bdca0416e19a2038ecf2f23c3ec23a95fde39951b57d6caf8222dae2829da7c7f282cbda26d297979d337eb7ffc2953dcbe82e2da0f4c800e622a46341f7129db2835cb5f128a4301cd0b0bec181b52993b1da4b2bb33629f80c763ff0a1e03f1182c6a7f901bd391ce2b02c64ed7fca01722983b30c25dfe31730c7638660916e8ac685f5309fcf042c2d3c3d4602310ba11cb12d14692cbc94d7713e0bb8b35eca655a5ef86e7571fb091d765bff45efac65eebb3833884b537ea3731de63c140977f0a61fe6cbdf1552aa5a73b6522e14dfa83ee5b8b2d21969b22051dd8441a2628b5d5cc4b6e4985aeb5541f3b8a5b8fa05e8db331d1499525278a22b30d25db2a7ea638f20cae80f1b290096b2b25177b330ccaed4e36f19ea5714ad3981dab83f5cf30ea6d9946a1d4d26c3a887a5c23878e61533099900e71c3c4f1bfffb5081e2c1b0738bdeb0ed5f826400cc551124f3990e3ed0884253b47188b8092654030819404db297edaca2d552d6e05d11b28f6dbfa79a28b270b6a76d235e821a416bb4b6b05d58b636e4f968be2425e1a38ce9caba88302899aac182a74a7862ff26909c3b71b8b3938c30edd315f9b79464e40eb80983776866c8a0336cbed440e2b34962be1c71e1269f508c7acbd259921920a40562b912f9da576ceef3b7ed4c1461b467be873795dd235d9c21af08def2f8c3b39fd77a1b65897e4140b28ee7f7dee53408bb46d5cb06aa030ac12c76e774d950c38754d7cfd4b26c4d69991890d555bb1c790148d6fa83f3aa429d1a1d03c34cad7fcd5f75add71da57853d10b4697492ccf5c6b6fa860e878ee96d3f1f00534cfe1002786fd8816c4386424da8fd651a70b352d2ec311116bfd4c2178215399e5a2e174e6965bee34d3c8b4859d26aa193b4a061509a2a02778bb6b852ae913c365abe8030ecf90689f6ea2bc7cf8874d1b5ca74b854b03b7e38896179aa5867e2da20c8b54a494d4490b5d908575ccce4257be3d2dd5b284c54225d653af56a882fa2d9cd04447f1c3deda2da54956942a6dc3faea7af25b035ce8d32057921b4704e5f87f9ab39f781ff25f7fe52b43825c7d0052913222413efe426c0d578126fc21161da388b8246d3394d2eeb502f6d1937c6eb4bbf8249e9e9631b7bef1bca825266dc46ef81eeede901cbd4dfabc20e0bba8aea274c248dd7a1f53840142d55c76ae1d0ccf9737e33c8406bfadf29d00da691740ddee948fb820d8251fce2b33f3469cf55945e3f757f535b351a6ea7fa8ca6184857d71ba73bfa96474b10ae3b2254d77e879b66cafee0dd075dd9d1e459bd47847109f2f83d5a51cc9dbc87ed1ea48502924ab5c7c41121f08ebb7ee25f769ea6b07b014f747510b823812cdea44715dd2a17491e13028527796dce608359bd4865892455ca1de24509bf532a26716ea97aa591ebae4996a4574a1aaa4c126fecdf0bb7b0b6db71527becdece14990e6fc6687c7e2f5b1ed24e459c60f0dcacc0fcafd13084d0eb32cc5e7939c60ad6cc5b9719c002cf43814f78ea60b2ca111d4f866f9b2fa5aab69841b78564ec5c55aa84587e95cb5b83068526171ed820e1b93b99a6357706d37c0e5c40f07f9d90ca4402a4ee887ccda4eaaf9c59987877c52e1638a5c9fff460afa8fb32ddfef5602340eff60ed0b7a3e96d2c0c39e6cd2f056e896baa8b5b33df92cef9c3da2d8725a73df6cda7e77ce26ec291a1b4acce49d23536bf1327db30fdd48b7e3dea71e8b7961aa6dc0ab2f034c356d0c44d972baeec159a00314afc258de84df03710c3701ffc4c5a709ecf9e35f7a6dc9f920392671133e4989992b49c8d7c0f4e4105be79f59cf9eccdb9713d625515d41181c8a65899610c19fecfd945264ca00576f7b0a7acef64fb0a1e8a9592c7e47d6fb85fe851d3b829fe0462461674ad22e8247cac982cbbf882c1cc071741a1e46e51d26c76191b5f5c18f217f2b8e110f161cae9e286ec25a0b031e0f8aaa5f19d75096ea4e7da518397ad5e483b3c32a3192baf161953092c479ffed577e42c7933924d562865251dc6a236cf2abdb75b8bb05653b4ee5aea2aa5d2fb79b28ff5b9ff634f33f7006de41655fbcd9a9e82c600056dc4f54e9e7cb65cec5bd88cab713dfb26dd13a569a28d187ce27cd3f656e01dd3681b16447978ee923ee5e981b53e7d2cd53b478954e0050814970101848435ff1a0600c6da20b634b3e72b89fcb233464382331b936a9baba463ffd4551da914423a8dbd138426b46bfda17dbdfc01c94973a1cc52da48bb335b8d706a6563a449d911c4ae1b51475f35ee6a00b4c1285ec7331158ae4698fd261937156a5ec587d49dbbc971bd0e756125a2241a7278c2a5066ac40429f751ef30e9da2421988af8254ed07d68c50ec421e62560cbcd9b03c6b3de41d715789d584a51c907f24211629b9fc88acc83b4c809c267444527f684483dd591503556485b8a4bcfe720ceb2e5d67cc628bc6c75cb13e22e968cee092f94ab464a9d57633327586f22e5c0ea38cd22d5cf421bc7e2e92b3fd0b566fe45d938998d4d5e8f0f5beddffb6c141b291eb74c82b4e381a1e18f70f1c9de130d3248d6aeacf05f02d595097b6880ace9c97209fb007e2ec93c0234a8791c24682e59c96aa49c46b0185544d9019fd0f5cb5654cf1bd529cec69c873c6a4823221928717ed37372a0378fd4fb2b71ed958e735577670940f9246e58588e8ca9460cba81d403c6204373c3449e399571a60e863eeb455b780dae6e94a6093e3bdf807470cafa175cbca2366d14b7c7328389bd8d2226b14aa3b0b4b95411da9dd535275436c2c1bad927995a1f269f5df5fc79c8ff717d4b2a8c1fe2dc8615a9e1b268332283f4d029cd9214211f7aefda3ee90e678d069d16b0c51f6f07e349a3ea9199bb7bbae153970086539fd9a0fb1ef3803fe21f940a1c2aff3b61d2a95d66ffd9a5bc20e252dfe917d0dddaa1de57ec86952bd713483f58e295f28cd6dc2df73b1676a08e34e19800b7b2334a067b0bf47e5520320104fe8454b203489656e920b83765697a49a5c6a9b76121c6d83275e2b8270a7871ab3507806a8f8de0761643ffd4ff40c46a8af2c0d51f85196d0ccd99a05a3615b9a5f0e4fe2e4286e9422f771ddb16161f60e51e29181cd524f6bbdcf1456092674a63c477dc0e2777e91fb0d16c1d5d0fd7e88c5c6b2fd7e2606838f0da176c3fb51eb5a7f21bf8e0bbf864b04bf23422530e9d6fde10e4abe03b67afe23def6153cfc84502444b07adf7f531a6c1198d94d2b5513996bceabac3ef6f1fa91b565ea6c70d2882d451d72034e9803f22045485cdddd65c34b0cabe550c1b40b8cc7195facb7611ae7721d68c9111fe719574ee7fe46b09496e1bdd819706757fb9633cfedb7971f02104613a0154f833ad0f97542f3da229ea28074260dab314ecd86c3dbf8f9d3e4ebe5c09a30a143de94a2fbb8834f32295507a2ceb6071e1b0cd3c5b6f260594dc1ad557b4914aad6a3b76bdf57b5307b5f911d444774b2b19a0c1781c9622ea50ba72e4a638944278238e213257197ccccfa11f63088657ecff097ace820c3a5703118e33cdf3516ee2c4d3034d8ae63eb8386b456076b22faa69ea8d687d602228d5539e9096299d180024beeb0c112bb35d3d3c37235fcf917dd49613c2f8d80a346a2236433d39448feea93c21cb630b931395ee43feecdc770d60924e56cbf65f484ae5e499c8f1b1902ace53ce997d02bf00c3195e58e88764de578deba36e20a4878587f4cadc9683187171e7de60ff7d76988716d57661c50f942a46be9439eb2867d134ca331c2d876a16a80bedc1d6c9d608bda9ed1974207d3cb10a12dab5a54726d8bf27f9dfc5a83e27f46095cd600da738c3d455316ba6d9b78558ec1f1f85ec9efdd0fd0ea8a2eda05d52bf1298bd5d40c290dd16ead41bd29c818057a242e7817b41940bf6ffc08e2a015b670916a7ad91110b1fc8307357dec6a435def3f40dbe068a2f2153ce856e9d124ced224eb73c76de1fb672a4fa4975b19c19386c2572836be38155ca6bab6ac663293d48d804a351c51a1916c1009159ab0f3dd7c4ab6b37665887a2ba404065441d83364b7ea5ee80459e1dce4f63ddd546457a2a88131cd753a4a06a1033f9bd8eeee8b5e137141a9bbaafac28e493a8ea94f7566eb69ec8ab60c530ee06ebaa2a0c81439613894401a7b693ab1b433fd86639641c40122f925f6eb97d441191bd8759c3c1c5c0df51d26c9b34e9bc009929d5c0df3cf40f4fac0758124a319c34a77baf49fc88b196ff370a5f41e3bfc2b7cdd1e3863c2da8bed7804fb1a15e8583794ef0274ee4973e23d3381422db588afc1dfb6443aa156ea4824ea64d84fe4f4211bcebe6564754933ab18c5b7638941ebe2b9ad61696432f3721aa9b86c78a652702cb6de8fd6b451d6df670ae1328370756b8dae16e7e529625519ffd6c35ef12b5dd22c2bc7b90fd8223c7ecfb47019409064a77f911a70de1d965fa26f8a5aac21fd1effc2ba0597f8df9211f9c2a0a02b03e8700870b1862d1a81a0480d958cad5709296df4c1591b9f6a3468a1eafeba6b7c54ca8952158f4435e7eedb87a7aa15c4dafd60da575fe8b125441d441bacaafbe4a0fbb6aafb6ca8c0a16c2f64e4709fd748adbdc4f17305a29b6eb09e9dc534a4e6129b13d96be7882e760500b17914d7f9d55c776590666121e4d63b13516438453a34b66fba6fe81ecceb17210eef50cbcd78da0b1960b385e007456b8c068adcbcac266eb03eb6534795d38cb34b818c731498005a18c28265416c24943da9d727dba17f3bf50e69262e3a33a1196bd450d563cfe9de26f0c4bf96afec2ef81012b7a007017656e99e0bcb90992abe5a92f0141a4a38f4815ad94b25254ab8a9b52f7d9b29d093e43d6ec0122cba88e6ddf24f2d359f44391ebde6273c6d1093aa777c64601a8a43f7bb611ea02a5a2972993ba6c66fb2660b44cb6842d4b35ac87b880fd9dfd9dfc4d101f735dd9a43664d9b5f97433cc302e48cec7d0a7cd70dfc4c5d7808b587913e706fcb0f81f38fa957a61a6294720975a731f9c95ad3a718d403299e2e946432abe2a3513c259988a132193fa2428ba87126581d5b5307b363ba2888f91cc9c67940e9ee13a819d6ec344a0b5ae49e661bb9a02201018616ff4cb1a7211c758555f117de1a9766d8299866106b364aa27836047d0e4f294c7b9671c30e422f0f9cc0e76140b91d444a82566ca278b289a66160f0f8492445d8a0e4ce43ff1db30abc17dbbf239fd556f02192ca3d0d7fd66cf7a4690e68b550f10efae46826507b27e1a4b8eb88dc61737aa6784f00acb2df3fd951c94f7fac2d788ff8083446f15b404f4cdc78a483d641a217ea9e0623809a6b8bfe4cf863cb1200a356b14039e9556fd299d983af0583581a5654162423ecc266617d541f1768190ed118019105707dfeed67a55f6fee88b35c7484308a1c4f62eb4d1fcb95751bdf8772fdfec54227451b03d53bc4b69af55e00b047dfdaee4af45bdc1396f6d85326bf3d4cc4ca451d5c0809ec7199cac9ce6f228d0592898a2cc06110c705b551aeaaf027c6bf7e02ce940389e4a32093037dad065919c671c22987ca77d419dd500868a7de57a59c2fbe9273aaf69d28a7f5ef508a679cdfe4e2464f3607ee851f50cf04da6612dfcd8f6d9bba2f64e8a8f8b4c9283bb9d6adc16ce6195d6efea6ddfd6168368069524894cda656241891fe136cbf9f11d8adef2f1332408377337abc0746038ed66bfe24e6b27a35cac9d136adeb8cd0b51d9a356efbf6a9caf9b6967f393fe6b4a9435026dbb5feb4618ace665c087efe240ddeff12cea9d0b04f20fe616b507fe27cfbc7c08b6ce4ac3cd5b8bc884f986e4e0b2386107f5124f98c4548203f1d050a3df3b93caf594249a7d1feb36001387ed3edc8219ea17e658eb317f6953bffbf79756485f1164e474f52d95337e6f8f267c36dd9f38718bdea12d2a9ac782a7ba2f0c620ba53a3516bea6de5d897079b0bc4c1e1e1070f3dfaab42589f6e807c87879fbb915ecc66e76489c0b06c188aad37569e460a300343d1ebbee8cd3fcff5fb893c64121b90c8eb70ed2c7dbee57bcd65a6395068ede769af20b908a2797f38ca0f13ebbeab3a82fda0b8f4657115bfa4abc3197ec0c1019e860685f139ab8cdc5ef6072c7c3b232a4f8778f815ad26fe573a455dad8e89c4220f0c8dbd2ed89d38e9d87565c60b793631761253b3bfc6ae95140723b0b7930a6313faa19322121c615c49bad6240fab0611bade61279f011bb3709576097713fa62a027c360980017da5d1d251d8b428138af49c27f217a03878601bbf32cee682a8e682ddf502c05e2d4502a0d4bf3691637328988250e73a960a86c74470d582dc5bb9e6f9525c84683723e2a446e1590b862fe7cd648b4cd6707b39ddd234cad5392baa9350912310bbfe88e918b95b81defb4830ed22d84b54ff8ec842f8ff8fe045ff7f874866f97f8f45d0396891b10af02c813e29911c1a87897074ce05042eb070011cefacaf2bdb24003d6bf9343e39cda99062c4a44d3ad871aa61f2784de80154aed69b8cd662e7922fb5a23bb90d7efe8786d64b15963fe0e3382f6ab77ef8f0f3df9cbd60bd8fd46ea45f376a878067626cb0b5855ad7aae46fa418fc89251efa3318998114bca682047d8983fcb7daecc2d67c8974aacfe0236e7eac838c43da257cfb9552174fbd68014bce41606c717a74a42fe56dbe276d528604457244fb862e652cbb0c531695fc062a611fe89d155929d03c9029c8abc99d4e402f606380175d38870f5d58549b575d814aae3292e3c978fbba8b074fb6dd216ac7205485d738e1cc8dc43ac367e8cd95a8f2467921a0b65a950e4806011b619b9d6f9b659ea8e649fd6740ed85da69997636ba878bc12099c2c352e93457997436616574bc3d88e990663025a112078a4aa91a540c0912c964046ae5f9b941ed7930f547443966643b755599ee79772c3d9dea6307e0fb7c843e0b614756f6ad6ea06d297724fb991ffa8ed708c62fb1251afa37cc4c565312e3702d288b984f62548ab5ace2fce0ff939b43ddfd0050232b7fde6c041c622add22128b11d70822907618c333c42d56a896c0c842dd48901991bb3f2086cd6856733b6d8a716856d39b4e5a1cfd8de0121968247f4e4dca6bdeceb13eb76be676ecb52ccf24b5fdb36d187122e100bf157b4ac236668261f0673407090d97274390bef40c77d0384ece80728cf277ead0d05690fb552d11ec8f5590b3fbbc21f8e60467fdcd11b84665838b22c472468875747786504eb23b668640b47ba7ec7e5280abf78384be3c2ae1fac52f9e752fe136bf20b6e3514be7c457b1034033677260aaf7f2684e03f2d65cc59e73e5d164c9523ab87979ed6a22d471cce468fe002ff507a7c024c479fca6c2deea0ca315030b8c6cafb1e284e06452a0ee79071aca7479ce3eac739a3d51af798264e6f02f2ff27961c12786af326da10710a08bed1334fb2a67a065cb450a320dcb5f5d7ff36f92557b2ea56f43f697c2de9b56d92251f0fb5bec502ade268fe9968a50055c7217509720e2fc54c34894ddb06833bce0994bbfb9e96b70e7875e1e299a7694b69611a8c63758b1dfa53aaab25ac6ea5d2bbc2e3c3bbaeee92a4e89eb39a71baeed0611038b9335975b9f21ac8ce1c719ce230f7e44f18f1225a1b29d28f0a24e8562f469ac519cbc003862a728c46fcf34316bc88b00eaa5b0859d60e510b72de0ebaac2e44f7933f11dde963843911a011bbf14f9d2b875d7b64f8edc311b7c7896c23afab85c6941674f52c980a20f12ff869fd976a8e2cfe5a4cd2a0493c20f1d6d4133cc58c05bdb938dff394203e530a153720574d3006847413af662e7f98075b49a1f3b79b33babc73fac7bd7279c3c41cfe2cda9c9f114731dcd164bfac121b5619f64231982f4c82af2b5a97ef5f6cbe644a9242d6e5ced730ad1d156c3fd0fed4a42c6862be86e7d2d598b832620dd76b7f3d4e7431efd3c20f6847213aa951e881c205865533186ed5cb0e4490db996876b622fd149f8f6cdc58e2558b3c77ef5973be5a112dd860de4c4be599933fe205090f0993fc3a2e842dacc66e15a78dd1132fec65c874102ff95bf1dbb9b119027923d0fb1de851878267ae6f4ef867129ba8a0f5c884f1da63202736b3950470510a9a4d8c01a041ab70a693d353600c82ef0af9c0b812e6b67635c6461c80139d6be0beb2b926a8aafec129ca9ef6efcfd61937ef9eb926a178e1649d250d41f9b174b2a9c921d030d3beac02681d68e6cacd797d2c0bdda28de12b973f37fe1466b621a6e1e2c4bddeaa2571bf0fd5b0460ee4f23e56debf645e9525a434f0eba4f15a6210ddd1f3f8ba8e29ac300877d3cd31134a57291332ef5ab9a4b7907a03c726d9a75c6ccb8ddd5fc5923123f36b48bf80d2562e5a4a94f34bbd8e6c431d82de5e82bb652db6febc641b8df83c9662a4a898298dc8ae71bb4d47f0cca78adce95fa4a0f32de5be40f8ab15c0a6dc4939dfdd6a011b8dc761c338dbaf38b16c4fbace37c55b7202eed932fac6f70a911899e3310f3bbeed832ccd58bfa9407393d01373045a16ff0a443c31cedfa8486e22704cef0870c97cc59d6a5ee26497e490cd4ef51edd53b1c9d1925b76f571c7179c3a7e6c0bea65b7ba48b7ac9d54c95cae7a57524d89e22df352a6c72519acda9b74b4b4dea30baa842d1ef22503d148906632fb043a9c3a62b3231d42ebf651b15b7fb3c1586f9923bd7a4174ae1e314489cea754b93d29f9a630369af412a674d0fc4ceb41ecfa7e32257c66b43bb03c6be27d257eb77fb55cd8b184854a7dc034dc4f5ca307f024d7fedbdf9b809c3578f389e809b3a36c710fe33c941cf9a73af18bdcec44348bed9d8e09d5d711bd81d769575c0cb9ba57a8b1649c2c84d332f21d5b329f04a88c09808625ea3cb7fd42a81d3fa1b575c1adbdc953b0c1d73d1112fb46669e60ee2ca1411c10e0ab2ccb120a978ad2e32568f013f98b99eff46586666ac72ac34c25ed26f7377bcfbdbe70cfa384cd94545a91858b750808acd54f66223269a6e7f5e4019c08e23e5b198f8a9a6e6f8ea86767e2d197c2ea2404b19c1a4bb2f7a5553ffe5d8005be8fdf7a563c93df2c5c2fa459e01c7f147b31f356a754f7a76d6c3dc579efc6c12876a6a27619ff411a85cc606621b9e5beaea1fcc5a326a2fff340f21dd0328d6f351dd068480855b5a74435abb9e170a018a766d58437863708cd3252d15db81a0a05cd185d39ad2ba8f4944cbf5bfba9233fbf210f502e0099db72e25c1e30ba6665c804acbb05f80fd957eb1c2dddaac2ea608431a6c954426028e9f95c5fdce2183516114595b40cc91d7beff665340d6867286067284de1cc17e74bf450f296ce836870762777533479e092e34d74587acb70f5c343b0c52af71c8f18f1e23cca3d8a43db53137904517dc9a9b906abb657b30cad8a156bab71edf1c80aadfeb93e120485e112ace2b0cffdfc1a1fda89a5e278f07fc9fc629f6e28acbcbcf9ce11edd437a96a040babd28565f58d0b4a4910dc0a5fa4d957df409ffdb49ae7028723a041c24519e3449f07d1878e6e37fab2eae3a1b71f1510a76e4fd55c74aba300691aece74eb5bd946cb60b79419bc7644da2bbdc0de4b1d13ff668dc3b62df2e7bf05163da9dc354019f395c180d26ec2828cab14d2fda07d671e0a9776b581b920bb2b74e76dabb6e684ca1480758571def4eb2b073d308d4d2cb6309d4f2caac5c32e2782a8009ce5f84d8b5e6b6bed2a43e979091488a17de411fef8c3c10e26bd32628a63428ad65aa1d77a810cf7a616112ee5bd8f7862648211177df321c5c9c97f23007e79a78ee09e93256c82758839d9c12f5b8ad837bf3255a04371b1cd2ac81b9f6270d4752ff0b9a43529d1a9a01179376b34c3b5ab143c64d853e1dab076950b1b6d077d10efb35272e48136193454a789d168e9b32b9ff6244e4c328e4a0d137aa13166caaf0219274664d202d39327ac7e559c1f1beafb9d9c14c51282515eae02f04fd3f4a5d73f8f1818f360ce84a1d7f7b273c817e0ae4119d5ebfdb91f43aa9b989517331ec2f9c6629714e1472fc1e7532df0110ab47c016924e29a792f47bf825491ab065c59c8459fbc6ca6c25a595fde7a7bd951d460fc23832e99dfe6b6182b81a584a2ea9f181015b3a0b5395e1a258b7a971ff31b94a529e2111156644192ab4dd25f912989dc9058a3d823debd4cf2a9a1ae427cdb34385379b81c08b2399f097ad6700a2d0ef86409673155df71bce3ff3df9f4425550a475b4cc469ca26189e7d7c34d11c470f1d5329cfb5aae7126b957b4c5362a253827252a81326074a44c75364ef3c1ca74a9e63d62b1cbd17ff2e41c7c6fe5bd7d260875832e340310b701806139b3f40af130eec79619816c916050f963eb15bd6ffe64ce019e54867ac4bdda23390bb580e4c16e669c7b2767f62466dcda23350c539961e47b1c38a77f28ca0186c83be148f0f6900916c1755b87081863c3a3b1790c08d41ca4fece18553bd59897c03a0d34db9251821fb6fee5bee2a0ee3d72979fa5a609d6f761a10b28e303d5760b275a13eec109dee3d1645df1fe88df484a8523e1666a4d10c7facb8d263e464968b233be5314139e09821d43ffa5d073ffc9fbc029a7ffd6f1e30fe0a9c42c273ba4c4e58c58a2f1b8e35c2e24ffb7580a02a3d8ae88d03779d2558bfb6fc0a3fcdeb8ae392d6f2867746eb38179f56b522e23115837d829a6ef133207b41a5840a1d66890c816c726bd520dce61dd5bc21620919050efaa1da03bb026e1aa78c91e76f123b1e7b93cff9c3d3cf395d42d147d07e81d67b9c67b251e0b35e84365f184cb5bcbcd308edbb566caf0ca46ad02070bafdd9e4ce507e7bd707a7935ccd91a066adc9277d4c8637f81886e6ca83d49f00e06d7144102f1bcdb81277d538fd12b6b49ef31156a5f46c0696658b5fcb56ca7e7652a4a1e86780d51d50118e869265bc939a2b78c953c87b2e9768ca2a34a0e67e089df86daac80b1ae05e5c3c692e812810262d5f2613c7a9b74756229b24e2f62c1aebd28ad63e2cc74ec997de76f9da8c4c59879a017d296c7bd395a9b528bcda66375d3fc0f781217fcd04b22ad90b7b2393b8bcf830999898c4524912e8c0081184d2331beef277ead69b88bb8e2927dcc372fc1891728ca3fc94acf1e3e9aa5b8e68540a17777703828d51ea11cdedc6866142225450a2de6316fa1b49ea0b303270466ae83bba491028b53c596c9f8e2d06baed8afc3e953f32274e5e68fba01c007b8a4fbffdd404d6a5995319d3e74c2ff98fe9853f107be177d53242c6df2ddd6c4df5eed255c019a0e16d6162d64cbc96048b59ed071295c5e25d033538025329081ec2e3c05e949f9325d8fcfa5550b09259996422b1a53f01e23e4a7c2d515104245459b759fcf28112222f1c0ba6d472d54208f22d2ea8f2e8b051b631fc44b8817a3b26031d1866cae2a04cb919cc235e0dbc5c26808a8462b583bce366d007ffdf8520495f16e9c42620e4b30e108aa4a8de7aebc2e84a84b9f1c89dc19002acb0708b1d4736b2b71421eeb72f8310a21a0d679b4b07fb385a4f8d1a7338f3d1d4e8dc0a0e0b8ef14e463ccb26dbd70542f78d1a059378717211ecfbf341eaaa8e421185c38912aac6df5c175ceea5c34b1421220d9426da0c094383bab999d25db2190593954b65043672c1bce83edce4e2ce90994b2610ddc101e17c6d8ac1ab00b68e578829c4c0f29cd66f393d004b1ddb1c04a9500370186cf16cbd738582701b87e54ea655ce8d93a8199709e6e28d804dc549a25525dbb2f557e6ee173c58deb73b3be9680fb440ab3a27e8e9b8b94b203494072b6b11de5475cf3299e516869804eb2d71f3082bcc790fbfdf3261b778a3e89830b4f8e7bef452f0fbdff303cba291fb929617c83f4bd895e45a637a1973e6777932b3f5184f42938d26635c57383ada8e95da42d90953a2aeb272d51c75bc01560498414a36e69a9dac3e6166d0758fe1a0167ed44a0a5d8862c4823351105d0ed16750fd79af9ea120b9aff60a187bc679d7976f196cb9c31c94795c8a104d72ab5644d43429ab6eae573a300e6864614dade20b1e0a0cb3ef7c62ae7480ed77936de8b8835b760531c5e10f93469cf350ff99d5df7d8224e17e46c533ebcc50008a19d5e2788f9350da92769f9df3972e86fe2513bf691e9f3c93a34c4ce133124b0816c74b3ed4d467a6cf6282f992e096c85f69415fd56ebc87c4afddff87f8a2b5ced6930eecf98332f559ec1a488609271f22bad2492eff66c76914c976900f6a98741a76c506ece28a8ad9d604cb69e5b9d6db3c215887e5642fa379da3a4b22a8aef3d0e024c61109bb8e26203352d597ca95aeae56b8c32a423185234f31ea78811b1c0293dfe4193278fc1816523a0ecc2ab1967cc7aedf4e4932ed73089f49ee008145679c94549f6014b98630ef3384a8acaf5ddf8b5524c4ff0b7868263078d74d6661224cc1cbe2fd934b274180be48cc3502e5a19c48fb478aa2bcc71d93e6a71d3cead8f62a988d24a4512a2283d46ec0d5821bc6851f4a76218055d64e9b709f96fa099d2ee6a542ef7b3635ee8664101b2db9e7646af72c20a4e950ed39590f835be838ef07e204409041a48b46f093d0f375681dc9199856d2cca49096ebfed51f01012ce1ab4c8c81d40a0269ec607016ffa7735b7faa0bd3d7c1e13af5383f81485e8ab60f1b77bf23534930ddd110183d31121f0ae35aecd7c8aa74decf3ac5c37942c4d1fdd3edfd44e34b6393b6eea151637641405543a0e3277fbe73a69230db7cf58c9e2ae8b7b260bc7982d851eec9e85bbc70b662ca51a5d3266461e64d341523868564d1ce540a6c562d4f68923b51a9f883c7fbc5a5db5bec869fedacbac904a6b5b6d00d86488c396c2d9a0d671afb240edbd018e2cf9c927022a2a128fb558fb1799025a0a63aa7364b3acd5703d830b466c1a9f38565216f7c08735e5140f4eb8aae59d2f8dc82815845889ee5b2426e3d6589caeba356539727ed93de37ce627b40b4fcdaf0fc3b443029a4d1d7ee515422473530cebf3aa8f13f1ce135d189b072654112b32cd847165c9823e6cb2d3f2d1b7a3d4daea4c8dc9f25c1368e6a5eea67940f1cf19c8d3d8c10b5460be153cb36934dcb3381f71cbef4db7e150c58fbfd82283a7bdd41cc2b01708b3aae2004fe105017edeed6608f44c158c1bc8d74f506108c1e8a3875600506516558173d76ecdb1a49a758c0dec6737ece212670252f1a9fa7af918a2cd13a56bedfd7e1fd09797000d7b18a05e809a4483c84d1b12d7948b289831c1cca199682cd31c2ab146623fb7260a3d0010fbe3b6a16cea51f6f944045cd623f88a2d31e5f456574ccdc10b1f93d3c06ed4be9bc5fdbace48057f4d52d2c5e29e2b4456a18555988c1835d4536cdadadcfe2b3759caa98ba3f21a5ea3c360a9f9ef5d14cde1b941405ed1c9a4744058bec22a54290ed8f4a0597a8ba1ee33c6d298b5ec0dbc97d6010d8b7abb8ef9fc5c840974924347dcf122146c6de92b89bd8972633098088f44ba09f8fa3766ddb3aad651850bda3bb43849580a3abcb1b3ad08231e3ddb0a326d26735027e9cee7e7a494805a3f0d910a51d6d4c0c4ac4abbbce700035c9108ececd7d50d5e68029e4d341452fbcc6fd5b508157d495a85995eeae4a9d855530f49fb240fd2a5c6a2d20ab0f392fa5045aa0a3ada59e102295456c2133a1e965a6f42ec1b6b6113187beb8141560685e32a4a14721521aa0bdbc41388f472133752a0fc3eed64efa45d0f826ced5051811222d8aba2cbe2ece1310803eec9c0113e8082e2b6745770f625f250bd32cf78f0dbb2e361792786cde3387975e2834c9ffe6858464c73e4863f720a4df662c64e942898b4b7d2dc5bd0c725bc1872d2909541ef068b5be72bfd31d1686db9e72579fc51d9c11136e433aefcfa8c171d68379473cb11273ef2c9a54ed91cdc9e18670e0674c65200a8fd23f1701d5255318bd35b986e5eb341e8f054688b0af3b995682acdec5c2d6e1aad440013f0522f0a536f35176cd4ce4380f8727f1d4e53c2ada5bcbcf19584221cdcac776fd37203f8ebf27e889fe4d00202c67790f0837f1b04e9e25dbfd759f3ac0fd6dd53f876e31bf98507168f2d80463bd8dcaf112468ee3e940def6914353654a508cf5c2ed726e13ad43a5c0ea758529bdc485f688179fb3ac3c7843d3a390e7f634f7387f70c66911356a1a139ef466bd3ad5ea0d671fa79c44929b67981b10fa8221c2fd6f3ddcf60bc150812816db89f216c0f90ddd19f514634e4363d99e367409077dc9113414eadeb25727a5ce54e15eaef1ab7dd7acceb5931f38c8880d13fc167ede486121f1f72c6f8a800906dc62b0f90687fb50de121b5ade2c565e7e7641062273ee8deba999d68ada7c6fca580419660fb8ff5351f8ca3663f44cc5c4012dbea565ce8bef3c01598eeba0ff80342a9d5f767bf827cb55de2f131ecd74d39a6ef8680125aa97d2aa554140bddfd03c4dacdccc32a7f5dd38c98b29b2200c79e59b9b2705ab56023f853a0c47a856c53311bb9606688e81edbadd0ab96db91201634d6335f796d4b3b794c9f9e7b48522e27dac02cb2b62f8119dd42410b8d075e879f9309927d9ac4d18e273047ae82175586b48607af41aadeab3391b6f41ddca2b05073a622c324be4d1755a8de68849dbec322e1643d5cf71092a974d312c18ffaefea150d31a12f23921bde12325998999cc8703a58147fb33d4dc58921277ddfc509a359feb770a431aa3406629a22cbf10bdc2ff049f06a505a73c884306cb0c793147e2be386060235c001b4cdd3e30355170802857643494be1ef061f2193fa9db8b02f58e414003aa724d5f37f469ad8f8e04ba63c6008e0605cd464309a00e920052ed9d6f496dc40223d30551a99350d29a43f3571bbdb817b73374fe78f3b7a4ced8bc2413ee42d5497125f4e5803e71c59e582a31628c11c2bdd0673364e2fa6f7d566a997a217a4dceaba9c8bbe3e6aa2ad6d5633534c0b0a404ebfe808a4a95bf861c121088483cd95fd08026bb8490b94463da05e517123f3bc094620e465ce25d60e6c7d2f1a537c5f8a618ea08aa687ba1ce7ba47334505019135922b5e264ce6d6a3fbdd91d6913bcdf4a18d3ab06d6e1f55e9379e2fa0c41c384b8a847fdec5a94ce0c58fca4a9c136c891ee1a642371c096ddfd882420fa0284fcc7b6b220f7c60ad6f4f5dee1b13c765a491419709f22aa0f7ab1a4421402979b4d82f4f6624c2c41f102434cce044220e368ac95a7712e1aadab0c2f170a61e6aca860fa144fc0a72e61f3f90ba703b5697e3d84f90d5801207b691427571427fa9928a7f211a889de3b5242f7146a5495f1ff4fe4f178eae75523299a466fac6b54f0715da99466d8fee950b5e19ffe819fe25131f2059f6d459ad9f4dfe3f478f7fe2c05a9504cdeb212de9793b45cb3d28dcb1e95269666029c86763ca5cc6f51ab9e35fb805f2a56d8f849662c2d027419a57cd79708d4c4a918934165f0bd07d450c39a24c95436640d589aa4f2c604036f4418ca104fb8b5b4d5a0d43217c6adf106e0ce8261dd3d663e3efde98abc93808f9fde7a8bbb1c257649cfa27bee6ae45b386ad45d44fdd5757ae29b600d2d42ddf3d269b747565fc7aadb5b7341e73d8a847af88f783849de7b5f0e846d15dd7a1b2d434abfd43c2a7b9d876f68a95238386d3eee077c4ff774094f0f2b6fed3613461615afe853a5cabe3f5432b0e07b9aac361e3ddae7c9a702e4d62cefe2a8418678aa2a2a86b0fa0adb8925b16b0686385538163cad22beef50dc562a2423a08a91f3805e12eb30ae42eb7900be85500aba0abd8834765e60778cf7d4750aa914d9094c647b8fb29790568a397233a45e77d7525b93b3053069ea29225e5caef88ef36506184646b4485bdcd2650167b5687d7e6600ec2154f5f3ef7c91e7a8f5352167fbef48e17c23947a884111ac9b9441553762341af6626632248c756370898e867020ab6b011cacb55ba6f4e06db9f015709dd42783eb748767dacd708f4ed0d8eec8473266b73ef2a38ecca2b43f922debd0f388237081009ac945cc4e1c2ec5dc5ce9f2679d4e5a4afdd0cb640fcf537b867db36dce1c1a7e40c0697a7da05c3fd2147c269a517e1efeb3b62216c2581875aafae7ec57652c251b5846507e1aff69c3ddcff58040cd62a09014d9ed0b9dc9831a2905f34b12fd19f1b4d890cf48672b21628f3537e0a4da8f9aff532c3a6dc76030dcb902070c361e0e17855028a5263c7a3edc749404213c888325e60e0199445649552416a74013c70beb6cf1c8d060bf84efbd0321fbdca1702980df7496923b49f8bb21866ee8a93462380ab11239c2e6ce5b0b46c2f0855fb5a8502f6f62b6b160814bc9ff8f749a933ce69d84ef1ea4411d3bee3ce26093853844dde374b260cc86bc6e5c7f2f5818a696c5bd167b012cfec26e7be76c425136eb67d59b28431537f04dc4e81da5dbb42618b8549ed06ec5479d49c03fdc0427657e3f7f6c54d1da5ee40bbaf7a463301ad8f8094d7c9a1bcb385b46f86ebc28455533dd75786a455fd6f09028bdb21df3fc102d2132b204a2b4211295ca3d7500870ccf05981ccbc9aaf31dd62bbb8f4e788b4aaf9c45794da568dc5a5d9779fbced6b05fd8756c6149a1267907b08346e138dc70c9320f96cf9c69a57e849f05ec6e92529f3a93daf7072ec95c9c961c493c07d7cf258650f7ed8750b44da3bb2893f79cd638755e1284ab25c17de4de55d75f868358793f6183016001d0b7a997926d2f3056a97fb003ec44977a1fe9cbeff3a58c2a1f409316a2e6f8f31ba9319454832608282d29aa16e5b95f920e93193f9a11fac5cdfd85bf2cde58e35c43dfb635bd14fa806906d31f784b0dcc78bd3c5845b859c079ee8fd2b26f0cd335af6b862f1da90443c2f688f7390bd1153fffdc9b1ac93eede0b93fd0130fcff725c124c7a1fa58a5eba9623d2794afed51ab85f1be7dacb608c75eb29467d929faafb118604c78579384878fb9b26b0b5c7d262cc1cb378406067c06fd96d7006b8fa41f3fe52ba8a2911d160ed0ecc42b11e4d72cbdd101798c266b6087f6765430f31286a46fd645efecf98443b23e963c78ca09b79ca6e8528cc80d1aaa4c424820fd3cb5270ff4a64bed59dfa2c005ce7f66d9a232c2b8881e4d2d62e50f5b9f6303412117cd8da98ac763d8ad5db96c357c3b501a611f1e19b0ce30faeef86f7bd80005c43fb82a65b522ce576fc38e534dcb1dd9f6211c300e982e015bdd8863c233125b51b950614532318694df49ef8c8ed85d73370b30497f9e4f5b4e9a7a89224e213c2e31f8eef047332ec6a60ead87fb4222c9312ee3b93a4d064d45b77376f770d26aa5f2145ddfad953a4cd8e185c287028824c79a9dbeff8f4b82c4e24ce52edb0c9395dab8a42fd385d7f78fd8a167c20cc1f856045147658707e7c0699b9452726770ba076a1b203b64056abf83fcfc572713bf2c190af23b661959ed2cc2ab6b8fcc9229e5a99b354a5f72053f1f98025983a3ed8a2d526f8c0afc3008291845b557964f0e0d358c7517471581cb0e7b2882691274ea38e19bd0a8f34d1d719b3719ed86ec3b6d04a285aec0ebd420813adca866b3e7514534d363c2238a95a3b90228e10a20453a8ff6efa0ad9f2a7d6b93b0ac0a351f751ac51d8eb6b5c5febb85d7cfe3ae0051ea69712e3c6523408cf88e4479a2a73e91e1f4ccacb0053c75217e4b69d3e8f164203880c5a1942caa600925465fdc0d764c04011b32283176c45044c5edafa74a0ab0f3c794459244dd7ddabc9fcae38b59870875b66c3bc2afcf6e6332380d35ab4c2048f83565b06c78dd4172cdfa0e592066f2c7a46f3f2d98e14ebf6d4361602b56d11d3c9190f8a213d03b357b9d5afa1811975a424041b85b6bd2d216de8659570408b9739d1968d8613b0095afa13b99061ee2a753f6e9c39a407accb29a6a37dc0af5210ff719da7d0b63af69c68669beb2f1c989c37cc3c9e3e274136504e2ccb10deab7f4151a349856aa32353906cb40a062b50a70596f6a0e1df05e554eb6d4ea5d4f2d7b029ac945364abde6d7899bb5bf6bb43e24abb643c8a6b57846dbf76571026518c7268863893e905513db713fb11e7e732ac4116aea193189924b6dd401d8e6e318f795415d6ae78828eff0dee937861d54cb747315c917eea8a623fea14d969dc8cb600826c350495eb945b346555cb71b671ac25b90f0dd840276c6ee3fe793883799414eeff36bbef04128b1af07582209f996ef6006f03fbcec32b15ed8f81a97ef5ef75d8d8528bae00414ffd8cfda3f632d63ec4ea76ccb4afdb4164d12301affb1b7b369f3adda11d7f951408210434885c4659f20fc50690dadf619424cb1274f5abc6b1f374c6b54e0d06492645aab59704b6ab97535bd282a320622ebbc3c1df2d9c185ed9e0814450a53bddb1101751b5e50e0620d6da3db3578fe63e35623df745f5c392cdf2cc51a75f26841632d8d3d663e48b0054fdd282503b2154728fa7ab29a2dd6072ca981a9bcd57643f64e89f78a61c08b1b798f18dce966ceb302b03ae05f00aa95256b68bfd8b6995e5758b85335c68a9dc4be945979c4c53a5c8ec86547b10a3bf632e2583b0fff165c139e2e84a028a2b4b6df6bd70ba7503c861cc616cacc784220008e51810691f4e869d77f083681a0165021b53c009743eef8266817caa4db06c070714030304073304176575b15f9ea6b3a093322217b35b4af28e8fd4082f884b94eb53273e17904db33a51f5def794023218962128ee226c0c4bc731caeb6218f7f65e032c755190641d0da6cce375aade6e663ca176a6ef20fd762d66b09468af0fa36a22127446f1922e21388d4367d861ea2be2e5055f37a34638571630202e1f2652a40f1b54cc9943a1a18fb2971cfbbe5a72013e916044bcb751abf4e9da71d31b3265a3506564c1bbbad44e856db1762543cb583c7096cd76b90d067888f143e1aa629f22b48044ea24ca9946b22b83d187e925f12d051a9b74a414e2ac39a4367227d500ef60ef064c0846b3a34e754c67b122000bcb91fabb9add6a8797e6873f9c2d724046c835a28daa8b49223e24639b392d6848599cc5d33fa08072059a3de75de31f3630d93c43060230a54aa295a5a1a0336e260155641d2b166505532bf9bc49ccee8c79686b73f25d38de8bbaaaceec5756eb846d5d3748b1ca2fafe434dcd4d145a304b432046f670fed853c8e8eab40232024da5ac034fa6f132c6d01be23313fba047fe55816ec46c63dce11caa24640e3b31044100de51c8c3021126f5bf2f3d0344fafb0039c6cf397a167a83d131feaaa8f17a2a1aa828f82e8161ee41da5e50ba7b90919679993eaa82279d91b0632ec36677a2a1ae206dd4fc8998477fecd4f01f28f33efabfc58a86201ea0586100f83855b7cadf43100ced27c34e43278d767476cf25d9be8d0a66b3fa472e7c205a99063b2e4effddcd2088748409259bcb385d27873605afc19e9dfcce3759eed132ff35f1b248518b69dc4f804bcf8c8bfd24ddd764900097c54f19893881e9938015193df37ef7eee35fd18b02db8355e9d6f051186168aa64e32deabebc22826a812027352ae04e81044f9acac51f9b98a7b111dfd3f6a6c4dcf93184918f03807de73c37a604f932f9a673253a3c6f109e6fe7949d5941bc9312f1cd03787aaf1ef8899ef7410a0169ea3c04e2d943428cf218f06e2fc3debbfe9d0979ed36460497296241246526b8afb3d9caf2a3636bf5330722a60c3574fbed903899f35e14b326747beb728e2f8eca4d397d418600e0e9b90cac4b187f78c66092603f2f29bf05846103da163b601b03afdf1888bf5c97e59749dbe14aaf20f58338688192171f951a8c0738f29f0f4557ec6c74650981673596e4fec38f982014ee08a9e1fdc75331e9b239f51600ea240cff82f8fde5ce48b83dae65be1fef39a96cbd14fed44f70ee5d169186bbb7254632463f14bb5d76609c27207a690d63b5880d6835718978a49ea7350fe3e3c80763e29701b6d5051b4d89da108ed45bf86549fe53623d2cf829f689d351c7e40beb911ecf06af09ffd1e17364c89fa05bfdc019797a64616c8df271f1d0a2bac8467af1b2cca06ebb911e46adccff2f56e795f53529050edb600859d7c10e3a34f6053f21725df61ca4845a4a971289e0cc599a6900ba311a0ff30cb202d0a355dfc87a6c3729d0c6aed9b07fac0a4209d9c0d7a94102e5a5b0f3c3d5f06a81877284aec030fbbf0560a9e11ecb74a4d95eebc899842b8da6ebaae7cc5d94e7f5e32f18a93044268682f03142de3b918430082a4bc2305d13ca387dcfd34f1a0eb74bc4cd380cd7db70c90e462c7add605e8b39643bbe288b65e0326afc82eafdda2fffe46339bcaab2bc4fc8b80971ac25809a600a72325fa9f2a61d4654a86150f6cb1475cc2275bf43ffa81b887f27f38022faff32bdc9298613e5e78fd3f25d128555ccbdfca7b6ad61047a66f6c93d94fdaeabb9df3453ebb35d8f0a7a99f3dcba970d4b96d683ceb7340c1be3fe7e4e150049cbad654883d9d4d22550462a35216e9b0d28dc000934b9f1ccd8eeb2e67e01759e4a5dd3f12be6d316aa8d7278d49fa19a984b9003f0b42bbe4a6471c630a461f55db964feed1908c89575510a95b7c8578a005812145c561c741e5d52f4dce4b5ac939911c35e755adf6fbc811c3c26ec8816c3aa7edfabb70c5282f74aaa5b4cbb2313ac8c9a9ba2eff000f1db7139af0d3bcd19495b405f656e9ab6a2eace62657fb132f767d8b33547ed41ac390011e9e5956c99017f10a7f3ff7932edb520ad95f5bbbc50285bb4fb740fb6cbe0487d7cd2a7f3b8925ffd56a1259845fe9db620257276f0c10ad9d29f49f7e67daa58909d0a09fc529affacd1c0e70f03c9b503a87c09b92d9c476dc05b322cb8738ad1e6e610e03094db78fba5395616591f511fae7e2d6844d771fca020d131242af708d7594e9e67c19bd5944629d2eb1363ee1ee87d4a3187e7d38d58d73a5ad11950162ddedeecbd5dc79f69e9084319e5a6231d81fdca09bc91a74a71a72252cea4c2e776f7f87cfb6deda43fb7b5671ee79f0e070750c16e7b405a61acc939047ee4794cfe3a0b2f3f9c607507610402cad8fd59b88a83a83d9ab6ee1889b15d09b240ceda5b04a700c955fc21406251d1fd01f8c804bb47b44b6eddd84028b8e0bfb746f20b55573fa7f0ec340a975e4ad378485fe727fe63126c11f6d756927b18d752a8852071b5f8bd7989e627262092e2c6e2935ed5eee77222fa8a5e64c5003103cafd6af12ec523173c08838a9984117d72830534db85d9fd5f2951e058910bc366e8cf9b08d4c6132cad7a004d5606f1da5042fbf6bbd791ed97f56ef81e9ed1403e99e6fc7f30761055a23aa6d341c2b37d7c52da7898f217f27267fbfcd3ea04d557663cf6dac724def660cb4a2fe79fbf1277959fb4f3cecd6ed3d76829e9ecdd23b54a7da82f755ee2e7f4f7677c3b02145cda0a835f7b98016b17700b98b5ac5a084066f115faebe6fc4b812ad10b707f2bd920cbc7595084264a28212978cc6f2e7b93640efd9cc6bf1bd971a7c238697fd1a1e062fdd08f970429e5853c41babb86afea130c363ca1d9802d96c057164b0da40559cabbf23e2e4dcb9258160bcb00d979ae6b7b2eb79e86bb255a607621c2e305b7c4d3a255013f49f278e5258765f31c38ca247bf52f3c0b25a4fa9a2e1034e8cf9ac4ec6604022c14e793f653bb83a0404242e41dd3d3b6475b07358f0512033bbc5dca0ac4df486d16630b2981c02f2526684d9de16696ff0f04a68d66b1c64418a028ca8a2d815034e7e53da6bd6a4d13afcd1f7e4f08b84e26fc1f488f2b3f3d9773225dd5317ddb0392b9b82fd8f890ad55de39f587bc594170cd3dc355c2a3d2629d8ef17ad481b88081da25358a9ee0427f34f2d1734561efb48131da8870613963c67aaf078e25b48dc8e6b43a72e21ab63e8b071d78d5fa90edc16b2967c943f74c256fb8e6acfa15350a48e14f2dc7598e03a781530ea993277fadf2552266b00eca91784e393cf33309e0d398709882a4b9126efc01f21cb14a597744822e0f762ed0838b2164114069ea4e08e680a430306dc8fc1c426f6a9f7838b3e6bd74284305c212f9094c665c1afee2baf481f4e94b42ebfd9be36d8f320b41bc4b7c81e69cefaeba9776d7c51b028ae87602caf4cf6d417cc085ccdc5c29c12563177242a4d1dae8cb2e24db05d3ad49279ab607ca5728410fbf258ae487d621d3940fc012cd3b9948f339e9e9193c8a34bbe7fd85d5a82bccfdccf53840b715618bca9abd097277f208802ad7819ec2553e2be938b9c0df9534c37d270815b85681e292b42faed5f1c0017efedcf47a0c59d12bc6d2ea6491e6a69dcb9c31f3d3e567f77cebc1b57761c60549727b20d8051b23d1ee48e9bea600dc4af9e84fd6d769c64232ea7e20274b9d120f5bf7f0b14d506eeb5aff2c3f4a46a53b2d065ceee746531c4db8e235690c9c65f814da6d54245af8a96d8be62153dfa11e77a750e0733d54e9a69835fb17b806a1cb08a9bdd8b779747eb58adbba087963c0b6a114f7b59525106e07d751eea4de2345cce8de38c26977d6318e93460c6c262d2784fe18e1847bc80d7d8a9f8c5b2e370c09da6aa8fd2c2233e0dd67963eb6770406e9effd71dcd2a5c5c18398754c58d67252f0b9f8418217138215d17d6ec23af174b9dac9facea4b06151996574b5f3253eb198a52f859b29d9570ca2fa12bf7e725c615059f88a3cd45d09d713afcf7fb273e6fe6af92c7c26e8f2a9d9f6847ac2dfd3bb8a012a2da59681b38463ca7e3f0e6a408b1f13a73425456530124e0fe038bbe62f668a341d6d826377ad499868c76a5a2f8bcfdd5d5e4cd9bec684a6d3036b02626d02a5c6aa70af4c4d76425e9eb5e3c01bcfb81342b7e2df02170b3f8251e8fe218005950158fc4604102319a6de43a617b0608100831dbf2c64f5d9cf2d67518023caba4b90f9662067295d295b8806d176d1b2ce7d52d9da8448c3a9aff70f6974e9b7650b0459355ab24da85cc2862127a0800da875f19733c71021f67868379d5ca572d2fc77511c569f1d0405b6430eaf23d849b9a9caa0514a7a5228e7dd174ba7b65fb6bdb53db132c513302b08f87cc39908b6909480e4dd3c6f6a5c72928d8552493319d2568d8fe239c41f49cd1ce0258651659cdcacef856af2f5df24a07cc754a0381e5ece39fcde5a447bf21024cc0462cc63e5c72ae74486bfe61bd4c40d0b1d2c34d3c21810a444416154c19dc2181dc1eb336734b9dbd770d4011e0652b2d2a112930e5e6614e7d33a9ca1bb983649cdad8fc0993fd09b3d7ad9feed3e3d63ef4eba63c76e4c01b18b9820451a323dbeeb4a17e34fffbb8dce58742ffdb160379a4ab0c7506d0162c9e9275d0c102e31989250ece21f9cdffa35b956f5178351fd5524b6516df8b55f07c7c391356c32080471f74d8110ab0604de7c5b00c44c813f785ffcf8302da0ccebb7053ccc37f0c9fdc59f0ed302940200ae732196d4a6ea10ac425b062d73ad6e7945cc23499ab7e11f16ead0709f8eb1ed0e5db1ae1b9268dbb1ecf22dec8025445fbe0f1f0330802148395d89e21981fe6c988893e39cc739fe72f917eb7106c720b496f2727a09db37d889600789f8a8a5517b8d1897cee077646e70248ca357403c7f4ef99bcee9865e1ceb0f695f3ac8e83ab87825dfc8d2b938e2848e3b910f9e8f65611dc01b082f11b071f5713c0e037ff184bad53ab4d388015e703807558dcb3f9347fd0fd6743c0b2f79e1ab8270ac20e37ed73ebcd016ba2ef48d03c997e48c94250af83df609044188d295f06c4e57a315cfba7f2f7183d1d7a93f7569eda0c6bb0cfc715dd382f5786e91798e26be80caa4ad4d5302994726b61d7bd8e3bf88e3d955f9a75f41a97eb83201d25e9a0927054b227d5061b37391db9a13c4bcf9a84c7549ed7508f33a2ab35a527b15c2b875aad38d58d7ea8a811acea1d597d625ef69937ed74d539f4931d005cb888b977e6e1825e96fedc1ffec92a85ddb6456685c0938895581b19efb9b8ac1ee7a0a06baf018325b80573fc110396529b99158edf88084996f8e3577d93d65bcaf84d27adf36519d2579f9ba03a7ae1ed7afed07054f8b9e37926d821f37be8f9c9467df8a008160b44906b50a690855a4898bd1ca0217c5e1262be50b6689450c755b286210cb7c11e3e4c08b1800cc77017793711e71d2644992284e76bf34fe023353e75801473c602078fb03809c5292129c8a5718c68bf8f84699cd7a161f6b106dcd0272a3c5d201229f7ef55afab50dd96dabadf6d3586980b7413a4346455c8f5d1eec02476c4bea1630c1401714e9bb1c64eadba48490d18676c82c8a335e6de072d0ff627b1032529479993613e4e6790cb3f178cf333638f9d6f2d3899345fed1906ab4ce1f8cac252dba0f14ebf7742c6238539c59712ff6e417232ea6c709aa0208f1cee2f1453a12f3ae05927c3d2e144620137c3c2a4e0a695514cd20dd4c8f188ff9e74743ae04e0df788b681b6a1aa9a182762a4ce04a89ab9bd81f782c1c36ee907238545a4eb906dd55e932e666de8e3c7e976244cd4b1bcefc4860f903aca783a6411b54e1df5907db08c17390afb020eef68a2c4561c16df4c87b02f5f4cba6036d4328f8c327e0d7abddcfb1205a48365632ee46f863d783214fba796bc4681061166b96904d23307596073fe01ea65a4fae12c4943685b89eec22099f048cef136737176182367c301279d2b1842062f9e21521a33048c1d76b177ccdbf8047d17421aa13d2194db20dfb0ce3a53dc6a2b9015c299a2f8da96b66c3bef9a2fc4dd25f174899ec1142d950c318a08e3502c3b2b6dc0df62ad1c423eb6de0928d95c84b13be13ad521fa8c6218d3e8871636cb5ab4d555af4858ce3648c044919555165edba69a7417288117b35c5b3d09289afc05b6e734a03d4147b5bd04c2422e629f27e033b38145c9de5131fc5f8d31286e78144194ba0542d3b06040e23a15d5708523783e7485a59b5295e809c0ec2cff9952a1a3407778d39306e28ffca029acc7b7b8cbb620e4b04ca3a9476c81370798347559697e69c171d3c9746e0e2ec88c2307977f856f83141127ee6fe387730c7d3807dcbb2ae4f32c29e81d08f0fda3860a00f2a566a6d2bf2ce6f587f1141aafe8377805853b0972463a9de19fe11269b0ac2754aa58055d4db0f4b215dffb1d7f7a4efa2569367a0b62218221c8c1e86c9322b69c426ca6bf481e1774510a7dddfa04dba791557fab8535da9bba33f1bbc370e613a57b5f87c63bc71045c7d471a94a3928e04b3b0b2c396b63fd0cc52faa9d7cf0d167d265d6a4e1955a0e68ae7b555aaae683eb6e3c89d555340cad34026d2e4b1f749e269fffa7f4338e41f15f626f2de3c298ef71bc0482a9fc40c2c45f6062114f48025d9f8bd48d2bc9904a95e6103cc06f8190210fd7fd0fd1f533c497aa93c0d118c72ec7f2e75fae257a4e53265f1a67fa80f97a920cb120a99f3c775d754c428063baf18ca55da5048eb00526253fda5b8ae69df24ffd399def19a398c842f043cfa48a180f58c8acb20619ec132d65a6275e1a0a32f36f866c0f74b81ba5bd70085e04fc8b16c1768ec5c391fc3f0cd1d67308d7d1d3470805696b9330a09d4be8d2e9c3c7309b93b0cbaf9a2b22631ec0aa19661eb98cd646fc10d3d0a932db1ee672290cece5f01357d1bc53eb22247ac6893e0ee28d49b22f5dd94af7711bffff505525cb8d093fda6bfaaef8edd2a4684d19968e6df1776b902fe1df1913404981eb94faa8d4dec1a0176a67b047a5e0f287381467d9180f138851bc027637b81082004e177b9cf87c42a7ca0ec01eb258a0249c0092a108b583e944b76f765604df80cb838ecf67f509dcfc608f3e05903e5cb57fa0406a0944b4241e4a4425bd912543715a2aaf587c07d5ac2e0d86a939340d0ce2dba802eeecbfa16159c24c83508c2b7de6b5e014f57a006a7273117acacbaf26396ad066d43e88c130671799f923104e8988c52c4574ecce38b9f15a5cfd727042b332a01546e26d62e651b41d8775741f52be3b87ed00816f325f121caabca393e174b0704da54bfa10f69971ab181cd34da7b54129cfe0f7a46c86cb04e5f37dd137c029eeab327ff376db14ace0a8c1b02c0b8e2232ff60f3dcc9649d59240ba4f7c0115f632dc87bdb8d61f59138228846fc758600c4765942414528b40771ec3b5590285153c0f325007544f499859291ee18d0b5996aaa984cb1adf2fda27d43342705b145a5e220bca47fecf987d1842375e98e42e6aa29b7040f369074e0f918689d30411ddd220be5689569088afa96f9238643b8ffb1f2a2b821d65344d063c1cc5498f7e996dfca0319df2e9b0a6c4aa21a4f275d951ac40799c8bfb647f448fada102bbd8bb1bfec4e3f1685224bbfd5eeb0309ef08a27bc91dcdc1c3a91929e8e2bcf238e9567311f0db3f34cc67be997fd639f5c4526b5665f80799a798509b37afcc3fbc1f99b9f921d446fc34840ec4e97256ddc5ea78cb339c3dc75eb05075d4112cb9860712186ecc5625a0a7d117eb6ed33396dd8d9e6df087b77bddcaef0971f9a07613e7deb4b54c0d24e7deef9c49fe9160920ab56211f61e251ca4f28f22ab3ef93d5ba829552ffde6b24fc3bba8e24273d636332663929397946e54fcb4eca41d511f708a25c1d7a67f9fa12efbb458c566f5d49176d38e4eb15e41241d5dee7e645de98ae19d001fecd6ad1e93490675c12452dabd3dc06e15939ac641bd4454e9c9dde4be38e4cc3efb29ddb670b9f20737636726ab67bf5ebc3452fd27d12dfc98eeeb2ddd8a3a51aacc8e1423f8314e521468ad006da256f430356ae6eadfedeb6c0c3f0e0b4dd6d1091ba1a38399b88474bb7de9d272bc42772f66fb7cfd0779bef789f1874756bde06253880a29c5afb0b359b40ff6c02ce7e8110ccb4dfa3cf44ac662ee26334d2a0781eee729b69db5c733434882eec9815d2c8ca252a103a36bb6caa1ceed78618bc6b45fca091067a80e068c628b2d4f615d420f12960d82902884aec9fa735e06176f1325b76a67496c04d2f47b2cc5502f14b689282aa0cf406cac309823ba17ccd9ff09ee223e143494d32b7c464a83ae8444fbfb92b31da20f0d246f593e81f831cad3a723743a51e17e9744cd442873a80d5335b4f7bbf3e0fe9f5423205b3938c19d0a08a343d255c239c01df59fba7f32a7f8596ac9664657d84fa2bd2744a19d2301dab6d8079bf5d9faec1e956aacf698af226fd1c2f018f2df22be10031cbd97bfa276a4977bf88f6295622b777cce64718c7ccc2e243680093e1a1fc23abfe412c7d9b0dfacae1c167fceda077ac5f2add68c3aaccca1c16761f719647ea730840bdf6aaef9ff1a3b611587ee1ae655fa3191f9194a415799670d69be3fbaa4ac0879145f94fb46a91bc7ee0e121b2bdf35e9fe04fa86a6ef8d7065bc327104a76d9d87187b8d4c985b6e2ea6a0b05ea1fde74376b13bae7dee2c9eb15d6f3ac5ea482ece2f6fa4385475720116b75ef2ebd60ac572b1554645bdcaa6ea3a11a97d1fca8011063b1136109d27aa3a3893c9701957f3bc85dcafcd694098ff2dd2218adbde2e11c8eb8ef850b6d98870de480739f9e60a59c1bbd53f8eb2f13d28436fd5b05fad05a0569ce6c80a03d9a332d626fd21095d24ce3900b89a8c318c9962e876291c0cf3ebf24d642fd3032fb0f28ae434d3512cae046569a90382aab6d78986bf26331d76db2e312ca2177558378fbb72dd6bd9e68cb9bcd30a46a738893f390ad8869500a6785d3d97f10ab478b0ae1521d058265f4110dc66e053e0f6365b8a161c9ba998f8c6c3e0e022795da79cc9a07182a3168b5c2c2242e5e3cee41bf2bcf1eeee2c43b53e1b415d29429158c9fc1d6e6321807208ada164fa28277494af7464234c6d75f079ce1c1add846f57904ccda5d782d971b4e47380771c2231ef8bfe81287c911ed60ef784476318e045260708f177ce1f8f5d114a4d060ee6841c5804cff0a0a412981468146aa49e28081bfc8c86f093a7fa2aa356fc843bcb97416412e10f787ab3715230d5991f0c592a81d9343a18f1697812a7be56daf9a5fe7e04cee01567b69c1d6ad8197cdd19069eb9f959c223c9fc948ad1fe073ac2ec96d5b3108b28d556e1effe812073d572a47121ada15159369f817bdaaa2f2a6be0ff866f28b1d61a162a65a1f32e2f32429470e72d2c213e477ec579e1bb1928ad148375983b9f934e0db359eb9642cb864f211442b4e24546d9e67496c2e32580a3af4503f10d421e841a4c00f1dda552dde00f8ef50486bb5094475b73df2efee6645cf8d88127121cf5669b17820fff65a6c132f52ed246d4b5554a72a3531dfe22a7b5894f3df402a3a6d018e5a08411b435467a5e8c065362e44cd985da587c9cc7404cd823e079cd6389a5f1458e6219c86a91f658e152da06d0ce2361b40c1c181d69d4e9db01531a0957210315ce4e157a746f24afb298b232835f7d36108278672a5741b305dd05201c86d4881ebaec1e98fca97d844f69109d004f97c03571c38b545a6587a1efc4095e80244ff9ea299759a33153f0a24b985d93dcc829d21e625cd4ad213e0023b3b52b9e374212d30c738374addba2d3b17a8e41d42107550cf9638e48709a4e9e70545c63342ec6e336646cfe7cb13512db7675a5aeb4b8b63d15724564926f20be06df3fc57594918d9dd2870b15a42cbd76aa03928e4a53d630f0ea2308023172c21c79aa684a2e163f224839fa05b210183c201856964ea6705c40331e27c6e33b647f06929b6de5c8eceb38b624310b471120189fb4ac8e6564846c2a0fd48a907c65b4538973027c529a8d19efe2f043ce7181e9b978a63af97ca5c0f42fab868876a862b29f4a14efcd29a2f0250a329ec3dd00c63d4916d3591895e816f18e56d861bb7536758c5564630873ade367421508950d01c79c971989c0f3ffdfe2f7b11963c867372c123541506fe2da525ce873af7ab624f65837815b56a798eac94c9568ad9509867a2253354a63b58b05c55849158bcc953d09a22ccebf9c7c74d43f672a03181ea862ba41dad460184654ac2ddcb7467e52696d50467bc5498769e2dbe7206861212a7148ed6910bf44ee06f615e4893a129d6925ecb021f7b5eee403322e802136447faf4875c609ea90fbede9b723f30d9b0cd4f51dd40b67105d67556cbfeb38bd859c01d8d9f3921d0bb8661bf2570cd1e283dd77012eca00e8c106701fe16a7ad047d2bdf8e4256807138ea34ad5d34531c3cd938a804300303f00dfbe4312bb52021e066d9f4329970a7f7099614974da2327e464d4157ff185c34ad349958e0b28e6c784581ca7cccfa9139e8c754aad53ce789510aa6f35cc582852aff4fcd3e4b512a7bfb5e93ff1c267524a3750fdfbbbd52c83d604348cada700ccd83b3fa4c3bf2eddb0e54739aa5bea2fceac4dd4c58243719e5d694484928a9d030ffb9bccf3a4f011730f8e26428ab806b83b8a67e6591521541410f2d6430aa904b5d1825a98e3ff69f14121a80519cd857ad762b0df0ae9d39f7702cedf416ea034fc2a352ad0661a3c85e1f73c9470c611f56b9441a899c57086ed9bfa295bb207865be084d96a24f6c383b9badfd627050d21f20062c752d8545d4aae67da3a6a68ff8bc25209fc38ce00f6b7401f221c0e827d219ea85e35c09189131a36aef4dd0f4f3af59a8fe1914dfbdc3a26c7c9fe7d3dd8ca8164cb612ce9ca9f37f0071388626b75e0437c1394d1816d5580d381b62912d49ac9552b38efb2ae325f67c5a38fd960046cbaf7e786d54c2e781b7410c35872fd801df31230b0ca736650d0a9a078d3e9241f799c5a0668a39f7246845602924491bfe621254fb6089f663caf99c1fc71e2c5da29a6d1fe0137610c83ddabcaae45789f4e38ade0f0768a8d079da0bc41907069883771d94e83f1e6233255a53557208c5b4b99de88c551baff55fc273937fb10dda1f96e0c5c1b99411bc9226d1e73eacb494a2b1845886c2c6ce67b8a82abe5b8fd0c803e5d6bea79086fd00b10a2a38b140d386e78c2949b7e9b517478b012f9497bb2e45e75b2025eb26432c5045f9a7227fcdf887377589f81024d6747c2d459222684273db920e0be935a1d6b1562c1c5813a3ab0d079f639b514659b0eecfd731c5c3513d1cfbbd57aa6a482acb191cad1868e90f5384b5137ce1515f9618a66baed6973dbf9587caaf4bd9981e29382ec5d5634e904608207102a6bd26af02706c2f02f8ce4e945af3b3bc5c8a972a940501372caad1b6563092c16b0086ff0f38a5d24a1795c3930aa2c8d81626c8b22629d0453e2c37f8eb612f88d3a2269f14bff3df627629032e5925b4b274d42442af0360b93d3486203152599ec11788b84f973976c139f2b361ae6ef7068e86f9484debdcf5b369a02e138b7bc2eacf408824594ecc3599e1b13e5e1f6632c5438ab74e881e499cf1ad60e7d9df14ace3b49d501b8fd6fc23329ce26bd388b85a0964088dbbf5180826bfd96c0bdcaedfe26abce19dc3e60892e274659a5680980014b39675ee715b40dc45722a3b2673be53b3a21141b291e55548e7696b5a8ea458f3d42276c03a766889b4c6924615cab5d81662d4b137ad4adb5951144aee51356b3ac9aa855ae72c01b88aa038d10128ca41a42842d05d0a8e57d9a00f05f7cc6335a374f0d30aa2387cf49fc01d60f35f786b8224b7938c00e37d14196a2c7446e343afa915ed1143381371b797ae134a4c74c636f52639ab3edb45be6620a2a9427ee4ca057be7650c9ab04131fe2f2da3668466a6e10bc4c43805d77bdde0d4c2397dc46e2264380c01b097f98e0defe5a74ad9cfdb3f4ce67a4c11e4ecf47a2f631d486891a59e741301a41a97ba929e2813a34c6614f85ec803022e2bbf10e3306f8ce551ababe5505ee65387c19aebf2e737d92bbd2d9426430badb7048a6329c099e846e68dbacc2ddc9be615798ca09191b02265182ebc2f2999fd42ce803a1fc7e6fb70641281785d70e4ef88d34ccdda5c29dab15784084bf92d2053b90cb001c351cf8969db219ba42528653623381b81a644924a62f13f0d8441008330e6d8da43679a3f1ec4b6085c334c5742831236fc9a0982cb2c11d08b1d391d82b8bb58cc664a877d15f49902e92d0f8eb216c9e94975049ef4aee336439a48c2eca7f67f8485bb06126ed37c48c92be3b4642fe17df94d244bc0d2bba9d6737f581d85094586d9ed192ab4ab412ec444c348e272fef8f75274370bf680d40bb2aa1a363db5e951469cfb71efd4ef13ee1588642c40dc1102846eb770edb4243b373188fe47af3214f004df5c072a970e5d47066e85996f0b58a3bc82772a28014359ed98cdc0a690734c1512e07c3931cf78c85a8dc42ff6557ef5fe66441beb6291ed6419fd73a4afa2c1c77cc890e5e3c68f3c32155beb4673f17902b05ef0df3f004eb90f7c96dc16bddbb442cf4fa8eea99a07545b95e63b7593e53fc22e7a1ebd80ee49823d57befa41c6031a6b868813976fbee217f3c0993c3e03b7679394166e43dbff4c393441de053bc0148dfeeeb78a33f1e97c77004b7fdcdec2942ec298c51b9e6e692d9142cb94b37013a2f1be2b180dff6c387bcc9ff37e172952531a4e4f91330a6434d662a2982a8900e70495ed68c74b8b85d5f93c666f8bd00174b956373904b147c0685feeaa5e0f56b28abdc23595e08149b0970277551dcd0949980a4de2d79ea201b18ca0dcb811ed735210b138eeb4bbacbfbed85741cad739d6e452796dd475f2cb9393de8bda6af1b7d59d671a3500ba14f91c7760be817cf8f993df970fd19c3fa16e975207102c4a422e4c985243698992c72666ab7e7c8673e640c612b4a5c6e9206d757afa0aa9b409466ff83d21dadf7dcddeb6cd4ac283f617523482225be93a8da2177f9070cb49e267dec08e910eec10989dbf53caaa3f508c42a13b52ed9c43cec9b3d43cc753b12686e8697e976592e2e2699bc2a672939be779092b4f74d192097c80bdf7436785338499dae45fc34e37b6891c0ac49ded3c87f61bf27d5bc393d523cf15be4c247a296662a3d38c80a53184db1dcc47e17c5f59860d2e0379804c9b372961cea8777c867a951a5df0f04a983215e2e3ed089a2417269fe93791c4fe905a4ef6e285dec3862cacc8553e05cfe9e7ade840958f5e8c9ee0e7989c6a4f8888914fecab827f14e4162031bccd5f052ece1ae80ad1d9060b1d772ad775b9f285da946ce8f4fdad74a41774e45df5d241df906ccfbf387e43bf0df54d6bee69e04e734dd65737ceab34e35ddcaa90b2cdb3f494605ed3f52ce2df15cbc9940ee88a9e587adba1c255a19cee56f5cda04958a17e8520405ecd44008b2db946ac4a56b0840f008855694c6bc0393c5c6f406bd63e726ba62f5e8bc937b28fb13fbc7c949d858dde754cb25734d40609dc8ad953f9d331443b9982fa2a6e6569941ecc044bca10f3ffca38c510536f2d423bfef295757c20131704afd09fde4ad17f472b6791167d56a9b2e7e9311466871daf9747d377f7614da42cca6c34be2aa3210b9de4426d21ec5015ab86f394aee4c30ec3110f05ed02fc61913a9decf67f773969b2ef8cdaa745604c2b4f46494e0ce7c0c1549604d8fd87243f758b969baa2fcfcea2c61522924bede926a1a7157d4fa8c2ab5c92d8232810adea9c5827e4a48e0d3d45c96a0a31b822439b68baba8b7aff27282f1f8aaf98ea5c2572e4977b6de4b22c0400e091f3ac825723e246913f7c0bfc38455fca663ae013afd3a89a1ebc6d2fed703d7be8348fb433c0fd4ad492cc15e7ed63a1c9247ad7982615b5abc294ce2e3fa89dfe5d21a60bc23b00c04560b200b22e6721000e6029313ddc893a82ff4a56b3d63128dbbd1f70ed97fe8aeb15e049793568624f899fb5fc3f19b90e43e3a63d77dbac4e2a679db9e4e62f676096c60fc74c4a0ba95ee587043ff58921b1513b48739d5a084b41c3fa511ba3315472ead26d4a598db5ec254c1c56f891041ed686be6db19505e1642123e1c543aa1ccb0a3072336279722373566ae8b102dd9b3b946a3510fa62f3b73c187c491cceb19119595d2aa0abf2ae8364cb102d19fa65580cdd84038edcb12127b6d1d0e373e8e25825da46c4c9cc4072a338b05ba0651823c6202dde7cc2a7f4f631bf716d2b08ee576188ee5a7cf0d3579308a8df7f95227cf430eec510ccfa7edc53ed73e39a360dd06f64905cf120b666927b2dfcef46f58fec98cd3bc31857e61d54862c4a356b95613a8fcd1f5f44dfc8d4118f966a52a67e88c9f64b09019949fa81b5ada2a8868f8a9daa804a9a5474a90014ea6a826cb1642dc2a8f14964b37aa463124950a482afe7a6471ceb9a58594f9929e241c99474bd4ccc70aee7392a842ed51f7f37323b255a4dad7d08cabe84c75a9e61514e33a3a538df92457c1276814fd93400d8f9995ab76841bcec0620d2dbad3cba94f127bbe2216397f8558eb06a3c419f316697df7e950942212fa730204294be3102d310a974f8abb0c1cd947d0809885af40c23d391bb5e282b8968f6e36195480ab9ecb3c07a2ec7f3a3d2f940c4f9d0dfa2092ecc3cdc57dada4a6c298ee1fbd7e3245c17a69c8a2302e921723b126d4f70f221cf2d8c1a86151cb05cc01583abeb3ed2cc0b395308ed182edeb497a1053a4cdac7014d94f21b0e770347319778abfc63fb753db5bb8b389a7e293bfe740bcbc7569afda83cc7b79bc64168166fa35cb467d613b6db5247704c82dfaf954f01180fbf4bc0bcdc05f26546b71834f53d07ad9f172e4013908b41651d9a6b5c4d3cd5e1bcec0c0e3bd20a6cf9e63eebde18038881a0370b8edf519c63e1ab21e5624b0b8e556572f1194ec20ee5e1c11b8d6e04cd0813d0bdb517bccd3b134519cd79f208959d7ec5309a5b5014c0afa8a0e5aa44814d49028823fa133822a543ada274bb86fba1083b5be33a3ad39f119fd749bb94b4e45420a09e0e5c6d035480362d08008ec6f34ea2bea8eb8292a1ee822e0ffb6350350a16b723d7cdb556ab642e54e2b1690570a0c8585c46a4a70a43696290cab22bbec8fd8cdf49ce880a0a93375eb4566918e8c6253baeda5e1e211c0c1eb69161374b3ee9fc6b5fe965bd405e1a5f863b889ba34bbd495d226236d54347495bb27c44c8e6a6fea6d06ff3088e6bb29a574f8124dd21645e3e1d7ed9c01daa439131edaf66328c4dacfb69cf63851983f4a53db211d53c19d46fa1797968b1c6d6cf6c376ef07b8d1f6211434f030a8943eaaa15dfb6ff6ba1493b116a5e8d07f1f38e28a74157c876a6a0c43da0d121fab647253791209eea5717f9666e4019b7a0c9cdfa31e36a057d05d32401b4d81ecaba6fbca116f97e71c084da716a0ce811abf06581828590842713ad76cf55c37a49441371844e26e888e83064cde96f51b097b49eccd678b96b03ad65106cc9240c3cd69039f71a883b2516cbe56c10a89d8e30d7090b22b5fa0a8fb1387e36ed8dbedbce91044dc99630745a77544a3ffd8600cd51da1d27c25587c31d23acca9fc66a998048d60b41fef82b0b040a9bd7101f9b81019d0d90632178e0f872ec26dcc71604c58c12ec6859b68e2009a9d380fb76950b7f1d03921531eaa99992867b2ead6044de1b57417fcd86703a7cfc807df2f307219a17b41bdee733dd8971eef9bdeed9bdeee433deffef8d50745a4ce28d1d21fd2a17951020392b7d529303ca7bb73333cbe3849e48b46ad7baef89b8e0dff6def2df97fc4bbb43d1827e529739e21022e6d43e2479bdbeae6a6d8af073775b76a429779e2f1b69b39e4cc6dbf5bfb89b6453f587917b289b0135b5f94d91821bff8877bec595dd049487a124a5aba082d4ab895760ad6d97ef16e2987d919997b087623a617a6d91fe8991531e691bf0a35c18edf81704c28ee854b4966bc74eb63f84b53ea9e12df6d27f04f3bbbc76eb313916329ab355f2f90e496bab81d8a132b65bde140d94eedee85ca48e6a87a54f75315b5d654455184dd61891b8de6acea09aa0e48f2ed2d154159f8f5c8402beb1990a992f2236d6c777c9bba8d37db1474af9f98f1562a2510680a7cffdfc608daf4214dff9dd80f49081f7b15390a0469d566991a2c1723194ad6a022e675d61483cdcb99318728ff915f3372e5df0e1581eec0a990539cccbc15bf4c90e8b99a1727e27a3887858bc03d7336ee892fef132459d4fceb78d6e03199589ad9a388892d6276958838259f66abb6924b0b3797db0b7ef8d1dead285021e4cc3a3c47b7bee3918657d441ba091a7b82fe3d5c4990e2fab0aa274738a63861cac5f546262ac3f4fe03d4415ccfe9714bb2ca6f178a4219a6a5476ebf8c7e1326a67636ad100dc40bf924e4ce721a7b89646d7dd268714eff1d584c58dda4f9b098b48f9e3ada04e741c126ea87dcf753a6cb12b7734514a78458dcbf2b56c0c52f501ffac4aa543fa838dbc22ede64d77b2c4d134369cbed8c0eefeeab98ea0abbc8948bf988066a4efc7c7d1c12352b2df90d1979cdf4bf554ab0d46e994e0bf6e0f29d246c0c95b6687ad146865422f4126a7b70581b8b1df6c269c48f47d4253ebdb6ff916d02603f2bb2c084a9ef7e41c538da4d29461a2b500815f918a09100ddc664cea1fe533c196bea9e25927079cd543af1bffae005b26f789974d94cc200da0bb3465c27f7a9122fdef61b876d7af874f2f43117493ba3bceb85a11d64e29edbfc0c989e5415469081c79ab827a621832775abea3d5b67c98adca450d543a75e1fc51cecc8ad5b2abc5ac646448e456fc8195111e66a08aa202c6b0eb535990817762406862652c412a10b27d23aa1b823c4f6893b1bdb374177a0f87df363fbae7b8aed23964263fb575cb3bf1b376a23da2a2a60ad2947aa6d1ff0ca6e5ed690eb633c886c635ba83e33d75511d9679c23a3430f747c39716d1cff8a6283af9ed12e7f887ee89f4c74664c866bfcd00f4a6dae80d36f3af76cf22f9436dc848cbe149346ba55046f910248226f58b91a4f15739255d14463e38890f8ae2a85010d21a5f4fa3d7b77c272be327fd397ab6930e33437e31f987518ac1abeaf839818c83d6da707d1daa9316bc19e60f8236f7f06ec6146ab6c25e463f7620ce2e1b33b8c4c0ea22131082ea542174a6916fd844892084171a62521d606616e8940240c9d46c1808a4fcad98167c4159afbbe805a3b7e73cbbd14e31d133f37ece9d2527e1627789208bf7b8dd265e1947d7722b7287279b247b3e49ef0f056b8e1d156afefb4898e46660bc2b47714b0ac311c561d703020225ba67e091add6f67c585cb739ef84330ed8ff341465ceb213154f4bbb74c2fdd108b5a61f0f14b28d8b7f7ab19d63abe9b1983687ffddf88d65fdad112f0da81e2a0805ae8c329d874e86c22a6e8a2e6bbcf68fa9bd7a1c3c2935a3ceab2fb91f5fd5c0396da3c69a79d5d828bc20344672bd155d491553b72c79befe5c60551ccce079a3244132092c5d77277b25527a8841da24ac98ebeec5fa2b9e1d4f10dbf1f158c2217e40eedfc8ee7d1e5a14b5a42f4ddb20f7f33a61109fc84815ad4922afe260b6d9d595f6ff77a04127598c871cc40327b2e1956c0547df1ded18050ec30850ee6f802aedb1555945c7db185524d8e9a5fd9f468b293455e822fcf32b012574121ced80aa350854625fe20d526c3624a3c5eef22905828d39a4d7bd3d7fa9afdaeee7d24c2e4bbe36308baf53be0a749a6b78dfc82c2d45c8b3140219b614e572ab09d6e2e6bfa14f567eece948827e3b1a779948900708a9e7a585432843e4dcfcffbc619bb29d7f717cf8669c7043b00afbf07f7d617ef38bc87c1cef7d658916c8d1ae0939cf73911690273a97c0dfc9526fa1a577fe39380fefbf4a239ca10ea76599e7265aa831a640372eda7258b0e1b446a17566e07a564cf8344270410731f12b7be22fa5782bbd8faa34070de05eeb53f80523b6f489caa93852b5726e10b7cff6fd057440219925450377faf6d84b6c416ad5c31401b3503ea6603d76a0ba754a38b85f437b690932a24f31140cabdfad6231a0399b5a6a2011a6f6c50d71a768a967671a734aee22391c3667433fe781bbdbb9ebad9e0dc07e7b6360894cb248801bb8ae0786f2ec1b8e7774346ad3b8461c7f241a051e9142dd40afc557c104174f6d65f82473f29a4e98cfc94819a072ff594ca3328c2e45d895a30ad3fbd936a0d6ab03e1471303f4e3080b2298769b5c838302f378a0c5f6560f0ec4010348a4c2a54df566b1761d17565c3ebf55d49945351a6ec343eb0481f1f79ce1b096d8b3e98f6bc8f8a3e34a2b442f37ed140b204f431a6c4fc455149114a326114df56e90b9f30d2f773905ce5789236a43b95f2235fa985cff9474898a04c493fa5da3adcee05d5b2ae8c55afa5996311557ce452a777d7c5543b619819788f7f1aa9e2fce0513f03b3e5f7dc47c11caab7cbca159c631c654bac08e965e9166611cc504714fcbbc38b894f09326b0619de044158dfc9fa33c776ecf2ee8ba5824518ea2398e13a1cbbf2b45e4bdb9fc604bce375127d5993567d9613c5c29a6ea9f9d0e50616cb39c165304c4e6e949a75abd2a09d113c33a427ae485ea972d65cf96e9359c17a9abf627b202828c835fbfc824809061788962b8f07ed810a7c7f4e7aa09075032bbbd7b46748a9dd901623d6685795c6a18bba1e9f96fd2fdb97dd6491db340cfdd525d4c9accea51ad61882eb5f3470bbad39efceb76e18ddbc872591265816e3f380bcf0e3998d9ff9fa7f74404c39db821de9ff986f0ad6863909c73bfdc8f829ad1ec4016bd430d09044f93a680e23fdd230e7da599993ea5ee6830ead6c47bf05edc83ba83cc163117a60b5845d85daeed0f1a2b4ae808e46a311baa5416b83d0dc884df29e5865f6b3afff83df2462ca75f5e58bbb642216239ea6ed352992931fcd65250479bcbbe002f793c26035515e4ba6b6ba00d1fce6be96872d71c5f279e40b451649498e30477bd7dfdce3a6d51b1ee65a25e40b5f2b86581c5db8b8cb4d15dce6f42acd0026d727674fb59020547a83666f8b75a082263e7f1f2e94899b6d7100691cf91d63ac29748b45d27533c5d4b5f1b0dbdeaab8765d35388be7de60751ec52b520ed0c1debaeef2e582aa8e1646e6bcf9d2ad8595d27654ba2f8c9a4c3b87e6f933f6721d29f3c20bf690eb95060b298cef9d5be1eda6496f1bca136b796332486e60e2dde906a2f1be1d352c2664779b2617bf0110219b2407c031bab83f2bad52982725a493bd97bda408345d153039b9a4df2f723408994274e1f1235a129725acf5db9d30b9a4e80071ff623d79cd3c9288425ee8e11ce59fcd1a09c5c00d218d799cdae38872d56082f40bc55c44031645509648561e3587d3a7ce2a58875b5065660dbf1d431260f8b3b4c2f9fa032fe8db5e41dee8c355f9d859fa10ce3223af5cf50ea4606b4f14196c3d5a8a805324541d301e96c420d724785635549706b77543b593654a69ca0a0b36d14fba7f0fdaeb1e5181e0cf58f56df4255024796920a5ae3b569f09884f5abf87c117af0c2996d92601a41f19518a3e12f9307125960015b9e9f4f40a6a588f5fe16f48220c54dd28fa08f4eec97dca7289d2a933ca327c8200cbd915ca30eb5baf34247f976c263a009ffc3ca1e72ad3671d614b8f72aa03b39e650643f00b768394611b0b22cbc61d50d46a23c1cfa0bbcbbecc21b24c43299013c045c446efe312e8f7d284b5be020d3b1a6b74ed427f6a669f72c8c9d01c3c0949d56c7c04a34f8743a4e06ed8c9f58918ab8ca458df5ad47db51ac6743bb5d425114d8f07dd070d165c453aef434bfc6f2a744d44df73c0603bf1fea082141017593f08ed978c8ce6529ccef9c5888b2a8945a7b326f6e523865daba62c8194d117dc58e19fde0d8da17930006e6b64f7d374321ab32603c8803a2db8b2757e9cae074e1317628e63175ffff298f846fb563cc7f6bf1c6e627a8086463ffa8ec63237915605f9bdf470c99b7aaa19eca078677839c5c7848f9e4797db86df89684b9d1a590d6cf0f128e5ca3a02f7112604dcd20d4b98855300c0c5c5cd77db9f8bffb7b89e99d2eff434de3cc10522b4a459fe185a639883fca069e34acb47a1de02c809c52c5d024859b053508fdd18e13037457b26fe080acd6f725bfee83c71a6d4cd19450f3062187ad18c72ba8c4f8665a2c6a2a0af94cb6c9ae1cb44a0847abae3ad8c8d461ba8245e9b46353881fdd825b2945391991aa7766208ada188051d967f4db909e235ff675392bbc90188a252e65ecaa23ce2dceee8fa74b3984fc2dfa26e880dfe5901d62d983d9f3fedffc88773ed999ea0e4a06da674aa6a790efff09e6682cc351a4ab3785f0a65fc06052a649476528d3e198f325feb3eefa7db1a48ed779eb0858ae95279c29b0ea494e7d0312e22b431d98b1f99d4172b112f9edf5af9936fa7886eda4657d21c5735888308f0e371a86c7dbd97103a79b20340d840780d3a44b288a549be4ac30072117a46f0f4aa42a18070a1d18ccb89813f6e09c97649b4458fb0540bc6de0b57703496a9c5b1c88570b87c9cbd5d6a454dcd050fc28d1940c7cce34255b54118eafca875220466309bcdbad2fbe2f2bf5165f5b812b8a3cefb87293d764870990fc470c3aeb352aaa27ff00115bc884a41f28cefc401d99e42e76ed22b98695eac06498f0ff38efc442db5278e52e5fc5bd828bab30da5c8242a79417ab3d6be9adea788ebebfbc67909dcd70d393d16c04d97553012e43b5ba942f33317098636ac255fe7751de53d1fa0d140cab2a2eee93b0d8ad6be771a483ca6dfa284ee434fb39949958d7e207e9a111752d2c57f80bd6ed54ef5c257a9cf3c95d32ddc6fa5c6a4c38b3d9ce70c2db38b7e0a286725a3a3bda4996111ec54fdb738437fd192a1f0fe848a3d19e95c23289f6b0fd1cc22cf5092331979f08b3cf4cc6d8cb66d296d69c697f65bce4495705b79781ab081a889ae4d8eab6eb3edb414cf8d6dfe7b385684f68fed7b08a1c272724a150e695d866ab5ff09eefd4515c0356956990bf00702f7ea0c57a8469e4dd687b21787d9d542bdf8ded9ca1d53ff9692ee50ff65094bcecc98d88bd24e63400e60f0591b5b979b1dfdc8a4973e12db8888f5bcb1ba49434c3cbf0a8c09553fd8716de5264f91db6110bef9830632c7679e55a02005ad692eb463cee93bd79e32ec8819b99387105d23f822dc4f1cd77781cf4e3566c28ee33b74b22d93c389e255dff94765f8d633d4013d9584a4597affb173d38ffb7e9c9920e3107a7878a8b96d719ae7a102304229332f8b1dcc5fc3d5a4225994aa54ed7ab657138cbeadda4c13067666d429d04fc46985ccfa3b256813d20ed1541d02481972217dc592fd1c3ef6deb1753afda3d5553e09348f93d0f92d53b2c879845b6ef9a0d458cdabec0bdcd24af62d3012b7fbb508c46a23fecadedfa72c86615559f8986955193002d8a2c12fc95113d8fbe879e3329b1e5275903b9eb99acdc733c95efdb825dab3bf8c0f011ba91eb67ee22b938ba44e8b4e6844c79ff0047600497cab91879cf0ba0241451f1a5a25f94e0112e5bfe99448e14c5e97f9cf1bba6844e568b14244f855e112c8f599d002ba4ba458adf4972bdcfa2e3619985d8cae04fa7d1265108b39fd30a6148924561b97b669d3112e002246965e060721fd1839b0c6a327adb2b5eb823413d698dac7fb6bcadc0e379204d2c8a1a8e303d53565b6a60b546d82ec615a931611b65236f8dc80c4ee11eb79b475e7ae02945204fa9283b6542020554d073820c327868610af10bcb17c04b0391a4dc24bf32c619571948e2569e58fe0243f0335ff7862c58d0878835ecad2476edbbaac25139befd5e8f87cbb330c74a8a814d48424d97befbdf796524a2965ea0a5c0bf10bd9c7ed4f9ad4a341df303069b07fdcfebca6e2376d2d2861dffb3567b65ccf67987bebae73378fdc6cb1e0c518638c31c618638c31ae434bc05ea7df919b768ee0127e1ddfbbe16d734c80f963b38d5467e147e79b2b34f7e59f5db1f55c6cfa422ae951f64aa4991dfaa7475d177a472959c9f00b1df49ceb5c7cec99ebb899098ea5e761883d3aa29a669d4a569a7130c94a4b94446d4e7d655a2571305ff9083327d3f211c2d03731f4effb21c8fec61e98a6833a010a4ed8d8a9e7853eb32b98161686996661a531eb58d7acbe72cf0ae62b0b6192d429a5630907dace766973beb4b40dfaa77f9ba3e9ca34ebea11c27cd949d274f2b6f3355fe18b7af7815ca651da1d6ddd74979ee36d8ef4ae35e684517d521a73ca94259bce9813a64ce14dd43fd29b8e73b6804e43c7433308c398486948cad125b9c39e4d41c7e1f9e6df633f9528dfb6504725421330d8d86b18a339ac24246c7518fee8e327ab0b83978a4197aa6d6956110358e9a7aa2f95a9e2659860bd9aaa9015c6aa2e8b43b5062ee84e895b6bbd74da4ae9156dad980a1fa74734094daf5b6badb5d65a70bc36c8a5150cf682652083f5edf3ba6f4f1f9a51b02a56d9130fdd21ebaab04b645d38b27f97c8c2708f7501dd210b1b11a99c6a7487ae35a23f2febe824cc96ea5faec18848ca4f8ea281a61be527948ac5727d519c736f8dfe2afe9c8b1ff79c831fa26aa81c99447e30f3a2f088ecaaa3e4a2735f72d2973e36891b4d4c3bb473a2836369621a6ff4382bc9893eb78f0d3aa924f9cabf0c821ffaf7813b44e776949ce49c1e4d4ce34d6924710e72cff9e8b4e2a51517555e2aad784d154b57b5b2e2a59187cadde2f8e3ee9550c54f28e75c857363398eb392efb7920f02018e25ddeede72ff7204bb057eab257a486a3907bae8a1b75c454b248da58a168a73906b8d28ae827b1450ab50e1a19f702177e25ca681c9e93639388e9c8e263728071de5e0587a5bf3d0ce32a19ca59d857216cbb58f0d967cd691441a4b6e8760e82c5f63c3f2c7dd2c5fb96832390d75a09cc51a4beda60ab2bed45ed62dfa67775893834ff2f04bbd43278965e8266e7a1eda4b4a6eba01475dc951a934f2d08ef2528f2c3f798d3d79bdfad2c73ef9288ca95011ed1f76a3bca6de946c947f3987b1948f888c8aa0bca6a67e345432aa953807fac9bfd38f8ccc40a37c3454c9ea2819ea6b6c4d055fef920fb2b1ccc2b0cf68686464541b4f9caceb343722525f737b51c8409718a8c449a038099306a44008011e9621ef2b8160d785fe7dfe758f67ba0b7b6040fcc8b1c67625a79dd7d48f2b8db7ebc61f57878d3bdce10e9d83e38f11c8dd77e3d007f6e18ce81c030c545f9c17e411a9af0c03fd39177a682a017784748272f08c326126739ddf0d749fb9532acdd5e0e020f16233443b8e092c12baf3a80caf881f7a93bed0c511184644541bf4214574fe43cfd825fd3cb3171b96faa95855d003595ec4e00a64f9fa5487dde60e7ea647eddb0a6f3018e2e9c225a62fd91ede681c82cfc31b9c029afef42aa0e94de30a68fad2de3eb0af9de4b522d8d7b73125bcb1fbfac7029a9e845556807a9334b0d275dbca0a099a7eb4c76e92780c2749626161096fae0b34bdf885e2c974e115e18daf086f340bd0f42199c99c3732674ab6004d0feebce3f2bc9820f675927b0f335bb0c73ecf3b302477b95c2e40d37f9bc5f7a14a1661ec2be101725bc762db586c1b65416966d1029adedbf649b305bbf7a5e9829dbe6aba60e77e355bb08fa7cda40225f0f31694e6aec5d622bcb9a6ef362997f0d01df8a18bec22e7cd45785377ced4850b083e6992d8bf2f9924f6f0554c12bbf82793c4dd4f17c6303ba60bf6edbb4962ef3e9c247692065ac2ed032d38042de1cd7d014dbf5911f010dec080a6590237729cbdd8b08c74ba30c92506343dde9987c5e9f3905e6271f0799e67a66d48dff4f3e020a16d587cf662439a86f09e345db0932c2fe14d2569e025bcd1d4f477c3509a615e80a6b79bf440f4ce8e738cc638312caba40ccfea0be36f34943dfc78867130e6fc1b47fb6ec0fb2fbce1587620bf375b7cb811c8fe7cf36f8b09635dc6b3ec26379d5fef4e77cb74e395096f5090e9e9e61e88de74f3d203d1bb730fcf497ae3ac2fc20bf4026dbdbc40bbaa30809a2ed8618437757bfebd26459525b305fb8b3096fd45f895f0d89744ba61c8817dbbce03fbe23c43405fc028814101717394181d0db98f0648800a1b0d555775315319259fce81a6227e705b77decb84b1f8259a2e350545d9c267866bcac03dd4a5ba8f75556f4484ba0011a1d448755507b922bb7ac518771be71cc63e6e678c7f03fae63649ea9b0b75d2db39324d92a626ee696d7a3d212216fcf2ce1d37cc71618eba6ddea67bb58fcf85a2923f3ff772f55ef0bd5a258d0ccd967c3bcf0c44d05084b3e2e35d5d6edd96a4235f7bad7b767bd6bf3047ad1b744a49d014ba98776c7bf30b72e2870fe60c825f681d343d100e480ff6e921e51cfd5ecdc86c01ead230f38eb706bc432ebbfeb96968802f16863364e438a423e1a88d0b459f1e89c55fd74d3efd06007ea82fedd385183dcf8a3b49eaa4e726499de5c1e9a28e9f35497a9a2e4a9d8eb79246887f27d9e123c26eb336678b6f18c3ceb3c23ff24d50235b31de1debba3a57e7d26e4fcf9cdc04a79ea4f5fca7a99aa475d2b326693ddb0c3edd194f7273eaf81bcb10ececd64b5bf60d34457171a73dc73f5af4ef5a22452ae909edea1e10f9d326bb2026b2bc07e40555b2057a56d223e1ec527768bfeeef6766810eb29b0c0fc803f280807ec8a9ca3bd3c53361a45727472cb32c5ee6246cd2cb4c845dc7695d322a4446319e2bc0d65615e812fbf880584665d05d7109280cfb500b63f18a9db0abe3255018cb3822e2af3b229247447216b3cfa0016a0bc319d2f9694f95ab742c7f65454537c7ba66959c9d63b74ec39bedeb72edb6bb7ddd66f7f6f56e9aebb534a8a535eaa550d0ddbdf7fac67db3ce0ff44f06f49e6705b782f48fe5a9cd8ab7424141b636344462f9694312bdd00341d0ab3c70e919ddeee6786b6def414b64893e2ad20103f1ecd2dbc2ae40958ca2b5b9512caa53a86229821b24fcc6b912c6b63cd39df59cb1c5750c73fcfd6e7e01593ef4a1d1807e7ef06579d006dbfaa25fe63eab6373ac2ba76767e7b713dc9cf1e6799d5328e8cf3f1990e53dfc3ca47fe4d7229e4a44646756663d74ee56920bdd726e1d0c2d67bfce3d6feb36ec395f7c3796ef368ee5390ea5a394e4a1e821c9bdf0066fd1bf6fbea8a2b3fc6633eb2b0c49a317c6c4f156321c27cb678bef38c39bbb2fcb111cddbe63ad0d415367f93961bc16f85541977987e5b18dc78d35c8baaa1d79587c863c15a80e5997101355087b405e14f50b6798c3a240832018d2802c7f1d4409c350a43b1b8582f6fc9301b2bcb5a17f8fe0c659bf9837662434f59aba79d7e56d148b5a612c8727a2288e66bd8e12e6b07be3c189a64d9aeb180c780412431cbfef28169d40c7ecc85a51cc6eafb55103ce4943da78636beba6966e704e1abd7b54da35cb63da79dea1150aa0fa53e996afdda33dbd54cdbc0491f80a6e611e06f280ac4aa5b585d9d1ed1ade90f658b91bd2bedbcd3bb33ca79aaecffdc34037bfefe36d37f998a481f086d47dc521e0c21bdc3ddde44f1b96cf4b747967a7ab74debd5d6b034e3a69ae2637c741c26d8674d4a7b782b6637dbcdcf6f4190bcb3bd6557df0943122f281df394ca5c1384cc8537bb88d9f87f45c755876786a4cce7504fbe96cc11e2303c684d865b62aabb3a2cdcae43d22f2855dfdc5c5d130c017217618787fcf8193dc46baf9d89bdfe8dd51183042ecafbf1bc884524bed9d01424552452abfaa1d4192c4446682e3f26291f2471851447164544685bc8474361bff47e8c305b8fdb6539e012a4f010854b28edf23c44025abbf0fdefa1be71e008ef2391c7f0c979f9b885f1da9ae4a84cbac6a2f646a78668490090120830387ccf8d65f3c0d87f1369c88ffe1afaf71d86f5f79ec336fe32c7fe2347fc3657c0f3ee36bf8cbc75ce5b3a73ccbb7c75be6797af470ae47ac87a23ab812ae53936292f5bd7429c9191b57b52574a932b4ab972a45548a4c9ad932d2c3a60e45bbc695701d296a3ffad81a51466e1c6dfca8f1abdaea08de4e58dbea88cb52ade230c2d8e89f09b8fddd33df1a9a2dd5df372c8c80465e7de55b3daf6fe5046917cc3c12c9c5852483f416a4973021e24d885497c9e5e557b515ccaf66fcaae8c5af924c98cc66557e7504c60b70c6932ee379709a6f71fc317ce65d38ec5bf8eb593811af0187f12e7ff12b7c7bbb659ec787ab3c4f0f1fe29d03ef45bcf75857f50dbcd378669c8d0fe77c784a0f8f19c26188f07f20dc7e197986701f798870940da8be54862a16e64b050d93dc822c4c65665d30612a534c5755a959575529aa6455a9c324ab0b49baab54415b505952b44b0f12dad53f1fdfe2e15b2ddf32d20a42c3507d556ff1add96ca9cee25bb299ca14350b9b53a50ea0575729025d5c48d25da5d27aaacc9ad8a1972545fb47036f0285e8a5ffaab63ab232b674585688788ec510afd25ac7c256b5554d6755d359d57476f5d78aa8c793afd09d07980bb83ddd780606c4ab2329bf228a318eabdaaad6e262a6858b5cc1b2a2923233aa6652a8929993696666f3ffb9fde76efd44f91396dbccb2265f9ae498ec98f4b4d83033d2da41ab0826329322ad179861451405095260832d044511446445b4aaad6a50fcfcace82065a53d90428675645506958cfd0dae079b1c1a356cd4ec9e174ce8c68719326862784536f330b3635db30903805fb7399326c6afd7a1f992f1eb63be5ef8f51f1396c36fc8c406c3c5af7313860316f3ebaaf97ae1388ec3f13843388f3d8c23c1d9dc702ea7a7871c9b9cdd53d363a3a7460f8d9e220bc333ebaa76c658b65432c652a569c652c7188b388e87b1d4e5f7ed949808d0ae9b4f9585b4a892453486ed9489b1f96ea28c26abaff234741a9ac944267a4ab167eba25d3d0bb55aa5aad4accf871b7bd3d98e26eba14d5a2df7fa54506aa79295450b162c5a7849b6f0d25bb4f052b7f09273712a3a11cdd3108ea7b41eebea99295a6ac857f48a5ed12b7a45af9068ddf5f1bc0f0c3beffbc050eca93c7567b55aad5624900f2e97eb64e403c1501c7924992ed2412c2c28140a855a31646414fb0986a138d2af3fc1de44848384b6b9e138bcee549fba5377fec7ba80acabb6b478cceb224da481c694714c494949511185c422de890d53a67aec4f307faacd96ea377e4e98f224c53e0dd54e43b52449f48a5ed12b7a45afe8d3108d761aea6ce74293c96838b24a6a920eaa648d79d28c24d3422b60b1bc9e9c9c9c9ca08835f18828ceac2ba792d5394e6b95ca7776cbcb5a23095957f59b27055957e53827d17cc2c6eed5b93c4d42e78ea2ce3ec9ac10f6ec14b960abb075b55aad56263321e1cbe3e0501aa5511aa5515a46d341b2ce76313a93f9d30cd3529e725a0ad3525eea9253e924013ac9c7ba7e2a29a30df5c040ed878f11e8c2b9dcf898c3f0dcf01c4f6aa2de7831926834929c39852f2f3aec8661613c0d633cf85792aa916830cf1373ee79706c3ea79dd9a2634d44255369e2d92ebd9e64258ee3532e9faa55529489b34aca52495249eaab4cd56157af9eaac34976a24d1a8b72ed28140a85f292dcda57a24c078932dba56aa9daae5e620239181700fca9c7ba708c224e75d598d65a6b6a6493fcc59772fef372f321d1483e9594194f325a17d3d164349a905d3d15e6606d71564998e7c1e12f2fca2a59fdc6cf381b1cbef9783e75f3d9d571c82c8c44a3c968329f58890aa5820a9a7a0ae49e27161b494db88c241a4dc51237bff1d3a6fbb97908632d5f62257675175f7a8bd3d06908d7a02409edea2b4419890495884a8a28ab7478120dbb60576f7d491a9a484e64a4144c232b12adac74d82589866497245a2db24b128d48c969a82464573791686575c32e492589568988a2ec9e764922d1ee2e45990f8da6da82d8d40174e19b2ab45a5be9ce20d8284df69fbb4e2b829aaf575aede975da3a3ac0e9a456a45eb1b5155f8ce98b7398439e1782f085a11dec6067470925401f7feccb5dbfd6b7ed5e93cf7978cef9e739c771e58f7d1d8f18e8017ab45ee1da7967c6396d1ae6acb3524a079f9a4f6d7f541445277c4a3e4e94944a3e4e9494f80c4be306acb5254d501927a75fc9cb999dbaa27f2f3b7572e2746ac2099fdaa9c48992e7c692a2504d388172ea8342d5504e94949438e1dc567a8aa3b840168afa537a62979c36317a497d7699f2ce4b282f7989bd3496d46753b77a2cb5934625d7f665976676e9e4a5120fffeeb2a486a19b48a4702c5f769912474ff4bb49238f91934ad358be6c8a1a7f543cfea85be34d9d3a41658c48de8d3c44c79bd49568d1a913d4a5c479e92d9747ae45afb11f90bb79888e7d863844c7fa474eb5c9691823b9fe51a964f29aaa6725450f1f7cb092a28beef1a8bca4f249eac8ce55cf5557e7e2971a3b494fcfbbbf4ef8ec9a8aaa2316e80fdcdb87b770c7ac241ef3f5ce62ecdedfeac23ec31d3e36f6fce3fab8ba02012cf9ac511a55d57a1cc771deab3409eec7e23c3439126e53f2f03e4b754f43422ffd10ce3fce491b69e436bce15c8fa32fc54087612c04b990bbe1461bce3ac5d849c5e2c6eb8531effbfce67d6b3ea2fb39496e9c7387aeb848087d8252aa027ac45812c70dd85d3af90c4b4e72f16ef1fb44f1a928522abaf8b40c4263b4f4df9f4e6e9dce4e27a727aff48953e94bba4b4ec3d857a29f53306ba761cce4f9a98f9ca7c4b5d7d4918f35959cf466e729f151899b9e7375f433deb949589d2a83313b9d511973d3910aa9a49091d36fa4634985e0d05ea2249283632d7b9470a0f5d803dcda4739787c7eb71e799874c94b6379f1ce4e55406590c691f3f81c6f3d1acb6f3452155017cfe9db9c73a679e692c719127a8db5eefddcdd8641aa0221da54c319d8c094021de5252a2f2139c7691578f29ce4dc7bfe7df9637fda479f0f4136e7240e04d9e2d881d6e6469390f3907371e4464e1ccb1f24277d3af41190d6f676909c1b45936ff421c8f64613ef73ef73911b71ceb90ebffc7c822e824efa924472edd6f3481ec88d3c4017471e2ae7bc07b847ad52a54ea89292d2c94ba792f64f9f4aa512c9f55722b93e954aa4afc471a6d2683299b4d65aeb6c0312c93fd2e8cb1f2a3cbc2afe25fa486bd0475ff60037f56d1b4b2fa4a10f4142f14b3efacf8a9f8b9f8ffc13bda435e9eb9e24e7a17de424ed2307c5b107f8a307b881d0541559801d888f6e9b3ab7e91c6dfbfa388360da81ebd3edad4cd07a816f64f124adcf10ec4a8e66b5b4896a71919e4e9dd620fbb4989bd6656b9d7d7797e1867868ce3aabc3e874dd1c3ad0d4ab4a6dc33757e839b4bfd19e5ea98cecab71d639b4adc576889a215bebf855db5b4a029a1e73685b9c73a02de72525c1aeeedc1741b3c51baea10ecf0b41f00247c41c9a62747daa94decf4d311870904e099baea81d3b9f13e8e9259e6dea375b6cafbd76f472365a87e424320ef20d68a6dd32cef8daed35b05f2b1502ad4fe8594fa71315eeacb556fbaaae02ce418ecb15cba6fe71c2ef19545f32946dadb5d6da0ba46da5db6dc23755b60be2cce2f49c1bbeb60d1a63d5377a5b57de58f6938ab56d75abd9b1e711048283a0af4ea53caec5d76e790329ada91cced9e71d7a802bd0d5379f143b75ae6e1b0778f4e841fd0e51a1bab3ad4a16b050db74d816284710dac6a87d7dd2a16e230522b797947372d25cb745d0dd0105f1d965f53cb1b39c9b3426379c873850fb3a47a3089a2d163b5763b3df61979c898eec9c73230fecd9e7def288471ecfe71eeb0ead43fb82e0b723413f3f5b8ef93e6f510ebbbacc25d2f409ea437d42eb2fbe9cc7395d398f34e36ce30beb30c21c77d77e72a61b110cebafef2deda1edbab3f3b20ee3f0cfcf48465d544f735233d0bf592745c786ee4847d421996959579b2d3f23d94f097675d84c18b33ef3f1a47c7b1e957b29a72aef547e6922122ae3fe5097ea594462840feb82035187bf23ea8e24b93f5446c7069c4e8a7c71601fc4e0b61c23c6fc44ec2d2f192606857a0c40e00d433132209d42a8b68539ad30a110e307a6e69a30fc03bb35ca000408ed5a53d53b9a609cd2806dc7d972bdab75b5293aa2cd86e9aa38ba5a4d13a092d585e88ecc168f05d355bb36784f4c8184da90211fa872a067e09d4065872e0eb52534f6bc7d3239335957eb6a5d8d5a7cefad8d5a706b2321d485fef7e207007830ba6d1fcf6e92d7f38793bcbe3d6992d76f1be66a5faf25d32584ca98d365f3cd85ec3a3ae015de3807bb51f3333313c630ed8e203b0c7431143378c64c38f339861d34f5123fbe350bc33f62918c108788a492554ce2b091ccbe98442c129188486416767faaab7aa34c5ba711c64424f6c5aa52c9ea4df8d068740d5a44221689495657c5b5aec2da4b13be5c192c19a14af576c66946ddf879508e725da711de703b6328806ad09a06bd46c8ad41dbf03ace06f61bf3428fb41882fad35ec7e92d6bacb5d6fa5a5da98c889de616cf3955da832cd0d9260533b881ceb6cd16f9402bf485382c111e7ee52e6fe22f9f1de6b1bb9e735ba4be2a1a264949305f76e84685d95a2322b337e7c8a461e1d67f54580cb7eea3be36d2cb2985156edd457d8dafd68affd599718b5271ebaafa4a71eb7bca20dd8e29ff526f8bf040a72c91d116a9e4e30c6199799c133771156be528f72c1a98d8f57581306dcdc22eedea58d825dd552aad69b55d75dee5857b977f15c4e172fc30fee57f719377f1d5f3e0b4fd5d22c0bcfa077b9c217609989becce0ecc744ad9f2eb2d8dc7f6d81efb630aad8ff539b14a1c585333845de3286f93b03a3a791bb24126f02453aef225934c79caab9864aa9bae149d648a34c9d4c924535e5325e1cd6f947f1f8ac5a5502947853115fea5c220c8202727a5128120643653a918f5581c2b7b1546d020cf91111359579d5969fd2a49caa3bcf52dbee255f8f82d5ce555ee9f729477e1acb7beb13ccfcb57fe22f95b345bb85b875dfdd221857facab7acbb872e1e22b123b562222ac635d2a1ee7549248f537c9635989d792bb44241f38697d5bb168a7139611dda2c745982ed5671eef5857a531d193c41176c7ba44e212a61ea0b440aa51bc355146ea6bf3282a0964838276755799b94810504e5a2c234343f548ad7d0e523dba87b115fea176308b7cc57889ea4eb529256632d7be454720b2f138367cc6440735fce47966c699dbd4b0e15f0d1596ca0a6b5301d2a091031a2a3366243163858583cdbac1ce282ab0b1e658577dcdf88a90a7ceea4ea585b5c7063364910b0911914b080eca904103192d1ea4a1f119d0b8c03f28e0c63d4d4297fc7b30061be5e73f5e796c64687c4c0407fdcc8c584885c452ecea980dd3a5f2bcfcd24824da36bd9ed220eb9aa12c964ae54e922f2f3ce8188126a1b57f2be0c6f23c33afa0d12daaaeeae35f24b270afc03f97c805cf5e62660c0c8cca0ba20002ca76b32f30a17f5466cac4c4ace440f1f30313f362be90910963db46b794ad24774a9e09b273cb78e3b694bf482a5954491c2ae315428c0c8c09e3c58b6f05b8a53c8ee26745c6b944330bc92bc82bc8b2241967d2704fc8bc78cd17fe8101238c61287014d88ef8e773812cd8f9f10f50145054b28949a3e55c6bb82f511166c65b34d47530421cdd4e19f14c932ba04cbc684f0aa80c6021cda13934099d2582309b8981128315e37381fe73c77894ccf3729a4bf4030d02788498791a2668421d76b3c82c324d18db64c8a049c480dcbbfc041b035d24d5553d65bc4438c9aefe2ab3905dbd4e0c056bd2e01b355886769f11c6b2aba8b8cf30a6e235f5830171b6a3fc0cd63833c39d7076b3fe17c98c4c03ff6ce4cfbde2795ecef23c305f799e1947790c452531112c04c3c797733fc30363c4402bd6887f7cc441462a0c46914ad619a72d98cd696d7db739d86bebb23cafffc13f9708ffd00871b8ec5b34e42524c9172f4af0a2868c8c6312c8d88889717c91c4f8cae416b1c64b74772b852a39995e2a37f9b955b057d655292f8ca1300e6ea2085cc347b010eb87b5cb4b4481e02a492d112b648b709ba5331d9b14cc40446778e3bc5b7489be4bf4e5519097fa80167c53a55c91edba4d4ab7b14651c901fcbb0fef7ef32d07c0739ee3513cc663af447a70a0d3810d14c1812196982f1daf01d09cf498f121c70d0062b076ca2034c59115b77e7f260dccedcb2d0909ceb66ec47cc170eb474c18116e4b7a704060544989c5f40033e345109128a2c86325d243c6715a9cc75ede7b70713f91e13fb88de2037816ffc16d5c857a7675924e7d6d4095c4c17c6d5898e48e856d41659830ce05d355b999751da92f2e09d214f3c52931c9eac358ba9039c692f49bb17400907662907682483bdc1224249f500f8e97bdf9f759f07f001483df067e18fc403a852e5d27e52fe02a8f017f790b78cc57c05d9e020ef313f0192f016ff170d8974ec487c0617c06fcc5478046074f0f1e222c40819a142e9b47998110ac6a17f8d5110bfc8aa802bf4242815f154d400211a0b365f350dc32115f7b78ea4edda96158694cd06464c0ed79c879b3200e088c2a29317f9a647698674d32fb8cf7d992f524b36a9239b36e7eb3fc0b350eccd261ecc43f910508e6916b87e1a65d88c82d8adc8b8617f11ef00d3c10ef400fb121fe032b50b00bd1c8d21811c556498cf000901dbb4161fc20fcc5d730a2075ff91f8af81faee359cef2d669fec4657c8e01f1ed5970cc5b28580c626834eac9d19e472269adb5d6a5201322261a3b566343ec61553bb1accf09bdfd0ad12754539fd027643f21fb09d96f55db3e05f6520266c68bd40ad531ae0a632ffe513dc3671883f14f057edb8f3be569125aa432e3458511524d5ec6552da7e6d424c2aaa3c3623d9296cbac6ba846d42aaa24cc8c172dda6c7982668d941552222213d07d8524c461bfb52a0a71b8f6cbb8223a829e953c01537e01aef20cf0975780c77c02dce511e0307f009ff106f0992780affc031cf60e70221e040ee31be02fbe000a40c003be4573c0b776500d78132110bc4991140316b0225a215915ad92c80af026384ec804783a5bb27b1843f1eff1eb51f6f62828631904b7c21877e2ac8d7ffe0a06c80f20e30f1f431efa635d3f5428a892b44687546c3c521f1e4a53f140d5ce9b0a0bdc1eb572026e5fb2594a8a4a0ff22015f0c5f7e0307edb38ec6ff8caffe0332f848e1780b3fc0ea7f900b88c0fc267d070976779cc89ab7c8da7bc0d1fe23de01c78237c7bd3092b881d352936f01f70204f6d3af0948756263ef0b487ee30b133691581f0188ef239fc7df0ff0138909f364018f19ee302d81180208478d9349b5d3b4284a46895446682b3aabdf898c3789b1b0efb1f7ce5adcf3c10aee37738cbf7e0341f84cbf8139ff1341ce66bb88b0d7f7996abfcf694aff1ed4b7ee2b8879a14de42c15c6d9bac6a2ba255916c16e2c0db715c16c4b792b48a8880f856ed876fedcc161dcf5b28f8b48d254542952c92da352c1b398989cc04e769e3b88234da979d924282e30720881d3ef34200f1c38d771c00217604010449baab545a6f9c625dbc45188bf116618ebb5ffc9b2dc091bbd4a4f88c83a2a0e0f984a6f9169216918e123a52d454662a339599ca0c06115c8b1796af84b1ec2b610e16e0f842f88bafe130dec689f80ff8eb8170d8c77ce583f099dfe1453c10dfc0fff00e7c0fd7f127cef22ca7f9ec32debaccf7e0dc0360c6db7098af7197e73ce6b7bf3c0d1fe28d700ebc077c7b71d3707f96eb14d5e1871befd883546642282d4711a24891318c61d2ccd6501040b8ce0fa3ebdc185de747d7f1118fdb68f3011a9be857487e5554c924b11ef0c66dab16caafd8305d8eb07e4574f22b2446fcaac8036f2203f226b31f6f92d3e34d7456ac96e12d618cf39630c7dd32e1f6961350665036abc2cb92a295142b36ac8ec8f815110d8b8e21de44663233c931d1e9c09becb4c88cab1a48f3ad243ade71be559b2d756c1159579531b68a80709b65745925552ad7992d9507da13be956fd15a3ba79212ef743a9d4e27545b0b1b2e4e504e4e50505050ba1a2c2c8d56abd56ab54a7452b5129dbcc48e4a85e8b3f231f1619166618b27fde4a050a9542a9552d178d1c7ba54a8f03ccff3bcd5fe524ecd976635939ac99c30139f2e3cb89e2651c9ea3a2a747215c016365c984ca7d3e9742a6151e3a9cc88158da739ab90ceb6ff5cfce9a792556b4df2120b91583c29c96ca9ceed947088f92900811e18a8fdf0e17a06dcf8810042b058a2c2708654e776b8ecadf0c63a972d4a18db4eac6d85377674419233adf0e54587dd2d2dad3046fae7aa9d1ace345ad870018620188661283a0d576e42c5049f536dd264afdec81b79236fe48d3c3b9266a24f56edf875d9c8343ccff3bc8f858bf1e4d3a2e4c7d1b278f189d9529ddbb1b01a5e1d0b95a311f7c05535657baa42b2cf4363241d21d572126a2c73736418c32b58565452c6f716ca09cb64a5429542959c4c25921e95e080542b43af5604ae1ecaa0ea24b17599a683ee04f5cc4cdb34e80941a3c94db9596b7118dbb6bcd9bc6dd6da2025c7711cceb876ad8c2a80367c53a5b47706fbe25b6f252da594528adb706b507dd97a6d0e0edda9e40a726691d784131506187ab69d8d081b2d43cedde8cf467f72cfddb937efdc9b77eee5f1f0cdf8be982f2f560db21e0f2b210c7bfa159a34d7679e913f9e8f07e4616192d5bf206f080bf3e5a161dbc2981038a07b5e41d0bd200bf36a5e51253d34cc9757879a174451305d2e9830d5bd9a57545f54e6d561bea80926599b54a6a432a034a830aabf58d857033aeb5cd07d4a749f6c7633ccc92e95814e5d3061a057a72800f346c1cfdb5e94b02b45d954466554866f7e54845d6bbde3286b1957bb29dc22a27c87cd966108ffec8a31ae1e900b66a2fa720c873b6e5cc8337313c4406dce59e573afb5f7de7befbd4fcc28ecad17df99207cd4c2bc3a149cb5f77e37dfbb85bd6d26583c7e325ade72eeb6e59c73bed7d6ba6d9df781a1c86ddbb68921f8791d37da9e25e8d232b1e16bb1bd9636b79c849f9f273cc1094ec839df9b2f38da4637bba71d09b719b9e771dc10cf473f24fb0d1d671741bfe14d2ec540679fdbf32f8c79de0d6ff27867c57e3b2e77b9bb77cbf8861527613ab7e74867cbbdf65eaa8137bc014fd2daeab5cee9b40e413443eec64729b5b43ab6d4a9754ac780297da1e34b0c4f9acdadf5eb7534dc8b0dd27c8abf49ab98e7bef525867fd21ac45f87f3966d187bb130d5d7e9823d7fa09e2dd85516469a2eec1473fee56d04b7d2f994d2a75b0d2c7697baa913a3d3db1f8d55f5e3b073396fde03bdd51766593bf6b03863217c53a5c42a55ceb8b6a8635ae9a5597ce76cc10e45adf5ab5bb8486aad352b61669f99933067ad60c84e6424e426ecdd18e3cbc3e26b71deac1d92dd3ab67eb175ebd8ded891aee13ab56ef1d437e89b2ab44da7ff50b93a22f9f419359c109c564d275fe80b89e43535ebe89242b1a914a02829f9e9c4cd1f8d72aa53275fad1c643977e29e8bde72947794cd39a7f4f1e98f4c9c23a88cceb20f1db8fb7c1b4b931da0739bd3b134f9c61b706e1f9b7ae72a4a377034f9c69b3cce4a724e1d775e123d8bbe7db96d8e1d95b3e89be7b18403bd8d3f2eca270fd1f3c8e39dfb71f736feb85bc4efa5d3fb89a75a2828d4536349f7091027ee7d275f8e60dfd0f7bccd71e751df3ce5756399f24e94e58d2c4fb19c7bd6977aec54a71ca71cbb0936713af200e968727372eaa70df2007db5e3e42b5fad560eba2e7d6c9ac7cec7e7e8f83516ff8fbb3d107a53f7e734d471f295cff0e6f3d55882fe957587cdf43beadd865d857fcf0374130d731e79807e723cfeb81b1c5726ab9f5b45c95525f7c2580a7553b24ffe6d5b182bf193d7d492a7464c5fd26da247eaabb4dbe49c976a272fa9116ae444b44d7a9b3ec8a644164683689091faaad557e9c838931c512a79475492937d1d48a18882ca8ea04b2a851d545f4484a400723ca3eb9193d9914b7124dc66f4cdc191e2501997f243361f7f08c6d843f7c6f97d29069b6f1dde301e4f1bb6f8888cf1c867181b8d26379d73de8d2c20775f4a44a331d0477065b779e71ff7476070ee0c52a2fa2a3a815ba638f565c4068fd820f8754f659b9733498e232a59c5d0a9839ff8dd701efad78537174f12f4ae0b75d82238100c39ce7f705bf43a0211f42f8c851c47c3b10cd2028e338c7de3dd4ef788faba7753597d755e9de2749ddb178e8658a5865ec935b8a864f5ee535925695039939433c93c839679f526464357e8065da07b84fb83ea42710333e7e94bebf0b7f90fbdb5771e5a0a4349e16792aa6df342934e513901eda3702e11f374dddc02ed9fb667da5e6973f77a23689f94730a58da2a56f12679398e6329a1a6eb3aca745d1e8a360b9197bad0c31ec69b8771f6c08fca0011335bae73dec5f8f77d2468ffdb20c7a532b0add3166e9217ac607873b72dcd96eb52a262ba5cffde64b65c1605b6e53c1187e18dc719917318de5c16a0fd0e55eae7a68b074e97ebe18b221631de448cb3187e09b59aae3b4ed77516fb6e6ebabc385dae83afb75178e35d97eba3f0a68e5c78ba05b43c2807c3ad7b1ed5387bb1418d75ba6e89c443a742a150a77dafab50a51211e18dcbe701c9d914dedc6ddfe213cc4784cd6079529ebb9a274b79a8f22d3522d9d5672f36aab1435197eaf70ba9921476162f6f2391854c56e24c7a2bc9240fec427184f2296424e4d26eed5e9a8884bae04ba9177e5713bb24b85e8c71b771c8ed1c76bfd5beed7a3f9a265a0f95717faebd10187d57d42109bf23c25b077a39ef922d894d47e72eb143a78bb5de0c47e681244062822e09974b8ff7b5db74b958771deec2d829bb577abbbd0c96be6e3a8bf8b139cab161ba48315bf2d795906ecfa5f5e06c043dc3c9a07782ae0805327c37e9e73eade8ae7f2c54dbed3e505b2e447998249ab29872cb85a83f4d9775ef55266959d36549e7eeedec95c1cdc9c1567baad46889accbd66cedcaac2d2aba93dc406dff34c9cdb9574d72f3f05993dc20303b0f981e6f212859a7a207113ba223de1572677796afe6ec8fd6fedd2ec9d7157535d54f9b5c048dfaaed611d1b810db6b7dbadc7c7fda1041774788ee1036d2d5d5ba5a579ba1f00faec284a954e3fd29311876f5126b61d7715ad7cf9787dc9ffbc3c4fd1182a6dd9f1b050abb346a61282776f56b040a438d22129988e42e41441204110976170b2e639445bd0d02021a12a2b62b8fd89b53b2cbdd86b79cbb2de74cebde32c66e59ac8054e3152e0a050de69c6b2af63ccfd3da3f17307c9e949852f987b23d3bd6b5637d6895a4855e53bff0d35a77313d4ead7b26e71c776b1cfee1fcc3dfdadf5b282a59edcf143fb4fb6cb0ab776ed8d5bb5a258f10e10c04cf83781d5ec93d1a1c37ec54a35ec4977e18c39fab450b88fad0ce64d625b33a36c7bf857ad08605fe69318b162d5f6d68a88888a8e537965d803c68ce73f0537d05faf911b2aea04a06814e690ba8c1cfebb88db3d9516e760151df6d2f20eab9ebf75e8eb2bedfdbbed64ef2ba4b7873f7c8bf971953c5cf6b2afecf51bfd9d8d1f8b9a03edb7cd87616e5d8a7fdfac49dc24e183bce30764f4018d05641a31edb84cf837271acdd87471e944e7d83deb66dc326d020185e16681286e2b6e530f46f0644bdb5f92c75911b63e19879685b974751a70b575677fec140d4579bef37300259772bac3bf28395b65dcde2188681e98a8bd942c2bbdabb37bcb5f0b0a13010feb94736c73f2a5595412da7db320f2e5b56cd147544b3d6d56adb771b63ec3141942ad76d37effc78765ded72fefb7ad9116deed66e7c778ef78755bfef8dbece4d1bd46328aaab7a877faa903ac37e77e7f2658b519c2a85834439e09f4b13917cdf77fe72efd3a297979087123db7edf3a81cd3211838680a93ad082a4c0c08135a8fd98002a821a8942b590ba3d62944230000004000b315000018100a860463d1701ec6c1207314001379a05e624e9989c32c865190630821630808000000080060208900e8b725a57873c4c4886bba342115882659c47e2b26a328d7d90b29f989ea108343f931cc501291af150cc0c84cab2d261f704554a2178a54fd7e6450140962b6de94822266484574c29de4b54480c6cbf0e73d33a791577467d362d920daf114c7ef3fa78da125a60ca46e1a4c57e43d4fda75c79fe1cfea9bea52d8c117e5d142c77aae7d026088bc4109c4a7ed98906156a9e2639f277f26c46cf7d1a19f2055c6c085b24567c944c082ce016ebfa44b8c5253343219db62ae0163f266c872a9c604e976438f2217dec424588e854591cfd1f44739a119ac64f431a2155477a0531272dbc545df989dbe411363f714506a0d7c82fa8a20379c560ba40488dd75d4be622095224eb3cdfedda39b5b3ed5d6afdaf4d7fddbfe48df74384a8575391e45dfbe2a03dcad81633d56268749e45097a624608238a38acbedd018c75af687007e98da47bf520528d1ab77859c7106b2cd60433657f7dffdfe8dc60dbd87bce63ef0e673ff0dc8eeeb9951dbefadd96a0aad0c1bb77da05a3f824cb56f9bc90f1889c032a94460e42a1d1a982ac5908b9f47c1e4c756db4fce4f3836a4ce87dcbff9d1b14d7892f5a74a9eb918caae06394fd349eed36faa10a8ed24d39aa393bf494442e59a977518c51f388cb9a24b651f97c1640a350d010aa9a6492387ea82b1323fa9f7c35dc078e626e446391cc683c9d7af195c572d1f9e5af696c4f1f1551e867e7a21fe65eb252601462fecee1b50e642949626c2a1d5660a1db01ac264a973449d82f05a3e6c8ce03abd5af8e526cefedd9ad7fdb8d9df673d472b25c69ebf29fed3ba3781cf7d8c40df072f250cb7fd60eddfdf332817ca15bc7729ac0a0f112466852da5ceda9f42a206a6a6920b34d84a603558e94ab777b1ed4561f24e3eee2ced7b344c5e6dcf67ac09c415dc4bbf9ff7d643ca392ca1eea1bfb4d73c09f0b69d8d8e31c327715806fe6d4761d9de8e88184486ac1d8288190eeb5f808655ec1b9a8c8891823601ee9dcba51a7bac525351ccfacded5d3ba044bc884dd63a07064a9042e08ffbe120db37571263f2ba370346779846648b6d146e4d5f1d0e57ea691012bdee728d341c93bd3181610096439fd0b37eff99f8fd375170f9f38bc93e50a43a8ed9f6a18e7d0c807d659140b5a92350ae4d4ac623d1bf455c21c00ff8d402bccb8b3398ed73a01d1d394995e20b3ef5309a26ab35dfc8a8cd4cb702374b517c2fb039bffbe8a2a4f405074c8d750ac7a3e2fa796b9fe9a68ac7625dd503be4aab37ad50e322a2b73cd7cbd58c1401c6fdcf5f7a6c5f726ca919be670036d1f66dec59a9434f8b1155b2705713cfad4a0cef9a2cb6c6472808604a160fcd94367acfe2d6f55e8f6b834e08af15f53a11d690065099573266538a59ac73c7d7807f5b0af3404a06794c9fafbf7aa088ca749e11a04b8d6853c8643afe54f32eebb0869440a026cc26de8f856009fd50b50236557be33031b59d8e49d18b3d580d3fc5116aa1c61ee32746c62adddafeaa23f177f82e0bb99c35149a977257481801974a83f98b3a1a036c9101144404c11a51b8e0ce0bfb80e043bae5eec5df57235aa3bc768cced4c9f3d7fe124f498f0d7b3985aed42208c6ba66827d35cc08f081c5beb8584d76ca8e8cb4264787798bfc3b53873da0183c624d4047d1594158c39a39f3996457f98c7b5a566b1faca1853d3e1f7ff66d69502687f50b9eb7869f3802e0058d7d1fe258dc2973192f9cca9e7ff3653917a3e6ca36daa96a29a12572ba24d5ab9cf1c61209d41cb3300d4ca81c64c2d4c2b401803adbd45ecec2672043c7f536291db4182cbc3fdb2067c08ce179541914213038ebdb484225d8b568e0a6cf5d48fc628ced01e93680a94fb664d1228af2a11a8b5271362fe7dbe5496bc76b143c014aa9098210866ab7923f594b7b5ed1f8b0871887940ffd9bb523d11425867ca610f0ddc1cbc04ed5ab713e941c16df325c07ec8833d5fec2e2b2a3c052946015551b5e728b39a8582b7c58ce4827b28ec0e74773f628a0882b441fba320cce17cb0863bed03f599c5fc628da6e19ea023347d4095d0991f8c39d7542b907a5af95047d33bb64cffb1394c361512ab3c7afcdbdb0045db74c34cf6c6d6c9f2d6eee6db8ef9e5927838dd33944bec08f473bbff8c15cf6ebc6779867907cf09c1952b5690c2cfa39e0bf6fd64b5b4084cd55096bf8e202d5e86ec0364cd451821906ac6b6e3bb2970e36f51a35109d57207d5ac26d30def55e51569d6366ed4cbcd12501af1c4d2e88d029114f8b3fa3ee0ec8486b549440a417a0679bbb4e9864f1a1164bf41a55759ed051c7f2d8c1d18f65531232b2e960b4d29ca12cee7431980fd898cd85ccc8dbb76e68000494c50395c416176f32e3db3eb8f964fb2f9ba26bfeb4c6de0beb7f82c025a0e855275bf00eea2f88b6f5d9b4c5ac4eac83513411c403841a78c79fb4837be2ea447e7b5f50245690bb9eda8d672392d46f387b0551fc1c3782283e073748aa6c51da5f6acb29bf04670e3010dba9a69dd27a2924db0d6836731f71fd9f9e420e8aa9cb29362ef3bd4b93d34bb9e6b2e344a28b58347c39c02ea846162f134f1ce3583c13e18797231e1f6d4eb5b09d198fcd322a503e1afc06ae0689309953b978f564c49c1d3929441e4a447900d7f929adde0912b189a1e14094d30337ec2344b1ad6762046d325733a7b8ec3aa217581c739c39b423e591b557780e0668ceeea29af392fea211cd354765227a14b5d87954e6437ad41caf3e5e7d90bc9ba8f9c5d7057df8887aa4fe5d3432166aab4da54745b21e0583e885c7d4a3f0077b54168349503422d261e8e02a10f366148d0ab447495ece9939c0e11861f01160e112d2915fa84044e57d7491290136bb6a3b82757377a05fdecc97ab468566cba26495dab83ced143d6a2940621a4904ee5489e6f78d4776722cb5a35be41e0c64297113b1910e07c8f2843ade8ac5a25fc6ad1a1d848a9bac8ba6bb33b9f9f1d1ce17904a6bc76161825f0f20b1d4ac513184db52922bf48f44310a6e48c53c0173cd908322a9ee28ce5406b361a42b1b9527059f918dc75594ce192217fae4a59a97ea806801b89dc34bb5f92391703d842c15e8a08bbc9747ffec9b50ab698fec8edee091f00191dc07ecd1a793a0de93f7ddac2a7a252166373cef456b62158e8550f6f046eae243a9820329652f5438c09b7f28b4960de964c0ff56129499a47d38c72d566ed860612f6ed2523907e9af4b78b30a9a401ef33692e2c11ec3cc553fe2ab5ed92bbe4755c63ca0db33acecffea3b814321896b466336ca7c007cfe32362ac2cb6c5f7c39f16d00d25184847cdee79d6c606935ce239609c36c6916bbaf4c1b5e33cafd8a026db801672f92a9541b252a4a771689b03ed7b1d1a2616c888aaa45134e844c9e68ce828472610ea55a24a504430ed4bf45aba44379bb1c668a747a6ab49ce847e12a2671337032e876ae0a659d42731121b19a04a7f54875a025e18d9ccb38e93f8581e3210096176969b945f40128e4917a5c7a116ea1e9e30f4fcd181a3b5f7404d252e595ec3c8c7d5549ef024900236f369dbfb04e1fdbb0a60184c1a8be70f96f073c7bbf3af643cb859ccffae897c3f1192297a7d52bc4c85b69a91a0b48eb00ec5656f1c4fd7eeb08f8264feb4538ce220640dcbe68b620d318b12875ba58ef6772bac05b7d22a5e4f600f605725ede62df00db6494a923bf0c923191fc9a89aa8e4c0a88522f4fe44bdf35512e4fd1a7261a1f447a29d8f3dd72f5f59f68c075489543601aad20a1c7b851797b368895784e2b7c532e0c4db7d4a8242359419dac3577d804aa5f698fa18c7c7b1fc7553a197a6fa838b689f426123d1b011fe8702093248cb2d498a44acf98648d8dfc432693c8280f5a6a0e2970ed7d330984b69e0ac621f1e9a821d2ec12b502d47befa8076be5e2a7664cb03f65fda0aaabe94c20fa72b349f0fd01d619de03044a9947b64d820b8bfbae482a48827763a0c3973646c44d5e7d207662247424f5272753a807eadd47ca81ecf3b0b7d24790a36218f4a46009fe2135cf592105f84d32ba1d9f8d27a94f3c7024aac1e91a8d450389f0085cf5fe49dcbcd66ae74113dcdf6515108b206714394e586ccc594e7ac6ae9d65e6c54aee6acf332204e99983f21265fa87b042b33675c0c04b4e776cfb56d7f2cec04b6d577eb764a8608e3f5510b9654d625c71c9440c222675c1080afd09f97a542bbca80f7b4a9f9b9cf1c86fa0410ed44b7d6f6fd9fb2ed35a9f25c3541a82f9396f7170e62d52ca5baa04fd40d558374e2296520093bb6654d396c394b778d623578ee7d5cd7fa85388fc1e862abdce7e2a5cd8043e0070b464521473f6d67ebc50420ae91acfd217642be1c63cd212251d1072995538f4bc0e033ee305b2875bba2ab0307576a7db44a4fce5b2720ed0292dd9e836e0a8ed172cc026328467c13374534d4d066710b8c912f9484b964e3d6dba0512843c8b6f827dc7de454e34426f169596ccf7325a2880a6828377758d8a1b89aa27b8ca85960cc94ee20bd837e11c0b7c6fa4367866d42572e1dc17b5b7c600c8237607df58f2f12664c10ec8b2b3aaa077d2222685bcdcf2654a61fe7a91a8fdc4cdd640040122a8a435f612c00583188e2002f7a5d5417c9a72bafbc49aa413b627a036c35f9e2508c69aae2b0c44c834d5a826a6e9f9500523142095a95bb4a1254da8bfdf85d308243b4cc88f80b78b84964ce5fbb40f4f9348c6ea75f484940d455ab2ad123288a4cb9b88dfda689bf8d23778454ba672c8084dcbf8a42de34e2645327b4cc06499d79241cd13d7012f5d2e73b06a475515ecaeb93839558dfe08f6679c32c1cf8e5b6e282f01935fb72e1b961e5b329794e51777bd6c05a1449d45cf265a618525d5347e87936c0bd897c6c325140363eecb8251895f249881e2a935e3b36c66fdc8c6ec203c3ea2969c7c04a183905edf01b0cc81d82860361065e9c2e9c20bf293b013f498843de9319651a4721973e0032ac7c10c4483648786190820e6c86f9df930b67f0850e8d85586ac407444b02fa1c035964133ff9655ffdbc85b33ccb1bb5178501a8153a559ab24b4bbc15ae59e010c072e36cf62f226b370055335678cb8ce320da2715a0e9e349cb200853547e94edc267b89a6d7df86ec20eee350c6590246f8e5854901b901dc46161eafe93498c5b3f88f11a784da105f7a1990b823fd467a3e1e79f9fadd7586c9e4ef3cd18eee4827daca13d16a0db82db72d3c1463240556b0a1ad29a36531ee790e05ce7ef67c969468705777437f67794b021584418d46b1862f45ab676df15507049d2489bef261608504431294702c6b0244b6a5219df4d5aad45b1ac2e00dbc5bd743818152c88cc0f0a251ec1ecc12dd76a28ab9acce590848fcb3763079b3682a104ced29da98f098eab39629894613217a2b83bce2b3d67d3f082f00c7304a7b7f661c404bb8146021495531e0e403373ba22204d0a4bcc837dd544884b7562f1441b25693a4c0483ff0c8dd26e6cac74e0ecd26f58207b99062c8b2492f0210dacd5f976be0c429baa08a9dcb4448f4e9e74becf57eda7e45654515b4851717b6fc847753c1d65d87294c05127bc4fd4dee5a3a5ca5901bc72c057cea6f50b109f5c5edd1dfb8995b418cf8b62f56940154249ee52e928565ed18e5805549f1284945c73c05fce2959daa823cf09b7cb6281d8025e89e9f63184a280561163e58e19a226876d54e8786218e364c5399f543d9de66e30ec6096c51844b64bd25ec2173c20f1c6148546193bd5c4b612e56832978a92b112d0d2698474c810d75066bbc22466f4e1b02c64d046d4018baf3c4b97e13a1b063e1b52fc6974e04601140dbf68084c304e70bdc5c34487761d0fe60c53dd164c97abdcb42978c1c4b118ffe0d87d52538d9b3660eb1b20d3ff439a207f42c6d6944017fa42513ed0aed2fcb2833b27ac36ff105acf38afb61bd2ee55bc86f6bd01a3db7bde50cfc95b1a7c5d869f65293431ea5a4ee400c374414547caa085a1a73ea1ed58fd425c29addc1b2d0851eab7eeff217d04418cc7f8b5f04bb5a0aa8bb83604e2918e2a20b78b103f50fd23838d084894d2872c812898a61553e3bbfcc305fe32db49d5b326525c231ce687e8fbd9352aee08c5869b63a7b64028cd71aea8c692a2a866224e3466f30863a51485322853c31c89a7f8f65360fd9a38510da9139f1c3a2c3aef2c1ab292a5d11a65e48755eb6061e049f04f2dd630fdb7d658936a576f1fce6502dc1ccb0a072b4077139cc3ebd8e99ec8b951587c65aa109c46bc8b62db878913308eeeac5f8899a886e5a37c904fe2e75110e88ec9b4cb88a2c429a86c8f5587c7d5a7f10ae92b1ed45c7a6f0ac85f492a44ee508593647b8c634cd2a01fbf314b330ab9315be92a858cf041e1db8938802836f9022290361404407a77b08096ae73837712515b36dbb756e2bb8b5f5fc45460e0f0cf0dd647d998ebf5e5535c19984fd76b32d2479eaebc102c471e98bc894b4e9260615a0481941fabecc6ab72601a943fd60855b995218c3c63622ad309c387f08e8e7aea9cf3861337242c534ee50a631fa675b8e7d97f6e21e8b31a3c51728234ca7ea715c2a2dd15199b4909f6503be11d8fef899740f6942c3383465c620c2f4e463cb256104957fd80837da23185e60216a492aef66fcb237659f006b1f5bc16c85d75e9ff4b457a0b7d8052f94b6d0d7bdd4894d67f03898b9116ee67060de33a253ef247ec41c18dec12cafd763146e961f35f070418e4e958068743cfe32e1a65b3ea73a40ff5f50e05cf71240067cf81c8e532d7f7f7c93e21297ce1171cf4a83d30415dbfcfab7709c42da6fa995c2cca900425a2534a1df31408129149785e581f54addecc34c7478bc5cdeadb8b18e15670a308ab05550fafdaa00efc4ec097b5052b1bf8109f6da3bc0f051b41991a06e372ce758aff33aba382fac0eac225aba9f51a43534652766c26f18f9c9fc0971395b884eff561d9300055a637a6226cebac2a1384398970c44c792a7e7bb789b935955237041ac79b1ff6206d1be026cd70088fed4d57edc9de316d72c21011e49d6685fc68c475848587fc15960f83c0a85d2c971db7d841d87e18778522eb54e223310e40e2fd957700e8451d33a72928a188899790ee1127f6351661cf44eed6c4d6c381aba1907ee9a351b3e1595dbacceac4e2a05a976c7a85658bf7bd514c668a66850d7c94eb2b612acf5fba4763192172daed2ed52c0c6127507f40ca9b4c6c49836c04fbc48c11b14f9ed75276d3d8f76d5c404cab925f55720a6781cba17f7ef6414169151b37fe0c35960b42d101daad27c2e0db599de84e08cc6cfc505f9ccc5144a460f53e476302447cd4e585ee6646af6eb5ac6895b8cf1baab6719a12f5bc10890dff2f54ccdc2e3c97016f50a80c84ccdf8b13efc20d5ec835fbdfbc1cedfc28df6b2dacc9a47975270d555a36396e01ec6a0302c03686b26f19f9120b4a9e80bdcbddd2724227e2d54cf2d4258bc84f21e38e9148a4139d6e3ee9cd83e961c225767b410524d9565d53529354c33ed56eeb8460450158c0f1493839d6714136d190130021f03ba3aa198a352bfb23563afbd085291162fdab1a11e08df1746c1642fb0d46a196af98e3a738cf87cd6f4131638daad1a7c0313698301c1d56405710655cd1dd7c03ce908d70e92cdf3c180f36606df3180131c1ec5dd24da022f232931be2c54b8f89a786efe31effcba28620c5350e84dca343240469ca0b1191c8466921c54cec04e249bd5c204f27b8490c10867116fe5d2c2429c32daac66db2c3fc1a53cc767d32994a0f2b9e65311ea9b3aecd36b6b72f2c5e59e28614e3c5cbdb0dc235d83baf7733d400d9dbfd147f7aecbc4fe24cba699e12bbb85be18826ee672d203b20c34eb80cc10ba129f50f9ca6a1d47ee2bc1e408981599f7074da29b3dcbe111e15801bf741a724524f7a39b4d7a455fef1d3c9244f9d0cd2e45b78dc35ff430ba99d5cb034c63de22d0bae3caf8675d582516850674338f332b172c6e4291335ebf0ae256b3e1de6c6ab724fcecb7c5760e0704ceabcb30f8481b4753de0b7ba1ed5eb74e3bb6bb44eeaec0ae1579b1a371684093b05e7691862005eac25486aadf6cf4e8738a3d9faa978bc884eb37832f6d49ff1d3c008713d9d32e966cb9d1fd823d4fb754fc8b2c4c20ed0bd2efc6e10ac0bc94c4f019064839477667d664ae68cb70adbffbbc37a541559bcd5ce7f4b68f9cb142ed368930586377b4e70ee345766751978533968deb94851e5be81ff3264340e6396f9fbf6e5bc75540936abdf914dc0ea020f5b3f0d657159bdd3926df657539d03489f02d512b0f5c8f60d7998d0cb745bd7f096d5ecca93714e347851f98eb1d7c76b683da190b6fc69b485a7d794523b08dcece36d812656d6a67f21d0bf917628430a815e9000008f9c78539cc3055f80d4960b34e446e7c5037d9d4b2d930b891dc3d7bee0610299eb6cb2452233fc2963cef233547b5b4a28514bd7091d6153526efb118eb4ee0a9f4daeb9c1f1bc6858582fb92ccc8118b1567044e48d0083b1cc75e8d24cbcf687aa3afd6cbda9d6e177c4a374353a455ef5638030469de2d1ff332674aa6cfe2e5dee6b575b13ed6eb5f4c054789be7385c604dfade01ce6e040e18b5fc4488fcadb362cbefc89ad37aeb24a13b9a850f5e3b802406ebb43e0c4ccf0369584e047ab38bedc9b2c1355c1f1990251f1dc9ed662cdca6bdaad361a2f48169e008affcf8a982c1e88550bae36a51f6b8464884a61090ee78b21c5495d9621380dd25415e114693149eca6c1e5d1d79123cfa365935f6211ec4966545757c9d150fbae64c0b85887119c783856df1445ab01b149db8a3f4c3b2be75bd0708dd7c6acdc3a14449e5549e40569db5280bb35ca1de35aa2388f47205a2c03c021e95249e8fb61bdcdfc1a87412ac1642dba4b2c86ea7cfd622cb84ed302980d9ee061a925a6c54bfa047ca4929b7654f12c665e4d4a4db7852ef7322b1c4b253c0d5cc6af8a0d5a1833d0540851f35dae762a5f6e610c155e4756ee4f59c6589075b905265cab56f94ede6fffba691eeca41fc8c792e1c35a94a7a8101bbae9341c66740b8cf8a6a84fd7031ae16840c8df5d3ea64b746a55a2ef9ac9761bb2e3fa855ae0428ccc27c66a40e20b9bb5781aac7e67d9b75ce4f4a2d9f95680f9b421c563f05a3cf39eb6a1e3bb26dfd30a94eb7636820b4ea1d4873a36d370de375fe65991f4afb517ef1967d37b1aa92523f92de1e9a1fae00e810a691eb55cac8d9e2fd33f0e85db53fe4fe3348f92c0831505d3e8c82092bb9f9cba5ab0498759449be6f1a77dbbf8f163c524f36455677fd7bb04643732ebc0dd934129c50320feb4ef53ee026deb15ecbde252c89c78c479264a1f07e97ad16989bb14cc3b8b0faf7df44890a04a79e1870f4ee111787787e51c5000b10b12dd6ff12d5438072a17b66d771812bdbaeb7b7c4011f24dd2992451f42c4cdd4bb20bc3f72d08ac528068b7a1a0eac9743cbef6519223064a5deaf2789acaac87d3887db22112c3fef9d2008d14285e01ab192934551998dc1ff6be36322a93bbf53c7cda54c01b60eff77194a921bc3d0682a6d215938243469f0508a3ba0f455c7f79f58756ca948702cb9ca5b7f2dc5814e679c3fe020599e7872fa20e58db2950811a6ea29e8d27a3dfcbe0c595a69dd9f39251b3d4d94b8393b4c51d138474579af28000a84fa935851da890023b389a9091a94b8da7c13e5867f8cc69ed781aeea6339f76c88feab8c7d35e82bff19d20110a3d30c6d3e6ae50369b734e399ee6d5da1f41c4f0e5b02f49a3616df18108da3f71722c5d1ab874d9c388f5c564d8865fe03a354f93020fd62908728dd4a5ff338f7fd316a489de0b447eb6d1be547a9a82284373113febdd7f4a94fb31678e58359b1b2daa86bca7ad899da22266cf3dfd52ffdf5869930962b7d7947a8c86d211346778d8c06743b5952bfdf9e403080995ef69be8b1c7d0437f9cfc3dab4936732a9453eadd2b84455e0d45db953355a867d8fc247c52d747ad13ce7fb219fb63dfc2bf104e530047c47e1f8eec9b6e3c3a68360d67146889f96c5dd5bcaf94460bb40425681d581cafbfe5ee87cf7828917e1c758870f632df3be9b8d5479e2b1c7f20b392286c0eabc0419363bfae6647718d2ffac0c4ffbd336b8bcd79ee8bd8c8692a2d98f11a048613e97c54e1ade3a5a26b04a574bf3da0e5c335c1c6a60e3c62b0e49ce88de4f6c995c1903ce1f648d691cb0798fba49ebe16a95f0854b7f940366a2764699b84d9e878d65f10d67282fad98152559a36956ad7802f0e093a3d454d90dfe0108b41d54bc8183c66a14e3d7d486d71155c1272f2c8781d2a5525292156f6093809794ab44fa49cc087ad0c8d4fb5ad857a1daf09d6f8a0ae35a7a6e6fd28307e84ffbdbb0d6dfd205b85efa5b60804d152ed09ad54aa7576fa7c89beef36e16094d1a5324c4632c34a7fd49413084501bebd73d829abe3270b0c85c03327261314abbca0e23b6f1ba96a37db5e80575731405f9473ff0d86da4be005dd7e7283600b4c8dd48b76c62672e0663a3d8ac7e26867b150d36f086c9438d3e3e6cf9b2aeb997616a5eb013e4856c7577800395d9821f13abd075c0624491b1b07a9da32e8c4ec73680eea6a3861cb2a2c31e013ec6ff245764eac7bd588c2c2115614d8c7c344454dff32b44342202ccfd1d8a9e9cc5c99aadc2edda91d36027165c16697e1918139eed1e92dec63b29c5fa9bff0785b5a0736b057e98b75e5050103a54a50bff03bb74cf79c6ec81a37189be82f4431f3f11021862fdc8543f1b6066bf54564915bcc2c811e8a43de8a82e9def5937c28892c66d46c990470f31eb86391ac40fe7e83b706095c83ed1257899a66e6d1908991725a32b6db123b559d719b7cc60dcc01d39b6c8755f57c63178e1fadacef0e22843d4bf1eb2364fdd9e7150d04bd5ed4b8e4f3a7d8515400f778d0d638c4f0c5e88668afb23b49bd723531f031d3cc3564665b70ca005a20dbab6c7bf179a6b18db95b7ca377705c3054417b085ccad37cdf29174aa3bd64f246db42554654051a3fdf6fa62b9105b284226d15862e4f2a273b96a40556f372599badcf1a9fb35a876eda84f1d9edb1818999dda03fe1863cd0796dd78db445e584dad7287971ef865bcfd8526bd36e8b4ac93174c8842e0cdebd8a77fd5947607d6c305f3a58019e81aace9326a58c5fea9eff5d35e89faf239f7dd334bbf5e29119e321017bac64c7c8cbefd26bb2a45e13d8f6fb8345408dc266a62b4225588bd2fc0e9e68faae163caf00fe2b8e478108bc262f4d4567300d8a3c35063af53af1826b7c187790dd5856fa702de9d40c0e073b0286ab10fa2bc7b7c5f826bae9a66403dfc67bda185d591c340a8a2ff5536a6d4aab9db075bf316013a73ffb824f9f2574f1094df292162059a7b16b8308e929ce0b307509a008eba572b106159e5c5edb61449cf4d88838e2a13b561fb46ac8d3145d92e0584cf1f02a26091c5de12cfaf989a96944e2c748b902727d80de3959c24c75e9b35258abc139ca45d3c574d7bb8db6df9b6290936d94a6f85fc15d39fb49977c4ba5edeb40b0d0dcd1f549cdee1488f516b05c8c33686f43d32e8571df83257450237cc532e5806d31b0423b5c440f0cb25a30099d96b59f02253825278619d4e0a5243f238af7347d9f936d3044e6aa1a02076b11d8f636198ab95aa09a68cc950e6b9bd511f2645b3de8e887bc32d8550b31e42e38c18ed52ea1c54ffc663fee564d668fffd9fe206f609e061488e20c3467a6fbd012f3385c6a2cd77d5c271e97d2b6b8aa7e85551d1b3aeb9de2e864b07c62e5362ba0bb4d6aea729aaeb3a897537005ab489ee2922ca6dddfc59b3b4fbb49b17542bdd64d74a6303fa99fe2f6c99163038ee1d94ee36c769ea45de5d759ef1f096c9fa8a4f745617bfa821260437121be264d26a65594dab5fe3c27a5f4d4644655f2ed89e8dc7ae2788ab9c13523587172451cfe2fae9ab1d040be18a251c043f7428e38286c2fa0ba2f36e54bf7b17aff9af64acd75e219cea46d1b59e5f8c3b70870873e69ccb70ed0699874da707139d8bedd1dbd700c04f85734483acb5c1f94110af9824698663bd1a880ea5f49b67949c91d80bd74c20f483c5f99b9d4a6d1a08337a6b833c9ca87b8909e4ea03a286e2d0fa53da0f1228ae98b2818e585f3e8570dcf67939ad57a94d04f5000e1a44cf3013633924df6e908f3b0c1166384c411eee50362c2e94fd691a8a9518be9afeea32b4d9d67fe1e33fd859d5937e82769cceae5613ca4f74d9f7763fa271e199b8fa736b68a687a41286fa54d0904f6157844f6666879271a1ad536eb6a8009f8c177b3252682c2e2b4ce9269f8ca7ae41573b2fc0448e6bd4520e144c48eec75ac28595835d24a768ffb1fcdbc491502df0cefe1091ca460ab40d5e891d3406685f8e39d1eebdf109cc88501015e9eff8066f6737a4c27f26c700273ba8610fcb6940db0169367b0f714656dadfef632172ea8c94b3ad7dab1ceb1364cc4e108ea8efdb5da343deffba442324386195cac755d82a7b1af71f8e9b6de1469da42aa5f161cacddba08c4bc5f675b5a48b1f01896875fc538d73f61d0cbc718942e462b4b04e2837e8b857db74db0846cf491ed16aac27a8c832ff5d060daaeaf1b60e2c4f4c887c66781634e9f840eadf2fe07b7248a39008fd13ebbb4f7ebbc4eb6771e75e51912fe31718807ce14bf0e1d5fc32d0b44afc1195fb003e3cae43cba5eb7082826ec708be1231952547bd9713e7fa4a141a360fd28164097d9475be19830f148439eea23cff0fb87af390884896d021e8bca12774859b612c34ef7715f0c8a0ab7295190587129842081c202c6dfbd707a15c2c622293123431d1e82b1899c487c70a23b4f5b82b51e29889d23cd9ca3cee6c57c32a39d0716844720dccbd5c709198bedae4e26659430087bc1a84e9a8d24d98a7b199644a145137f5a08913bb9632e637a29b450983ddf675f428566664664d8587f7cdf454fa17003694584430cae9d11fb8763b09a5353ec15420debd4fbdd865bbf32a9a8dfc5484514ad46d012904024a474baf5b953b46d98d716ad3251908d2af136bb73d8b7ce69398ad4cd46e6ec03d1c1f468e2f53e95233d4408b0446861690a27a5916f81033f110ec86e46129f836d22406a25d38841a685c445f0355f3b414b35fd24b968e059b25db076c3b42298989268cc48e3c434cd96f83253630d5eee09c5d1ff02f673b5339d00b6a1b1f775e5cd2862394f9d77d98c946933409db976d07e35374a4f84cfd9611c3ddf26c0c87b1cac136b2fc42982b67918868e3162289bfcec0b9103fc5a0b3d5e21bd017e4020b077a03541831e478c1a491788e54395d665086fdf99cd2980f07e66813161ff19b2db84fb3dd5434d4954ba6fd6a01d48497317bb1e5997a2f39b128a22dddae46fbb10087a996eb7a84d60c9deafa8539c3a5b889d661882125c8a7bbc8036adff147677e095577b963018f53d1e334d8ed2391d83684a0730f460efba2b37d9bf7a2e46b643e26eb9779a3a5929bccbc5903627cfc9a8d45436e3438871b90a2bbe205df96f65a5ef820852c3ef31de9cdabb7ed12b0a81ff2f2812e41aea340811d082e7ba086f87ee74e9cd34cb905b720b267486b6a1d33fe9300a9c566d2b1ab409e05ad014c7e64af3fbde8d614fbadded916fad91c93f649816fac91d0d8b0488c7d381986a570b476b17588fdd12f9c8ae50180416db19b6cd7407520c209e40e5209df4c6bcc51503366ab61592030fd77deaf3db847c580283e82c8e6fb6e287768361a8fe6c570a5a8d2ac0157dc827c6873cdbddc484c5823665781351070cb23520bd1f35b720244d2368ea0ef966d48d15eed7bcb301c4d2288056c348b11d52b8d7e2746d497409f4a10b18c1c68904ce23855746802d373e581e3999af80e5941f51ef3edff2ba1dbd24d632df0c4882e66d63793328ff672a70c1075eb617ccd4b4302d8c770d7d14fc3b242d3888f0369947689aa0bdd5ed7656c6211bc041c99935c8c2d0248bd6b7e9e55ae1a906011158d1101899c81f4073c2314311458905a68edd364d5a95dd44ecbf2a730a28dd309011b6e538a890690a0dce6af86cff445f4f25be4bde20e415f224961ddc705abd4c9751114bf2eb1734ec09a5573bccd15d175f90deb48b0118f2a4042b8ab382ad75cf3bc050ad7257d1b1a72b61d2909f77f56cf7e5d2101d7769ddf8ba559f4973110a4ba7b188a0cbc7d47ff2c9b16883de25105a846817ed5d104a6ba58fc4dfa615cacc12b174756cbc81dd1cf3a930064b300ea30ccebc75651d42194585336beea28b3b8a007405f75d6d8afb1d781187a939f75ee619e09ceeffe33b922be4657777000bf345fb40fd64ab033385b39dbe574cc1e2b11b411d587ad2cbed5b2ee20927957f4d0c5b72912612dfd7252bb18c6d9aec00f6e4b8058a1e7bdb6e78dbec1b0b56ef14bbd54b707614a57547564de5c487319a5c7d022df40bcc745c83ffbbef3b4d74f420d337cc135b6528027fdaa7f5396217acaf98d6521f26002c3d82ce0125fdd83b0fcaa7ca887be105d4220bddb9c6feef6fe95ddf47a6e621ca6c7f6e6dbdddebf52377de7f69cf9ee8b37e04ec555578d8105f74bdc2de3250326efd08cb0d3eb5bd939dd9c09f6bc56c9dba10f8206c1e838762b60bbefe14425a2d8d7963193155f7bbab862fb1b66c7d576d58ec311b9ed5c64fbd106a009f7c2c0e00dd4a4262ad068f070ece40154abdf79b6c33760dddf29de892e78f0c0cf89122fe5f8e5f330e6cc3f767572d88b818f1b37e1b09873cdcce7775703274f4532d465b731a3b050f891f89708126ebeed853b3f8e1245c317b13f3c44de3442a204de861b2743074d4bfce272007ea4006654f7a80ff7663fe545f4d049946ca862997808f6ec7a79baf325a9d454fe076553c7747a7311d7f57a1efe817bb556c664eeb1806a6db3b3fc0b77901e58e9ca4b32c4d697e3f8b1997221363a16f6b73c3635fa32676268444a407074d770cbf3bf245fbd18a8a97eb3b2e0f8b487563be6dadb482f0e42ed18159e87d26b6069185bb86f0e41f43fd14006d082c712d62fa30446afe5081718fd9423b2ac1a7d09264a07c4e6ae3f555e8920043f65fb9f747d0860a7169704efac6caaa73166e3bb3395c2ed2660aec8e5ffcae4911a3e2485f467f5c2f5abd8e14a0cd6edf84a9ed85c83a56a8e0e6acce832d08e487103417a8da326c6c894ed7cf6a7c7e8828228b9873d324e7105b29d6fcb58c6b6f55a2d5b0b5fd4610bd09dc4afc628ae487905b6f1399afd1312f77c7db5a7fe0d1f587b9350438618b89a570bc48081f23aa6479cebe1eb98c92a7bb7c26696076ead07f0bcb3ec3ec1faaa2e3ea1708c9a9515ababf102d8b05b6113aed60059116e3b6ab5ae33d044626c4e625ac0aa7fd9c96ff0a1dc50fb525965fa287538ff8e489f89eea0717fb28f09ac01070a03981bd56b341d2d696548da4c74506eef259d6161f0177a1543d9515b269e82258aee81cf88691445d439daef8a32b045d65ae6e9ac9bff92ff98b2dabf2b60e6c00d52fc2e1e44ce67099df26e3a88f1ed2a4de692187882a563083f3ad34506d2ffe403a4dac660b4c4fd0cf1e43bb69a7e013111bb0f935c209dbf2381c5b06628de24d84d294d02ddba96a6c3d523175dd7d1ff2ee9ca2cfd2c4db7e6e9ef6aa2517705f618fd26d19387ef95c45ecc0acbd9d890095627b14f6f5ea0fb893d7616eab7d7a72f47ce95567bf0a845d2406827be22943e35cdcd6c7efb8de9465ca59f529de24e4ebf82206d8f620c0b9ad814013067ba7ae1ba29b2c961905f868a8f880639308d675abed719af4310f1ea5912e32596fe99bdd21ed45a89aa4dd51484a9640d68e2d029c5d3d024d18ede06b404df46e986561511cc1cc033bccfcfff42be851c7a55fc9f2644afa63133d8b356ecf29cfafa3b7031f44a808568ddc4d2cbff8bdc33f161a0b7eab7262ca347da60db4c4d3f4fa59cdecacc81f74a37922f030e04d125bbb515088f6ece051eec915168dbc5d658caf86f73163024b35da1233fb742a8d1e99c883590f796a1e3db5dedc2f3538c2056d314db742547d7241531ec5ed2bc422c922420b69b49c68c0ca025cb022a202a1d07d74e7defe41ed07b3df12dfa27bbe50d6a64d343fd6c6151d8359fe451803bc909e0090fce265d56f91f1d8e56818d6bfa5f8b4039cdb017a338829c73a3585bb9f331204f65d9b256a852c22df95f045309faa0f9fd90ebb358f6f2a3a4442dd85c78b582c609ae23dd2509cf71b88741fce19350c1f24c76b5af81130e68bef271aa2d98063c154c4c77c6c76aec8c00e1d4e97373141c3ad17ec675624ed971510a17cecc2f9213b345170c3beb5e43d31e00019ff7cbbf068b2207dbdcaf0e39174429df8c61922592f543750b888cdbd5758228ccc5606b7829ac93a4272aff0f923db3e6a7463a6a8273a97a3e21c2512939816ed0fc7276845f00d2bcf55fd657f510ea240adf1353372a1b83e8602c5d2f12d021e7a68ce12e80e01b65d50841e4bf2473157c58f441db197a915927f79767aa95e4e7e8ae2c9aeee7a6dafc3d962fee5b05eac90e4905fea36a245f47525c4472ac46ab57b7f1aec75e43fd1903c964eee33aa069c23b51c326455b966d5c3104bcae5b13e6632d685b87551171e1400db7ab12fbc6d231999d6bd6ac5a67a20005699479fe744db024c4603d0f6183a1dfcad0968df0d407d3a0df2544761c2006709cd3123fe21a449380fe972a116862cfd72aa8e58ab9c811a95536a99a748200dbfdb584741d0029415130e69cbcc6d099b8c47b86898256d7532543d774c61a7dcac6eeb037458832b4eb248a30e66aeeab0038a4766fae4aab583148f2f94d3a91af9be44548e8564b80822d2260224fe9a6776690be08930763c9ee738b469fa57a3ec5e929fa3a84c4fac245f232827539aacb1ea5d04d1059d466612242dc583947019917a52c0d45eb0f0abcf393ca05e893db24d7a6558392ea4e89139c2761c88093f7c9c443f4501b49755a029ee00c396322ef0485fe66466856052ef3d822a42e4e728d5cc71a73194317d3072785c604d0b8fcfdae148dc4beff03b1c9c1a44198fb73506cab82a6df2d96cde72cb3dbcfe18b29b71c5c474d7e89b20cb8003ec7e92921a8236d8a70fd84a7f46a095858221718ae5452e435907e6108c093baf4608161e931eaa4e127aaffbd63943b8fbf7906dd5fee9fffa85f46fea3157fe226c4eeadf41592e961f73917b401cd7920fcaada7eb3bf1bfbc6b26617dcfad13fb563a690046577a2398b1d0d728ac8a71644c84942d244189cee18f92c002317ee207599f5a1d6a0aefba0dfe6b971e1b3600f7bd2c217df0c48c1dc8fdaea1609e7bc410b86870aaa3785c19f39989047a8ef673cdf84a2b6724cae03b38c95d222a8a1b1d1f9ed80a117b34abfe8c9a6947ef676a1f5b4a568a76c17835614c7b88b16e5234b6cc3a46e7a7ce4e63799315e460605865ea298bf977fa04967e424236876d510815da30225a26f5560c0588a5eeed92b0d49b8bc42c75c4a9f8eec989a1664f7f192e96438b97f978d8a11e4f690f2e30ea55d265a72c15e45af09b22f74690b27bc9ee5492eaad526cd4e510439bef149c30ced2d2fe75e3352a7c45c59970f8a6ab007445442cc43784282181a684a4412838b06e885fe656861c209aa3c7890fcacc94f66cebb1a6181bf5b64935e86eaa5e6c7edb0f6ff73f9cad063989ee7ddce338b5013a5a8c9c8e4d864c464c4448c67a00c3d3eb393a4fd8763ddf277e6b4782a98ea858ae6a9847d474c04e00aca8f7456f642730f0bf3165cf46a8e7e0d3ac768ac1031c888542e5ef352ff10169fedfa18d0c7d2d53ff157ac6bfb440527c6df4d022f96336319daec5cc7c94d75a41b4365e9494cb296a199076953bee0c6335618e290a901f403a36297315ae91d74d961ba70a30ccb1918ebfc41e0c65cdbaecf2c6d57d5ba5ad845ecf32ccbd00b02abcf3f3044aa98f74b5830b34bb8be944dd079188365126a3651201c5515e3890ea9dead38a570afbfe9ac38da1128b33655a1071ae7d7985a4af83e0fba8128431719d7074fff89ed4b83ba8f8ca93ac9e30bd7a4282121bd5c753941505c3fb83a2c77588d64bf38c27d2ee98f40d8e01f899f9e19b8c357938b08e11b1916407045aa378381c6da4cf0465e20969de150c2384c174d2bec1270fea7d7706e0c260ee13a2a8896ae1e27c13b9e8c1ffe4786ba4f053374296401e5ae7f35adc7df0547c41131d76503dea41d5507ea9e9ac68dc4734d064c6ead31c37cfc35ddacc483619f113581a2b542a68fd5b290ec4aa30639f0ddcc860844e8de0c8faf62025fee14335723d186313216c4777828336763577557b2fa564752dbc7f77f79f5d81781734f26f403bc448e5873455a4e8730552e9a1a1058ca966d28c7bc54efc54d948edaf6b8e116c6de8f028a5a075a3b13e5b0ad30c919fa638454ce180f422742ab95c13381c3ec7209258079e6c7aec7e5807326189448d9c3d04b9622769c9ae27e9fb8c9d94f7bda6f3531cb9a750ef503d2c4c2a4b4d2566a015d90610c0367e8a98d2ceb32025ba6e71c175cbf0e3d107e19aae64c3adc94ed193052bb9c7e4157e9d040ca43ea7b2fe01ad9f578a5a5f6fea56e496efc62055fbafe0308375199a5b179f37e50b1010cad25fa175d5b692b3643da0f59a96a9c9e33d9688f4ed142f903146211a973fa8414daab85c5066d4175d3285f3dd7937d303eef8cdf89305cf78372c244a42ef3e5a706c9740856a02c1ea8423239fe1a74c8c5c7b30a24b85636f4807c0afe4cb7ce6ee2d29408edaa2a8a5560a6bd0c3de706f6643a6cd3b07310a61c2a1c4902b33e39454749955266e32e6c130e8af9f5f427d0a090a08fdb049b122e2e09fadba0179634b01b44d0cad0e4d1b1f9585d753c98ff90d1271671bed9f7ca5705cd4db19262194ac7d561d2211679e77b19aa5b761ddd853a170f7245df758ced5ac521eca28dc049474e84e33c7da8f4d497c2118fa283aa9e3dc37a4a951b8e1df183442724179fb7836c0ac2b43cd08f1baca656cf4cb6ea46901d79780a9961f0c41819f43b6a5e141c8a6c129380146461535681e1a5326570b978a832f4d2627ad4e44369f26dccf44541053f73688a3df24ad27109132849e2e81056349184b675688080a41d3c26b869db3d188c5f2109d03dd2b89915f8b336ec84fdbfa3d63e32f4af8223b18cab73d8adfdc4ac2558b37f906b3a596344197adb3b286b53828312b76451e93bd1d43b952db7e479917d4c06e9bdcdb16e54fad3ad3e198aee8b2bb2202fb19cab8dffbbb311b372217ca8d9adc9be387e5b878d6bbae955865263487b718f5535a6e0eb9d1cb2c5ec851f6e197a10ea167b53da6719b77f7bc103c678a90e06a89e6166d88bcf84b5a07130c87729ecd778dd51c0daa8a0d1f204a2228d11435c7fec4f20c8090a546c76db556b1bebe2c003052a8452f95713331228d0e1890271b1fb01cb50f4280ac4b64781cef59e78427ab4776544ff5952226d2f09f4b6f8b70824e9a8432da1d39c22a4c25e344cafba270194149873335a42a02746b0af4689217db81a854246736ad49886a1a35752aee4085e69a115b9f9760ca51563503cf7068e71c207427cde2af7c8ddfa535426070e30cb9a0ffd4037d3108dac5ccebd069f927a4e2fd02643d94409f34c76daa7af332466ae0f4b680c4b70c42ab3f7f10ecc2e099b87b2f27a610d7433413407cd5847d4e958c9b3566ad3615be86b0751c643eb68c0a25808555b0fea2c6514a1e04c467ac074dca950b1fa9f1131131f24ed052645b6472811984a9c92961ce7293294413e52a30c3565416b97cf4243248a2d114d0b2d0ed4f43b2e1455000505bdeb356b9da9d8840e81ffa1fd6977bffc0b1e0118793909a99261093abd52630a0e61c8c781e653dde6d40b88f139dc18c4f89cbe31a97f229057a9180e780293eb99a73328d66db6681941fb3183027989814fa4a061df4851831b82530638de087eba505150945b6bd5d3927186925d765870ee5adef965c7e28db57f04839dffe11646ca1dd8878cc794de60b2146b2d9db029d79a83db2bb1112567f5969a704456e249c2864bf20c9e883fad74f6139bd8cfb84f4dfc0cbd0af46295945fb91801226a46255295a0a0ca40f3f21fd5de1d7d079730fdc899ea207b95bd4ad30609c9a2235fe490d4d1b685ff03163a9862e4f2e9ab051c65562fc64e983c1986374baa03b374cce8c7b020b0d19dcab6f684f026f851688be58982c84758ab088e4d9305fa3f50242419f158034471b1014f7caa3afa704ce800b2dc184029fd6698179a65f30cfb8393099463f8e43084d75626dd687b49f30c7638c06101a2566f8817d2fb5eb42a1fdd4323bd5d21ba38eb19f922468ff6b2ba1b4e3b5d48316f5c93766a9316727983f7199e7ce1c863d5a7afd9538c195163b7268d2ac755d7f18ac9c9e2eb0fd9f9dec40bdead6429067c21dae6fde1a2a4f325ed34e985d66195e800059b9afee5198999f24906d9c378799f54c00c936145cb62be233a0b1f9e14399f8a2aef88ea16ea0f158df130ddaef4f647941b934563da9df4134aa7fd47d8eea382f22e797eae5e6ae6a3018d9ee7929e0e33cf105e4156df1b015d08a8424a95cdadda4cfbb611a1fda532bc6d1def195a690f4f17955f9f419959103260d8d182b360c0b31e2de1cffccfe3cb04dc7191c4387d1cf19ec4a4005820a409bc8f08a7956c8e808cdb077f43b5145117163137597548e4b8f450f6979bcb6d886359fc82b7e85735bade61f140fa9881e52aef01c62d152d81ff684aa0af4a5afe761d9a6d25c2daad94f73cb1dde1322a1366d281d0416c367b794128c67d55b1e112ca0fe34812bdafd71843a6db8dac521b9cb102fc13c6543b45dff51f5f154f12fd0355096f08441227bec1efacc594e045f236a797e0884f7f22634ddcdf32d4830d4f8a92c9af40a4a0cdf53829aa2df97ec9cea608a71f38b179682dd6fb034d3e0f3e3f2538022b401d25be6f8771ecddf27da1d97b0bd39dc145f4e8b6f3f48e0014c2813d3831f8f2c1bf5428c5cea5690124cba76f42962020d6b8232f91d2bab7b3a79265a133e6f72627af51356acf8a6eb36dbb8492359a018320abbad77888336825285fab6ecde8d47f7f9e5267a7abd46449dfbdbfb68fb3e84e0b7f50b30f810051c285892dd01c2daff3ba1d833ad699d9c71c6b2f0527342cbed907db123386af704ab4b58321c96283d4ee863edf27807ff2e19188a948bcda3b1ec36ddc89f029ce29d7002200d316cae6face29606d8c4b38dd9c7a219346e413b812c165d4a550dde392b0f75a3d1771e7001d269d551834a766995a1c1dbf9c46b66433f39aa945b46328672184e671e66952539ca4d64f5c9e6367ab45771de3042da2f13cd7e5c5083b0b3851e6d013b3b3bc4aad8f56101b2ef2bf9c682aeb1d510159fb5b70fee7d16041084c06b3f19d38fae778d03ce44f7c570af02073c7e42a0b56885c8e1c1d723024da34f00741abf81013c5d3600abc73d689fb05f04b0864b6cb977e8f529e34ebd16cd96cabb520138730e866c68ba189e92448cbc0ae4f7b4cdf148e0fa44f068d7a51e64556bbcb13235a824a703bd4e1ecce69d7ceacb4e79c4787437029904720f7c632ea5484ec9e292cfa290b3f3e632eea541c4a51f4d70014f42be59dc4bcd600d3f1246a807f45c9ad9e995cbe233958f97b7cf6f4e9f50a3a063988de46d8e5d197f9aee740134e887c7195b0825c9a28c448d73f9e56b4ce686b029f6fc6f803fc0c59058bc2285b92461ce4e0a3422ee0ffc502b959b55e143c06fbea485fe1d2f5dc1e743e15da43e5b185d8031a92f9ce855482177489f2d12b47b1e85beabafd471b396071efd35cbeff43651a0b0cdee92b8051559294f5b027be4b82c94c76360ca1ce86a0889d21801f843462bc64741b68315229f6356d7faa8f2d29b556ddc1ea58659053b3389fa0afb3a2958ef3545951c82d52459254d845ed9cf1e7be40f97d3243315c22db4faf4fdd06d921112db27dfa6929b39acc1039683b61b44dd3b9b47d8b8ace9db96d0b6c52da3682dcc8b9684ab379d7a72345720b3fd263958c2a359f2dea13671cc4b70cf15fd46a7c1519f3b1d5a95fcbf344f58013714a4dc4c4757f028f6ad682c0c014cb7c9d284e5c62accd284c6a928ebdcece96a6001a31d4bce64b65c8ec21c97f7daea31260816b4462a69862339817042447944ac3f620e571c8ea86029af014cf2cdac425499e956134404280c488c35be96c679b695e54d0f4c1465f2a4350019dc73140a15a1b90e1f12821abb6bc159b6b49ef4c2f23332a139b0de2fc576669c781fb092abdfa931fbf4560a55413112f7a3548227a4e9b8c7011412d65dd973e6b30354b0328e8c59b3b7fe82c03c63ae402611ac4f668d2504b39a2897fcc4e0e3faff88965e98fba0f9118df872043925e31776fa583fb98376a50010e162944b735b88fe315cb8b11eb9189a04e986d9cfb10259aaf44c63daf26dbdfb4493df05815ea8a56b4af1338419baea99f93b67e1e1aae0a1a0766add720427d5676bbbcf4407447192da69f5a3147c16473efec972b2088fee0e4b0b4d85b871fff9a1aa71e3cab3a28227c614ca83f2c100fb2b23cc30360682daedd4731a37308404f7193770ff74f17ce41b67c562c82c5aeae08905ef1c7b7f434d0681f4bfc76e294e2068d10a521cc9f41fdaf9081f988298a3aa1224bf8dfc3dadef8604bbcf9138b0117181e4e00b1c6e562edd022a71f469084b363f86a95635f983b2c50f26fd353fc5ff8e24d7c10dcb98eee34a57749f2aabf5312f53c0b3cb52624b3f20184d1f432b4d4c531d7e6c49b9522cba946e0e2ded49d6387860bde881f1398949109b6d45a537ad29ee89e3ea447aaec006bf205a20da4de43a3e7ecf89bfdf94fd517c34769d8c970bb25f10193a1d6afebb9f22e0a4234f4dd0ea8966c6f89d3420d056e8bac0336cb2e413ba7d82964bc2ed7caab420a714ac56619bb8e1f3f591725cd1097b36fd8e05e65d5e9b444f7929dfc2e04d89a01358f085fb9a7adb1865a31dd1efea325ac8542a5029953dd84082c1bfaaf2ace63b39d7278004616e12a77f71e864094d29b795432584168f7cd96ae9197a84cc24aa1022b2183bee4a85874db0d61e6f081d5b8644686b6576852a96e327f2e084198729c727e455875db82e0ff7ba5e3622a6b5711d47b1210c7acbb02a79690020be4e542d51e8f16e8bc1abd60f5c3d04da8be31461855bf90d0578f8c4d3886bd2e73ae1bac59dd3b5345f01bea0a46c3e708842b09c494aba889991dda6db268a264210021336c7c59a8f2fc132b0ddf03f778c3224b897f389543c7caec880d2b4a099f86186293909ec4a1b11967ff11797ef0bb9dda79183c84865c94b1165292fc298fbaf642b4501812bc27e5f39e2c47505ccb854da8114cbcca3cdd3069bda82e04808379ab702bdd89f5a7347fd233ea3a0bd6745b4a781c9df2ed7c51761378569ff90e573074db2545f28c34897e8532ce1b4b5ea1209984b61b01c5984333a48fcd71ad18465cbf65ed1cdbe8d67cc7e6d0cf1306adc9eddb37792ae0c6a8b96fbd46cd369c743504c0e2bf57819ba26fb0ab885c00216dde0f93c7fee63d90f0974d4e58082c0603cdba6f7460cf5f8f9b1d8b7e96bcfad6562d07819543a0c52d9bdbf4bbcc6aed776528b7769cd30862213de4c9a3cc64d46b3edfd0d5a5bb69ce2e792dd6dcb7eecd2c4bbf2844dd62dc63a4b512f78d422979ae6cf058091d65b6214932805a7c61c16fc01d8348d015795ef5983522a1608f2c35ecc427a37194106c1911134ad53faadccc477e3b3cbf11bcb0c30ca3f31e0efbd795500a8f69ac646825753436ff0aa467f00c20d7f6b076b435013eabbe13471445766b091b47668c23050d829f643276d005b01d48ae36c0e132f67f9b9b920b7c6c5ea1fad5fb905eda7841062f069f06800e854ef45476b8fcd9ec63fdf1dc261f209fcdb3c0b5b1e0d5b19073d02697830fa4707f18c9aa373892da30cd09596442ce247f229d13d7fb542328f3fab934927a3a7127b03a9887f2d4511d1cd575e2683f01e0ebbb40cdcfdbb0a41eb5e4d59fdf49f8affb473b5e4fc81100a52c3cffc7ac3ac7454ca100997711918a745e50cd25b3af3138268b63a438dd5c7f9bbccbff0e7db841da56dc5bad09f91fcd331d3edcdf918fefb56fecfca2d31ff8eed0118b17aa1f23abb6b4b2de948959417b7a3aa78fc56835ca026aa0f74fa8e213c77c0c465d8ac6915af6f545fb5e2b6c10c33bd7904d1b487e9cea2edc152e94016b9283a6ab87cdcf0edfb131f9fbf96199d9231d6b47d447d6d68e2e603265a832ff97bd2688e74b92748ad4298595b1095751004c5ded890e1674b4e892013a44b6a3078e866fd0599132e848f28a067e62194aaa17369468503aa4f46755c535a0cdd09d45d29d022393f5481c9045b0668c1571d0026fbb570211bc61c40d0602a6123ab158237bd37ad0bd87b278e44a58d14e9687433cdfd3c9d317112f43ec896cb9decae07430cde177a18fc8508efbe529d264f1f21293a74b590cff576749f869450062b399b0f5b24cfaa151bc7e9b852f61a95422104b71fc6fde2f0657a51189c8d76703f77991da3d554918275bc3148ac7941d09fb2873a830e07ea86e5d642c9e54c329e1e914084ef106a71c46d04ef80b4289c201d0aaff03f6411be980775e7d892b33f84a18a37ec7cb29a59e9ed80bc01717a6b211f9de4f522a001457dd1f67164abcbc25b9cfb9b9f1798010cd3426a48bb5f0e3309769a51c06ad97f5d77a3c429a1ee3388e91b802c1488690e443a94c5ea0a8af448efbad871c0fd0e2367f53f2db134c627d4157d9a72e1e182d7c075d1823d0f470721d998e4d11a5575c5c366f0db4770b782c4574673377699a4f5bc079b2c993d247dce33e2d717fdf32787edb6aedff11130500f7e98b716c7721b98502913cbdbb23d7e86407b9abb1a1eb9d3f518fa977041400d22563972b56ff8c9337607ca3c090c40238674af00090327ab5c1a1e6d3ce5885ff9c354b4023f0e6622c5854b942115d4b1f65f353390c0530106c47d1313526e88a35b684263afcaed6bbb031295e45ac64671d7386ed2db71cdefa688d54c3637300cde12196454ccb232cd456410b131b627d06cf1d308e56d1c69a8547356b1f995947d8fc50590b359b9f4f9a50033f8bad6795ce0bb99409a63f422d6d8efe6cc45ed33829ef77a721d8756119207202ad3b3db0423ad0d4eb4f36123e97c1d231a11fa18f162935a6f05a0e5a2212b9886a15b91c0beb037d9eee5dc14652b6363dce27004a1a659a8617d3c5c124a4c2c7685b95aa14f7a525350e6b3a46e35e72503375ab200a2aec75fc01b562c6bcab1ed7e2f049e484169412d620a339f521142ae507e45e3ca57880a0f385f7be814ec4275275b9afbd145ae26ef43a52cbf985b992b5f09ed75e688072669aac02d3cb37ed03bca726cf33455e58797133febb84dcdeaf00b78e40b30b74a28089882ff27aa2e205f18ac9dd42bb008ea393a6f00f6297d6ce576378a0f13f5e87faa690cd454cb5d65b847268e62372a89d91d246b312d43e480ad6ffd924d888f6ec65c98fb8e32bb5bb130575af2005989be1e2073e76be402eb1844a1b137f205527017d1e6912084ecc31e905fac41fc77a3151d37032768be068107ad43dd07a2b89260291a37081a94149c63af5db1e9c2e9aa2e7a914580f46f445e0b0f106e0dd1a7204200ad63ebce75f1cd734ee936def6204f15195a12109689387b378263e99669e44852c535eb089c543b9140af21c0ab534053006d5957e7ab35487cb5986464730db784a8f64eb51698c92ff4b0a371c36a5fc60bb2f741c0e1ce9af0eabc0e3166218d1df7d90182b2cfeab5e5d5ae45cc2a702e9d9ee3c3298ea90b499cba0d34fccda7b0d4fc86076ecbb72a657d41a28d82ee8615f8e4f261fdaf3c3cb3576fd7e4e5b8651aacfd83199a307008c51f3881233e705187d9a5ffb280c65e734ab2011b7c02c5a064e266c0f2ec4c98920505a15022e39092109aa85d9b0f6f57cef2d476b5589e5929a2da97d50272560f811390b65543689dd180c999f5bb439b37e489d801459dfbcb81bd2365a0116a330e05b3849fa0d23d9e2ae063584d46b27a419cce71c7d3645be51aa26a24039485904e45229e0c86d2ce60f3b315d5af5601ce6018ca19c949d00f30f6479174756433aa539673945b265f9d2c3d0bdd98e071f4389af1c4a80374e9a1670a5a58b6a13e861551e342d686ca38c2df8c626676b82c555a77bb196186e7dcb1e0c727133066e848c161db31ce1f4a48a77e01a0dec76c3130b7a90b1d71ed44bbe8239bf45f51aaa535d5618fc150f4191c631bf5baa1fbd4219a7156e78239a15842580dc004629712a735da0682a13b29eef6a2e8e3500ebc4711da3709e3d743a66e392de68435951eaec1cd571d9ca847b71b2d9f17103fa0cf8b5055816f3d8816ba9a69352538a5048ed2f1ad642a9c9900d0b98524d368a25a7b4b308e45578edb8cd4848299b083551affbb31c5cb057a5e7fcd5cc4790abb108d4ba5dc93dad5fcd8417a36c5630a5d1b878cc41842ef3b35322f41eca2136bb2ef6e016bf27654248d4d1fbae46bb4873787d945b589d452f0b8d66c74d0497f9ce525a93ed04ff21da8d358adc98fffed6125a6337d6cc2f524089a7ef8d3543675d67329ba13a1b6152eab3190ab2414df97951e7500cf23720cae206e31c3ad24a9bc4398d56fec5c5fd059f5590634117b8c5dbbb25744e214cb2041f0b67ce30521ab6c9327ba97553c6969263cdd2ea99e56cccfc8b6354b9711d80fc9e59330ffa3742bde12ff55bbc3d5c131e832cf53f272bdc422c631736b9e316004e61776522877b9e8d756609918cef4538ae94255b844479222bfaa19ace955cbc1e64df352d3fdc85e0808e18c29fcf56f1de562d119a49ff6a92ffc61055d77136e8df79e33776afd884ae67b57f3ab31c053bb1ba93a619310de2a5664377f314b41f2a56e706ff2458a06ea22b4dfd96ff82c5da4c3d5d2dddde580f10ee2b4d69941f25ccfc3a2e6f2505e211b2e6bed988285a89d17ccf28745a0fd5a68a432d43d57ba37e28c984657096688d1085ace44b06ede653a253a29a6fc6911753f9df99da49e0942e1d0b0e4e5f3d2884fe8080f171e78feeb686c45040399c67e315017b8e209ec4eee2e579b88c296dc0ea335f02377e130c642639b72acca6bf7cc2b9d3661a0a33c91a950cc7badbdd928027a2bcb0e1b6d327c056870e379ade58834993a988cc30a45ee255fd289ed0ba9f2a3f2f0202ba26a6c3b434811e3cae9e814885edaddc52a102b721170c8cb9e4054b6f9567c920522b2c8f9290a702a3f2cfbc70e4ffb07cdb2dd7c52ea03e9d005bbf7ed6953856b83760f7810414b04cbc5a170d42a334b9fc5560f778b99aa57ef91f161d86a3719c72257de41d6e98ce255bbd73c963d357decd136aa78a184f104210cb46b2713a2b9c26d10be13e1d222afac5f9e20712228536713e7b43afc0fc9c61d4b57826a01d57503e89aaf867874d911512a5c6cf4eb83b0027871250d3ae82322212b71d152ccf6f481796afeb14bf88855904041001f0bc0c08252b1086d43fd4da006fe8207cab1d0e5e64ac4263756183fe13ad057415deb74a8a4fed7015c46146efd6c18d34dac448e51a1ffd205b64b60c2a382a14319189b8ea46e5db70673bb3dceedbf824b10df6ba45c638b458e04d5e9f267deafaf1f5cbc6618500cd54f92772a94a36bba72a2b0eb0e7662888c474af85d0b14fb4bc86ca9938f95ed21056692bd71de826b9d9742b1c8501973b1532789925ecb741fa2d17172c299794afc1ef74256b06ad3f34a6cfde00d9cd9a706541e04ee24af23d23e83ac7d01e9dcd8ad8a56d16ec4886e3a44264088d8fe50c362a48670333f6f2865dabab9d7f1b2d009a7251a75cf5cb14d88e4d12830f4371e5b632aa906207d0364c6066249ea73a07d0dbc6b307742442e9184a4cbb8f92e0bc09a9e8be8da72a18b1b899d47419050838cdbb9abbb21e91e6bdc8a49052ffcebd0e1016daf654af1d361dac6ded722ba6b4fef46df56a20be81b0300e5fa5371de066e2634bfe57c17e523466bbdb1772a0cc6ce3c516bfdd2a6a62de987f4b65f0dae1a6f7ff2d0268e9585ba4d08cde1d032b294005775f56f605c918b0bdf3937426d4e05a0d85253d7c433771faae5e2a19bd5f7dfc9878caf82cd017e2931508b4acdc94b637b4bfb0147202b4a0c35d9fa9c72c3e750be33df5a28fab12f8f8c0589e40fdd0d7230055a4358bd9e969609e35bd083dfb09c195a9a8bf11e2fd3e153bd4f4c0cce37184034dd85b7b99429367a5c1fc5d66314784655b37e04acb3e0b94cc92928f7a0e5f1fe090952bcc29023e5688c5a7825a6e4889f493346d65a0ed2eee81d42cfb90e664de8e76937291a3c0e3f1ef0d0a51e89b9763d4f9dc6841393ccbd17d2b7e50ead6af5c105889e7d32a4f083bab9e0d87db93bb0e1b92c9eb5409a7707286b409ea1238faed64d12076d095c04d383c2c8e881875aef624cbafe9f018d3426f28c674f48d5009aa3c1b1853ad5a06ad90ce0be880758d030646fa03816c59709485ce4e051e29bf9624cb87634c0da9b565b097c58accefa5bb5fc1738769b942abedd215deb504f526f6bb05e2ae30579b82b72106d800e83faa5bd74cf33760802a7c165f88e639baa9b1cae745006e252af32ff8563b4f12e6a7fe783783e9fdb813b8ea9c39e3a2979ea8881a33e9c63166a335541eb950c3b60709faa1528491d75922de15d2126d4d31102de51b087cba33bf0133d740bb600ac2643c3c5e92230d6d40bbe7d25479cfb2cc01b4eb0edd125bec276a6154385a5c78f2605a99b28656e6f2898c4a31d112c106097a20255406b35fe1ded428caf7083c08d4c1cb67937dec5a05dee428a17e85d6d92c22c24402c11e2f04c1493fbe773ebec9afac63eb773306d5222c02b2abe2e65cf37587c8592dfc5adb18629a647b867b5345ca0e20f6c69bba11136a3538003c46caa9c2dadf6e1a94f7484672a1cc9150b19a376a8fa7cfbf2121d4960448757b063b50d9e0dd3b1ca8d3d6c0badfd961cd0930b489128a96dd86917a8307a77eed3957b50c3b31bd40a583e3aed35894390defbdc60bdc211a8b0e6f175ad1f7faa86fbe9f9e40d297790c983311bd1d22554d214babd0d7e1212cb3cd86d8227f65feba1779dca2069c55fa2b8e6b180b8db1bef9222bf38868c5f236c527c54e2e8ac6f3ecafd044065fe0e00ded8873cb877373a5114fc426a96ab7230aa16982ecceb85b0efd92b33aa050c3ebd5eb85dab2cea3610dd40e5252b51dd98e480908cb89e41769fbe92f284e50e6924447dc3a920882d16aef24a42df494c94fb3801d9224807c53f25931030a2075c8b7371634accd8ae3007e71c2bdfffdfc9ad668f0a075c1fe98ef433e398c882b4ce9baa71adb0f69446d276ee2d3aa0b723729ace90a3a679197e9c60bb076ce2024ce2b3b73e73da686e3874f1b32bf7bd9864a18b1b28d20587c6802017e4536dd2db7c9d03fc5166a5c565f69de8876c53fdecce609c581a04cc68bca5ee004927971d714043f5b0ecfa0ecf77cc3b8550b9bbc46a3986b2a006109c89e09d3c63fa43bda32694e99e96efb68c98a50c75cedc43b4182d906ad5b06efb6ee592539d41e42847af2c949794f5017cdb457683d9b0a040355ff2676d12fe9a2a5bffc4282cfb9e8fc3128c41a6ec500b044dd660784e52cd33f813015f926d060909914ac747843b99f55ad6b37fed7d382f5c29d0cf8496cadef73dc0684353d1f84bfcff93e9375c2ca361d94124acce655b0c862bd50d95df140b0cd9118307db750984f6c2063ee288936d691871d31afe14160028e7f1b70ed002497ab07edc24211f2c6d9db10fb6cd63ac9bdd37ef32c0fe822dbd49589f456addcb3b296257e0d3018eaea025829bf6e7e7d3aec45a181354568a176ca1210374e5c316a536b508becd2d893a50684f79b0efa4211b9478360829e579de0cbb340c8a960602c91dc1064f5571f8c83b3c7e2c9464d6ca54b033a900c388a56b8f4faf85a0582d285d26bd318ca0b68e0699aa5b9199a1c4b74a113aee04f457a8e64258ad44b9e1b2de98c1197959ee4f195f9290fa47f19d3ee4920d48165974ba5cbdf62b9115624a29a2a44f694253425851f4cb8b40e6c56c5bbe7af369554a1cb8a71ef9d728856f0d1e1c290a36915fbfa5977652575e23162a405fd33e0e7a83ee104f9f26b924ecdc3c8ab969c78f3c400bcf2756298fbbfea68147aabffd0d8584cc0274f4a86e8fbde282d42ef1059bd5024ba4ca8a4c29f6ba35a923562eb5af97766f8dc5ee8c8ade5efdde4d7716bd6d66141a84db11c05903e250cf61e738bb32225e5c3f536d61b95c4afc004389614d599a768c9fcc51a837922453d382e14948422635bcdd02069b692e72b3108ccd1b1de6b8f7ebe7549eac512e58489e07c868eb70481eca1e4690e85fb1f98759126cdbbabf4cb3e425da7d4b203af5103366973c96ff3d794b856c784f2e83882923964f1a47d2c6820b8cdbaaa80830f0cf119a89474a54b24c114f44bc55c95f50f8b39c7eccbf42ab389b9ff0797c57a7913f816642e0409f4466e8cba85ce55e58d87278f5b1b6f2e329a007e5ebedea4745b165b84dbc0b50b03e203e7b6972cc5b6eec41154c5955c7cc46ddac755a1b7f8ca01eb4a2a658a97b7dced0c8bfb3bed58f9bab8cd5b0ca56d1df925932796a9ec05f0393948584c557b6bdadb4cf943b60c2f0b36a8d703691b7f9a1b570a141c3024ccda2c3c4aa5a9ce8b5831abfdd00acf8cc1277cdf58a8ac2f4977c2ce55a457d9af0819f475189b966e2103d8f1193d5f84d23e483bde96b21ae1a282765f6db023d805ffa272f4e0e111c07f29d538a16e5653abb242bb7c6efa0af422ad19f733e35ae4dc03375125e23de3ac3289378a02e40dca7adc5331f33b00753b2540843e288814535d375b4d33a4f9b44a48f28583bd22438e2115281acc2a79324517e4fc5a55d14cc99ec2999103de79456365bbfa7abe9ecb0f33107e4da79a4fcb3ad261f4816bf7c12b8bd3652d683b76fc1624a0ec69a6afd38d123ad29d8d67fab7eb88ac9636fe9eb597f05cfe7f2fdcbc669733b6f29fa2ad4d0c41a7875a969e309f54112b503280d9ff6592750d485cec730c65a895ff8525a279eec00c4e2fcc6ecb257eef949e786e7a44675c47f28bf451b2bee62bfc40739f1e2eb489d1ddd27b78b7635127d3d692f2a778145ff24b6c57222088f90ebca43bd16bdd9dc8ff2eceecf12368b34bb16760ad7eb296d9dff77608d563d9321ea73bb23b306519d560bf81518613c2a5dc3fb1b1141d62b24b62d3c4f21b0e185a9466a13b02ca0aa0d4bd3c49f7c33e303cba97d3376c930c6f03d857991d0244d8c7804993c2b31b1720892b1a8596f0b6125a8b440e2a58952b0a052506dc84ed3f98639684f558269a5c18eff5684ccb46c6a03bc25d16b569494d21e105f1eb00046ad8bc8d1b818decf434226785e8bad73c5ba29b8fe218b6d937ef01f961b4475cbeef820f03838b1eaf92cbcafadac8428b1beb6463d8d7303c05488402f348fb928c9fab3a48c1a0b80d6600f67d99980cf8d2c7d6ce3351d5accba18dd129d9ec4a445842cdf3e60b2c849160869b6c90521070f849902d3880e43ca36ad7214d965d66eb8f6d56c07ced3b1c3bc30db45bca042e87f5b8a92e963b1e6137668aad9eafa48d01350d0b1b0a80a6d41be398074ef751bd3629020b6439a46bf4308ad6c8a30d96e3fa3686221cf1c723713b984eafa1911e9ac8ee959202af9926377de9a9ef869204633065a928825a843ab2fef85d9c26ad1651de7f58c4f851b8877ecab9cd310dbabf13f51181d6c8243e25a444020145b967494f79130118c67798e9cd9c13e9445404a6b770777a849e7773caa0ddb31e22c0cc94640a2a5a903ed3e79f20689ee2bf68ab12bac820c65f212951c58b46dabbe1e86e28c84149f356dcb87f619e8768aa997a7a6997f840dbbe689e9bb68ca40a7182ea2007ab14d27a95472b2ff12aeb6bebb029d486daab3588b59e92f13dec8980db16019a91abf17689a71fccf75bdea05045b38d47836a92a65e90be08f6e867b37fc709e54f8f45e4d1fcfce1a3fd9450e59c5ec4a3c1b9dd3ad2abe76e4fba8d8e5c1fd8b6029c7e4fe87db386c888df39e8f355ffd1f6b273e70061d5476f5e8ddabb6937dc3b2f26cd3cb98093b40765577b327b0e442b3b3bf1d0d72bf8e975b518d47148c22336780f9396ba23e905cf52e6416a8a809292ed4eac59095e409eaa2c0da3a1667e83ddd023f13454759cc8c1f0959f57dda3c6e4b0c09e94200a97b2916b87227c430c5894e3d44c623a1187ae569aa7102a91de8c37bececd1ca6b3c01cf2d9a8a3004538ec413d549c9112fcf71c39b17ad4b20bbe6fd8900bd522524481261d286858b75b4f05333726182d797f8a2182c24bbab99680a8765de01e02f252d5b1866db68d88b6069b5bb426033bdb222e3b66f8dfd03e3d9ff884a1c2abdf7b4e95eb1a0dc07e73b5110791cb85df33554a20798eebc137468a95368545b836b9b8d07e317533323d1f6e1c93e8eede4399e62388c01b7098e477752320c3f340e18b397fe1ff068d6aa451e102a000030fd2ef82d70624b457bcff8e34ec3a732c3b0004f46bf8c5285e40dff0d837999d41e2cb6357c87a4042d13653b0a8a6012845edde3c64f8f355f1c1a482fb45f17023b76f9f6abdbe557270bc8d031af0c29856205c915163c9784ce6f0e02153251f018207e691c2290dacc444971ddbe8813aaad28065c86b42a9902c76e783cb8373a4f228ca124f3502da097056be94004f778748dc125a698d5bbab0b684ec5d42e4de5b4a2965bc092b0ae008ae7d8e9b2ea18728b157dee77d78532986b0600f31b6f6a9a914434e60da548a2121c0187fd98ab00dd9f3bc5ca2750c7b554ba69289b5ed6f18422c984013b8a91462a8c2994f4d08c3125db1cf23540a30f0600f31aa36fdb7a9146280a24469e84d859b4a21861fec15bd0ec581b78caed083a2c8a3b318567a2a5e2ad4a3b4a9146228b2e3093050adb5d65a3192c863f7bdf506091102b6a74a57ba8e0a7942f626e4099586ba275c0fc4785fcfdaaeabed9205afaedb07ee6eecf56c30d6dbe15aeb77fb6ee8e6be92b4f5a76907961c227f743cf0be0edfaf0aaf8aae0a5c9690baa27badfd015e6c6ff5d93777b8bbb7075554513a15b1ef6bd9d1157c1c161d06f67dc5f75d1190dac48f521bd9bdf8c3daf67bb1699bb48d354b33323694a6c39d57c2b582b782d7fb41c9764f785fce7afca8a75d4c19393ad8cccc0439c12da94ea2477eddbda6f3ef14b472c59ac1feaa8dbf3769ee2467922bf9fd0dc5c97e9f4371b1dfcba0b95b0e851e6dcfab53183d12adfab5aba68bc8918bc831a2490ed670e4a3472eb8aa960d234af94db199a6cd3c989272bb2a438abd9f37336df7cebc19cbaeca60bf9056ae58333dc25ead70c455ab4ad33d92b2f1bd177726141e41419d07de760001795f68de9febe37ddf13590c555affd32740821c2151f1c3080a6b5a916493bcebb1f4a522169395610673bd5ae48949091a461fa020a11e7808fbb837d3454291806128eaccfe2a59415a247d4d017385332a9090ec9050b442ac10511475560755337d912e1577c84df2e95a458a308a4646f9ee557991a0a825a1c49ddb9e9557c811b2a62c4da4f2ab725045ff3491b2f68767a22d7df19e525defa6543f80a5a4d846ec084b1399fcaa4aeb539358b548254284a5ba3d8815e11f5058d3063942ba37278c804421d375a3dc90acacc4e4e4c59ac1465c2d5275579e118a009351592c968a0208101914464631198dd266337ab3aecd342e72c29a94e695987557de0f68b42124271a13b1f809d8880b1bad701b70114ae3fd09b2ac3fbe6badb82f92114c07ebe96f79e2a25aad5ffd487e71af581a3b3133b226fe518b6ad1d7fa3c23b0f445b2a6903be4a56f91928835edb5a2b45be54df28a70916cacafca50d23d5679af4a131216c98ac427eb1aa9e94b5f2acc2bc476916810f9706f366e0d3187bb69af345b49092e4a81e94bc588ab45cebc1e386171add76dbe7d4be7dce41293b08eac9b883aab7b5957a58fd036b37266c6735d2a4c52589ac8fc4c13d97ea271d1bd95686c746f407014e661b6bf341169e298dc551295b44a229383e6c94f4e74a52ff72766c9c4c4a4c4e4e2a27b72b58b694d96eaaebc1fb4a8eabf7a31906d7f1243101fe9e087d874914c9d4f697d4041c9d0d1179aa44086298eac697774ec381af6adbbdfbcb8b4c8bc7670f64d8d6545469ba1a4dc62546ec660602de669ee944547d987ca07b4614cef600c73d0947230941d3485a8b08e37e6a0a832c069fa0037358da3e802fca607f0144d80c7e81f0e03c4595cfe1283ad565a56ac95914f2b40db2222ebc3b268b52795ab32a26259adb4ac465402a02d6db997d166287a95a2491935ef63f4e9ca1da9797510ac832b170cfbb95828ab15aa75db303516166b74a91ca6c6d26aa556645e42e89e37409ffff44910445c57c6f5f25432ede2af7b182d93a273bfe91747d13337551e83c9c05aa793910f94c9348e28140a8542a558cce478bcca1a502c081d83808ed9d0b12174cc87b803a2618cd1662829b798980983b9ad6dcd404243794dd449898139f8d0d429bf699872a7ccb4fdf1f1b13030303030a73be6a4a81353d211e63698181871064c696d900d4a4949494939dd285f893a28251d535ea6a0a4a494b68790905dc29a4376e8af85332dbbca5d5e5a6080d01524c864b2953843263bddb48f883ab4928eb293c868b28b336465b5e2624d4a7a6c8735a18869ba6b3065ac8735cd3206748471a679384df77053ef7014adc36f1a87a7e81c1ea3671ca645f3bca601709618ccb67235c0a32d5db9792593c86cb35c02db299a4801bf95265588354b602f077ce680b21a11d51ff7669e8460ad341dfb412149b1a9c82e0425ac798b76e1a263fc45e7782251b5558c282a369dde31bd63537362dc4a94a8f9d142d78aee83178dc004b4f3ed984f105634cd5974cd6bfabe45dbdc25c661b4f7182df3148dfb4db3388a7e7153bf4ed3339fa93c068399316031a01738163d4c204848b9dd566a6860b1eecb89e1c2a6860616ebbe9b0a0a0a4b28d33a5fd12e62bc45e7d8d4d0c0629d4e4e0c17363534e5e9f9a271ec5ed7dbe9761d53fe22eaa0fc45e4c1db3c485f42140ce6454023a0a66d31cd9f24c7b1151483d1dd8e26f37ebcb8d492942811dae9b659beae22ea78571179f0065d4299cef98ab6398b66f19a76f1161de30cd0437c013a883f402be0d7afbbe8fb17ed5d45cbc0e8fc98dc51748b9b31585189094a808494a8f9d962fe244721ada0594e0c588b9958508cc7eb7a2a2e8f8951390873981a0b8c499ba1a4dc62ba96cefb5c5e6254bc24c0a8dce5a5056606949a324090d0813bd780d0951f47990579062a954aa552ad7a00f9fcec08e221c462e565a6eb8fcf0e201e620d0a7ace00124931b319da4c0e2288a4e898c334ce3bbd9ff5cd13a01d00930005c4dc8bf9f71a73afa428e28c987f37e674df7e13756e251d636e13738b6921ce88295b10dd24a085e883355bc06881c3893af733a14cdfafe81667d134af69166fd135bf96b98bb681d1b8c7e8dc4dfde234ed7da61283c150527800639165f2ebc7bc3535aa969879184c6d05f51da1bea3d4cfec23c623e6f3a36340d6dc11a36341944585a6633f3534a1fe8b36b98b3e79024acea26d5fd1242ab49419ca2d66c2f48949898d24688b3f5b41319fd84fadacd5a3a478b420f4781389b2026e36dd1236a2694df3d3d6052efc74ddf9c517fb8901c188f9fc18ebcf988f4aca0b94172f5e24ebc56ae4c50bd87d7937044da592148ea6a815c780623e319f284a861060ab900b4823211e3dac9035edfde80ef124f260ecd9dc9a7b713777676e8b4b732fcb6fcbefbdf9ddbf2caecc8db930f776d7bc2ef7e55e18bf29bf2abff794ca567e6fbf2e7e3dcff35ce89f26ae675343d362268763211303f3e262766017b6cd43271f1b56898844558810eb53935e2e50473f8aaec904a20ca9a48cad144b88a5726264e47ee48e78e1a74760a8782e5aae56abd572b95c2ed205caebb56f80688a536f1d59d16aaf8a45934074e5c89ad65662b3ddfc49d28455c4f2716f9625f4c253c2f56aa69aa966aa996aa6daca68b512eb0f16abe5224bdfc76ab55ce48bc805496669191959ad56abd5aabcd9293054c8a14b0e6d568f5aadd672b9c8170c0543c573f1a35028144aa3b8a02e927cc16227171707e7c57772729696500c435114455176b37301361627e24493cdf8841a55a20eabf01371e840d319c42e750f7d4c16a609ba410aa26ad963ac62a786f1beded3ee94866e7a73a1bd75210dad26655b18f16db82fcd48ef359972e828dae799ad3da519c18b3befcb259377baa8746a1889e0335093b8d3a26166eb7662a269b69b33174979d26ab55a2532d84d5dc45d359869be60264cb7fc65ce4860b2da1d1a1a599554b891999155ac55451d72361293914c74ac0b93ddbf62b776ef7dadb05819fb3ef2a04bbc018fa0201779b2c10e2020f285f2717f4ab4c98ca484c95eb19a8d5686972b37d128d73ae5249a7c4db73ed32a8f95d87e0293b55e654069b9d030969cc064af58ad16bb8da64d6624256d16ab99b2daade12422456a654dffa28ec9c1560c68d26730d98ca484c95eb1da4bd30d9bc9622d4b5fee6d252f7da42c66ce4860b257ac863b993981cd5eb1db8d261493cd68544a6ea3699392e40426abcd541b8b9594243398ec15ab698d8b743e262526f2f2623b41144080c0c0940441616454bf5abf5a96e60c267bc56a5f59bf5abffa95242630596d16f39ef8f1c3ab5fad5ffd804451ab4ed4a222d98c04267bc56a65d901c166b132f0c8cd72b11c6c260439ac695bcc5ac45ad466af184c5603e976236b242435121308c6445eb3c76edf0199c5728d84a44652fb2cf6c9be1ac989a6dba4c4362329f561b257ac469ba996a64b394a49162e6431ef07b357ec3059ccfb810bf6d294ac77dd2a62100829482100fac741d73e43d3669fc941048e226ec85ad8f4e4028003cf0c1933361c0b990804a149ca17979a981e185acb596ab7a1995d5612001c7866c898993908def03a009fcdcc68b390e5e0491f04c394ccc1d3aad40f38891ee22dda017fd1429c4537e02e9a01afd10b38ed3c4e43e011d017b88cb6f1185da345cf7320ea7957e280d71a7042c0c60586788003846800031650a3956bb958b0ac0f223b6487ec8f96688b5cb4da13cc8561852d16b9162ba4cd82d01a93e85ceaf045af5c6ade42ba5aaf9defe54261b150e46db3d870add6f862b1e15cae140badf39c8e4184a6c073dcd048bc085d817ffa848508498618a24e2e71cc0728486828085af3f018fda202316ed33a3ca777380e6dc473d0b216dae634da7bcdcd59f65bb40b0abc469b91942edaac02ae183d362e710744c3c822295f5c6a62de5263a991fce55ad461a989a9952d9ae52e3ae6a5ae56fcf8785f4ccd4951a7c624a62626a6b43b808058585858584ef7cb57a2ce4b494796db585e58b43883a5b43cb468836c8fb2d461a9f53fad6fe4683d3d339a9222adb5fe77f5e926f988a84352d251bf4493e88b3374598b946225528315d9b9256d064bb2b5943022a53ec0493402dea20df0174de32cba0077d104788d1ec0735a00b769051ca71320a381788cfee10678ad000a4800101610328448912498153e3fe8d88f090b01989cb0289999d0ba13170a50a0ac40eecdde44a26a64548f82f4dcaa409af5a0a4dc30c76263f195972445ab0409d291942bba53414a18a36d2ea36fb64de7e4a001d042fb701abdc36b740c16ed95fac56fe8235e84aec00e3c10a191380574c5c9a162ab4042571f7588567bd281a7a671f875004e014d7174a8807ed001c0830f3b0829c2d1b5a1ad6b42a61b2360bc70a113c3e6db5e1062b4ce65344ece715a87dbf47dae44c7780bcdc369b4cd6bb48bbbe817678141a2f7cbdacc8636bb51040e227298363db499112612526e37161d72706a9f0f3df0b0830e3938b5ef8682d2126a0d001e7a784efbb0830e3938b503c0871e78d841871c9c9ad0516e1d590e23eabc1c46e4c1bbe5208509513596a4360811c3775ad0f4e46c381632116891bd845afbb059e81d8ee3e139ddc371680a9c086dc473e89ee7a06dbcd3e87b981a1867d199f61bba022f421f71da6b38b59394b3d93b9cda91a0dd64182f74728c3ceb6c2a7004058ce851a1b98c0cccc19892205daef33e9a16253e31202272741ba68409f19210b6c8d17a7a6634252c38162c8a90a0d2d58a868264a01e30884092902145ac88f9fce0589439929cced5f44cd942881a48376cd09ac6e1343d63a679dee91916a21e231e011923649e65eec93cdfc41932f7644c99bb9091992153cad0882e2e374433333ed0883268767664dac5658c380b7d739cdeb7699be734ce4bb4ec2d74ce69748cd7e89c447b2f7507a3897a7a9e3fabd1662c2f3ce8a9d166308c78e182dac8ba1392db883afae02a5763b1b19089008b106bc286c08a24c1aca03018d8109b1a7dd242abbcd33311d012b88c361fa34d6034d1044ea2897a7ea3081c44e8d326f03c81973d7f7121baf11a4b491481cf5626b0a29282723382e224c444c386dc6c654da8e486628b80cee60d4d8e2e7a5cb07881833143a2614360446e740913b2535060dc340c18ae160bc66a04060c5a278b820322680114a2082205288c2060eb0c86e0f3c5f8e4e8181fceddf7da5c17f7c585716fee65f18bfbbd327e67fcd65c9adbe2cedc9b6f77576eee5e9edf975f98df1bf3bbf37b2fc3a293cbf93df97ddfa7a37f9ab8df735c62e0ec1b182f5cd8d4d0b498c9ad745927270735f27c3a31725028140a35c6ec98725a605ec61466e978dc11ef4827268bc562b1a01932b28cbca2d5deaca34badcbb22c4962764a2d2c703794db0d050505052565860c9515928e0b89d2641bca86b2a16c281bca56b442f229ca392c56cb459658ad968b7cf948d221c9d7ebf57abd4e64cc78e18129296199a6b9a222fb8c49414c50725bc262b1582c13242a4e4029275c24f982c558e8d5d0089118d749a95229954aa5525dc60c1595bff0c0e852ebb22c4b92989d528b4e8e4b8c9b6d47d95036940d6543d950454599e545a780523ee3914e2c2693c964b259cc8ea893d38242a15028146d4690a25a51ada85654c3952b9f22162c453e3e453e86923aa0d7ebf57ac18076e8f8e41c2017209d56abd56ab96276409d9c169f22968f2586ceb94b8c18df89e972f7bd963f5a4ccc8d89892123b372f05713e9fcd3798c9c171e98942a9552a954aa91989dd57558725a6e46b1502c140bc542b150456390fca24b9473169e4f27460e0a8542a1c6981d534e0b4cb9f2297ad165e9a28974def2183aa5924e914f91cf8f1c12dbd54439ff72ca94132c65aac8c47b0c4d71be154f2505e5669e9894d8484afd1a6d268bc15ea4abc55a8da85223ea2486a0e9e4e37dd7fbbe2b851d34c8b6020082116a158c8400527aeb7804fb630c4d504a6ba5011d830b587082312431861f3ac40a4a69ad55ec2146d83ef3be43bc40299235994c60e882222a50137872a2d2aa12526db5292ad86b6f14f610e369df27dcac847cf1c562f80a83116810c2d0836f05e0bd37e3e09e86c2d0829b3795220c270803083e37ecd5d5a522dff7dd6e0a5c2a954a224fc651afb875064c800109f76a30148196f2d7b5e1de1ec623f0f64a1cf50a4c35a52bf4a506012f813e4731edee69208ce3bdd33f8c38f43e36c3b46f8903b589bc532a6c978dcb9396f7576c4fd37bd35080f7e61a6d268bc15ea4abc55a8da85223ea2486a0a9943fafc3d7528ceba6de152aad56d81780f5d5522c527ab10b82a0369562480e70873bb2893dc41827654febac1e5c7805b586a5a775d6532b3db856cad3eed6b1d03efd7496563eb9ca9e560e4f6fa55a3f9531d7c79317e8fab8b91b125a4d9f9ecbf3f4313fc5ba4a893187dd397572bc8a3eeb56f928f2b03ca358f459f7893ca5ce329e52270ffbb5c8737a4fea174fbf42e371eaf4c34616127d5a3bbdb41048cbe5d274b7724b679ed35bcffa1cf7b5194022955a828577263f1ef3a835a78f25b5e6897bec934cb1ca9edc3a39c94abd3cb1d0269fc3eefc70a78e53e353e59903a7c87cd65327537eda213b75746f7825459afccc283fc79dbafd24772af54c3e678d774be77c178b2c4fe7b1a2ff137d7215151c64cdd11edddc05923da53c33b63f4c50ca73bc95f637f602dddc15bab71f3777839395d82ccbcfec8f92c7ec8f8dcdcf6628b7194ad973baf97c9be570ba399b95b1d94f3efe0add9c15727d989ca43c33b69527597b599e630935daf9e3612425b79d942a25c61cdd2629f978d8b3c843f29e92f29fd843b8496e85941cbfe42625fe35b5664979daae0f4c529e25ec54973efe5a798ecf1f4f2b7d6c7c182c7b7b6601bc825a33e69c7339d68e37edb93c2dd21e5f2b7b4e87759bf651e8deb0ec997cce0f292ef57cda78bae149ba7e5a2a5ae269876cf1a3156f912cae75f17805411647b7285e7cb638d645f11e16da634db2263ea9ab152c6d7dec8f10eacc42a3161a4fea2aea90076f4c06dda9f3a4ae639fbef347d8a9c893cfca3a364be7e836acc42ba82de36feea8c593fa25fee8def071ce20ccf5ba8f8e8e9ac8add3c193abf5931c4f3b59c775d2758fa7d7a3d3f1e9d12997c49dd4c7d7f1b4727a4a53951263ebf9a7b125eab4cad3f3572bd66a75fa8d1d4734dd797ceaf4d4b33e3da52b506dc9e3edcd4ffd549e48ec531eb33ee99b8be3dba9546a3c81cfe1477d2f6a943e47fc93e6a1fb8a9a6e8aeb985f7f1ce75ce2f167a44c40c18ffd953f36dedee9c62915260be0e9f7758735893dd42450a89f4e4f9d15a8d67cdfc33c3e8b620fe0ce654d221f3ce8654ff651d4f1ac871aad89cf30d4a6f0eeb038fbc406412f0ca3a93c756491e25212a5b22651554a8cf6d95aaf6ebc6b12b5a52651aa3b50d5c302582bad2c88a20421a0369b06f09edae86d6c49846528ce477a1a0590044fec950ca4d6c5b3b53bbf73f87b6aa2b73cb38924f7ed1e6a4a71d0e17537dda58108c68bb3a33dd9c4edcacac4788ee38aac2b18eb1419e9ab17ff400a9283f615f2aa7783ae10fbcff37078712572efdd3a4424c9de7717a6ed2f2edaf519297c02a4b43c476c4d5ac40c185a4b7f6b1daab5de5aebbdc7bf7546fd2d4f7b94c20139f257eb74d30914c5b75037fda451170f7ee1290730e77124c9df344f972dc6c69b4ea75bc6e994b1f34f193276fe39becb1acc279ed3417dcee43c8e24797e83a2181bcb9de7943ec7d391a3462e1efc4589278d1af98d35e9f37454a952623c953766462e963d2347953dad8bd73766ce537963668f94d49aae832db12596e70b04411308fe6402499224c1140e40d0757a5923a9cb9ec1c75ee05dac11970d3e05134f96656b259a76143f0b0feaf3c6ccfede1dc5036ad42f78101477c29f333333326662165505486f2feebc2fd76b51a8eb4369ec692ac8d6db34dc92f12626366b9a2626b6c434cd6b9a27a74949e61517f6399a368b3b499553365e99f5190313190a2091dd1da4dd07c64cc3508354535ac2399732e8cee4a675572d8cb8f3b430562d8c2a9dfcaa8531567addb11d6f974a1db6ea34643c41249d108c5e114a034469cadddd9bc2761e15bbbb17e47a426e90ede91974a5d3c20e2b7c6077340a6ec0a1deba9f1e1a64744732e84a57838c2788e40979439406f47a80af3d469a4390d2d0dd5dc6bba34e08d2cd7950d85bf7eef484ba2136e709d9c88c3f556cb2ae0a3437837a423707c48eddfdf480d8a8cad33392f17ea88ce5e915edeea4403c1fbb3b9532e80adeb9a471824820987575c198511d124a77539ccaeee804912892c59dde1da4c2e2f2bb8342284d1a8ef2c95a4acd9d0fdac8366b76bf39977b9b21621d85558237ac211869d4e82ec3e2ccdddd87ee9dde5d1090255020821f23c400e7a442764769ec5cee1847beea7e7da0346068ca39e79c73ce39e79c730e95183398f5b781a45d7a4b8f9ca557cffaac403fb43e526680c6bd756fdd8a3fb0be2a33e0c3bd75675dc6bd75ddd1ee9ebd97567ba43beafe5d90c8c98e4c01a4a1cfceb7016d984c600e8c3ffc9954a7ff7caea0b77c5349a94971a635332e859ad9bcf8a6db77ba9a375793ecad0d79d70f50333fdf2b68aeeeb0b7fc635379a276c7c3748ceff1a9d883e93df87507d975bbeb8eb0356fd0a3ddc35479dc745d67739a1a4ce5aa90eb52a2b9cfe7def24776f74c694eef4ab1ac3c6ad014469b6b91909bab46e599c1d207a414559eb57dd23589aee4e7309b4aa5ce84347a94a685655d35b2b7dc1925994a1d08964aaa69cd112fa135326a4b7ebe8dd294ce13d34f929d7f3daeba9f63e74357f24d3ab466beadc2e8f9f0b2052dd2d8bdd475ef2ea3d69498406dbda503a134e0f36b7e85c2d3a7b9bd300cc3300cc3300cc3300cc3cfe481a178428d2915188a27d49852614a73373daea187347af6a7b3403bdffe801da529dd549e76c7cedff33f9fef27144551144fa28812c5511453a2a812c511510cbfd0641275f0b74d26d305b2f358c71ef960ced5e8e6ea1ae82d3fdb2576be585aa19d5f607992a952bee733d61e42d6cc2f8d1d2e73d1ee7edea04720a82b0f5d839c30da0dfeac3bc0a37e6381b20ddaa69f372ad0c6ba02d9fa938511e78bd867470d027fc41dd3b2613c3bfcf954213bbf7421dfcfe763cd7cbbcfcfc70713d9fbd0cebabe61b44bd89c8a0dcab7f935a95a61ed8fc5a180ec8e9a04ea9535bd877ab4a67794e7bd24dae4fcba23c8e278d42b764ea2b736d05c0e1a94c644434feb019deea702116132d5a40a647336ec2dbfeb0e1e775b18c163ac6fcc6cd33d2c8cf8e2477dee94f6e9e14119bbe433de5833dfc69af9ddc110ac3dbe2ffffbb1b96a85bde5e79a64cd7cda98f935ac696e312361cdfc3ac5682a89b0663e58dea04576ae493b67d5f3ad8fc585cfaf912f4436bdfbad6f18b1a6fba47fb03955be0c1d2cae7bcef93994267c7e362c2e040f3b3e1237f77a297fa6fb703a15756ad82885b8ef42309ee3fe81861ac6d3ebf163dd75ecae3ceb969535ee0dc766e0f3aa581cea670971df33be863571771bdd4e8d6795428cda461263b7c79f1469f78c658d6ed7b839f2f8e3253b2ace88cdb83a762c075cf6a04a22f2d8c767d35d83558ea5c548b6c6e9a4489e9035b12896d587e70d5953a835b2528d42d8b8d1a55cad91956ad4f4285d7fd2f42c5d57bbf319eb291137a7c3045374c0055f68810f6860061c7c246e4e4713474948a20a6340c20d7480835fe3e674d8a0045a08fa410bd620861f38180c2df0c11454d8118334640107d3bd3b7df7486ebcbbb2061ac6ca44597db03e2eb122cb55eadd71d2c84491b32e811bb90a973a0ed39b2377ee1ecfd242d81851a5563edc1c11f7865577fb707314078f650d728584c2271ee3999fffd2b11f5e6fe2fe751d38301df3813c5966a0e290d781236a21ac8d1b44dc1bfe4b2371ab8fad3f40948a3c787b658d5aa4ea82fdf0f95d3aada5dbd7ca2cb3eb3d9b5ddfd96c884ba5d24f5c3aaeb5565c05f05a4b2ded0eab87c15e894fbc7157666b86f8ee93628a2bc26edbd11a6b77b594d274a7620e30b63dbe56f85230d6dbe7fc3de39df1f7ddb3debdafac9e572d2ecf715b4a6ff4e3f78dd5f155fb868ee66c63b3bd9f2ffb74db27bd4f2f54089a7eaef6891c8f7f6ff7771c356a3fe793acaf5d6d524050c8c0e8a6a13b38cb259b9ed293ddfd94f94e572c9acbf7d6d5d453971a8d6ebb1cd5aecbe88198ae288dd5638871a52df8545ce54ee664a5c39d0effe9fc4aa37bce47473e37d7d9ae3c59dbebd9f5205ed1150f674aa9b91a435b6b59c36e4f1701045da159d8dd13405bba5bd2b066f7a127406a3b6990adb2f3c5e133dbcbb0d5e69bd3810341f44685e88dee217aa33ee88d6e1ac40bd8e62aa634f794c66e50676b625c3511f80b7ee10d415d71ec58ea485977b58c241a6d68cb7034bd1badd9ed9b7b63b1a893bfb238fbae94c36e7bc515575c51cab73cc2eeb1eb3e767fd196ae7bf72195da3acf7639c0b55a5c99182fb63bacb5c67049b7ddd50460c6d6a497c76e5acae80ab5c7cf785b6cedb5a5a663cd363f97f93bc1587574f512810553e09460c58e735c6d8adfd9ef2068437ca5a86009e5045413c69e0fbcc5954ca8aeead2814c34beb22671a2713a51dc4981bb32081585cce24abf4735c174f0844f38ee1557b480a5c90474a271c2678edb5d0c3db03c4d654d824e51aa0128a334f434f0c17c734464bb6952f7655db7f7ac4f0c6cefd5d361e38635eb0e1ebbbec72e41bd8d1b2653fd4cfdcd3a847770a9594b9b3dd5a08c2f683c7f73eb8d197bd29ee60ed05a4fa93d4875dd5135dd9e876958b3d61d53b0a3ed466dc3de6041694a2b2599457344ac284d486d7d873f138c96889bab463ec49f7205bb4ed411c25a1b44c6b316d9730328bb814021dd5b7d16ab913e83d0dbf48a9433bd251d40d4b066ad1ed25782f1a453ecfa9b6b2b15bb5a8cadbdb797c0ccae61cd5abf5f6fd68ff5a4f541d912106e784da129dffb9c7faf093485605832995e3279faacde3fea7927dce10e77387c872303dbfbd7c3dd27f84f8fd61c7dc6fa33eca1beea1ef1f542b59667cf7dae07cf1ed34bd55693f72a9aeee99ef0a0e962e95ed4f75567d29a750564dbb6ab5dd7fdc6e66ef4de956777d09be1f5741f778ff72a763df7a00f486d37d779a715c7d394666ada9c3d8fd2223da042044b00123a0111ce6d04465940c10e1b44f1069cca86345801093f8a6ce1099cfa8302149461073930c20f0eb6f50a90dace928fc591ae535414eabdf814bbbb2badd0965c59ef9e90c5423e2ec92a4f932c853e2cd4956d786267a44a58038f8dda540949427b8a7d9d308221fbbecb21a929353f86755cbd8a3a2367007e0328ee529375fc1aace31ae9daac5316bdb16e7a153daf8a5ef79dab3e3b38fb787c300c5dfa03f24141ab81a7901479eca61d1c432ceef4c8689f619afe8cce1a747e467b76fc636b2b139547151ac51dbb674e108137d6dd7963d775b43ca2ee71677c063fcc04104b636bb2ce3a10e32a86a36ed68fc0fb3b0ed4fe4a6bc51eeebff288ba59e539be7bcff87c95c8d3e33d47ddae77a56883b18bc2e268c7ef82d85ceda14b631c34d3f6da82acd0a5eb0e5d816238bc2beb0f1446d4ebf1c7ea9628c23f2ccec8e250f77162269cd8f8f4c44f78176d309e540a1b8f883c3de373943d382847dd7847abda8eb6bad3ec244d5b20360a7bf3b1c59f16a9f6a89fae3c804eb212a8dbb3b2fe1c8ddd59c74cd8dc4f13243e16b238d7f1710f8bfb8e8f87f01216f73a3ef66171b163208b6b1de34d6aa2da6b46fbfcba221b8f9b24c1ef098b73f17cd3ee9d787bd6559eb4329ce5ba8f202ffb8c36fe8ca238a282c86705108f21267e4011640a211657c4e26a9a248f95e086a2ed1db64377b23ccb0d7bec5c79b191d4713da6edbea28eaca4b4e5846d60c3bc1c3ad8a957ba608fc534b5620edf0f6be2f27bc28a43e3f939b1b10b96437758791e51774b4b810421234a80912ac10d43bbfb27ce18bfb3bed7d08aaf1274691eba5d2735dd60dd618ab63b91a7b6bd12476d7b77e9baed4975da58e441a1b677b2ee4fd79d1279c68da312d91e8e8ab4bd9307c14a53f6aaaea7bf93fafc449e7ac5f6bcb30e8a3bdebb8f88395cefc5fe441e18db2b71c0002dbdd5e293ab34c2ee23bceddd862ed66fecf784c5d5edc4f7e37bc2e2e8c6ab12f7b078284c929dab338ca5ee1dd35c8d7bc3f75c464a2c840a329e14098b3ddcdf7777c208a2e8543fa26ed56fae8a4681c5fab3f1432c449a74115f3e12128d1f80c8cdd81c1d9460458f20a740c82adb16a02b1509ba525f8ad99a16406a3b311219b0391df9369749d807079d5868e35f249b2f919cc791247fd3443d61c4816e517cbd5ed7b1b33e6ba886c6f154cce2535ac7ce3a47b747f137d73eebab733e9dc29fc2dfd8cdf21310920bfdd83689f11e04536115e93e6fe8dc8fe338ae76ec3355753efdeafcd4e9e67b7ffabde1d57587bdb9df0ec37cd2a10ef851d4b9bfc575d72dbe3efc9df5ea293df251d37d1a5dad5b5ccbd5c2819e556798c5c1c6fcd36fad2dcfb13ceb4b9f75bfaec22b107954fe3a0ea25bcc74e763a151d4c93ae24190fc8afe474dfe448fb7473797726b7d641c9b9907712a9fc562b14e27af78a74e6fb5708fd64faca263a824ebdce78ad4a91911001000040013170000200c088643224994e4609cabfa1480127286765046980984498ec32892a31842881842083000c68810cc9058011f9904d6919f720418a5292dacea0199710f21a4081ef30a0c3e8261555c1e8b71a67795b067b8b4c5d5aefe72986b450b3c99043b880287552cfd948a599ec4d078a2f5a530baf231ac8ea8d5ecbdc50af1df4445ab6c135c64947e7482497c4776614f739bd78922a700e8b83027dc998cd5f5634eb7e51d8566fe6413c8416ae6032a0b2875c29dd158df9dfbb3ab4602d68fc35bf65cfc41746962980c71c82f33f23efab5d7a89b4877bb0c57875ebd218e4b6dc4b96da2b952c57c4821e3e57055d40c2cfc61afbf0213228767241eaec0a1a31a76c53904e6d12dc60a2e163ddf7c0db127d000cb8534fd44f9816fd910f0bb9c86a16c93dca20e38f10a20bfc443ea6bb48d6241fc06fac856f0357c0fee98300b4b54e4bbc134ee288645c4b1945166be77657f206fd07ecae25a8d94b6db64cae8c719ef8bdbf42f31a7c939c5577cd0e5a9ba28adfa4d4d5de47e3812f9ec04ac5bc63a4d77d3e3e4ea6cfa011ab2a662345b2ea4557ded87ecc761587765edd6035d744e87c12b0ebb75d7d12840c51002ef8a6d03a029668cb66ca10d1d070da1f5764f4416c468716d37da8750fe30619d30df36ea9700f2ef596f072bfad74f9624647f490cf397f25a2d05a90df4bd002b066acec214d3ce30a6d7783ed84a5d48fd2d4a7aff8a5ad8e7db9b22df9cdf54170f6b6811e88969d32b313752c03ac25cf9e68e487569fdea01f7587042fa93c470ffab129f0eac6fddb69b62afe77900fea0ee15ec3410f9fb9afc55598cee67decff885bfc0926a92c2751fa720c8fc6f252293f9c440014f1d2e70f0d667e513308a125498a6ad99d6181b61a4c266331362160d5d8a6b1497c3d5445c59693f2717e825d5c09cd065b9792fbf6adf5b5c0ded870bca73a3acdc0b72a2b41f704a0e3eda9c4628a9a62a8a496e0f03ccb13636658784b5fa4059b8d12ec2e6d2d69b92a7852b4bc4e1154f1ef952082c67220deb9a88146e189fe2933380be3b964f982ea01785b4060214dd9a08b42e4ca6163b40f4e883dcdb6c5e9812c0c689bcf18644b2e9a70c0a58ea64abbc5c6a2706af552e6b6a68066468222258d7432c1f2e5a3c2a2f09380b22b69f9328328108c604853bdc6f28ce990277aa387084e6e98dcc8857692e0e8e58f36c97c24cd44bfe8978463373caeec22ce0ab3283c497912c4bcaae819aa5ab426a2b9bc9f20b36a6fc31a2936847553daaaa349f6a1495af89d86fb5b6a04674681b1f4f22ea51a4af63a41231b2c6b306210e8226965a3521cc86e29d35064371feaea6e9195c678ec8c20be1a60cd9e4fa64ad3a19b6de7158be511a6e1a0c43f7ec9723249ed4ab89326249b6a210ab9c229dc79bd0c5e236bbeff50c9173720e14bb733022b0a87976daf2cc35341ab5c802ff572767cd5844bf0e6986d4f3e8098428fddf704abd9bd50270c0b2a4dbe3192d9a60aa324dfb23190bac19b778226b8d4f79e8ef12debf38891802ffb06604e6219d5b2862e8a87239a2014261ad1581651c52270373b30617f18be1d3ce1d227f007686e52c31a3c4679bd75cfb1110c620dae648f43c0b9869623891a48558b5f01153ce8b120900899862ae4ca7a9ef890381a240d4de7c533f8ceb87c485f58783b9964b940351c7d55c5a394b604a2282daee8feef0f10421ac186b7501fe69eab2dd4732dc23718141795697af0e64e02c5003381f0ddf15d0a1f3b36e986ce7757652dd3a010c0824234cf2faeed7db1dd8c57bb2288e1f7f7898f7326a123dbc037e4690765b8c6cdc60a02d809658d62ccb20a47a41d6c1b4de6db348f1dd9f517ab0d6adbf7e21182b5ccb023ed191f8990057e6952525e9592ebb862e08d59e6a62b29c3f4335b95b2df1545ea8759ec4734b3777a2d35c2eeeac6e78197be7b0d8e912706246252781598b739b62d7c94d8b4a13b2e360758580a71e144e25ce83694525c3fdf24303246556e099d5465fc1f2a031208c795a1f6daff37421ab03408cfae474c803c1098e37be0e61e3ec0c5f05b61eee9410aa42067ebfb97696a03a58771704074b5f8cd139f8a381898c6b62d3bee0041495ccf9cb15448fab7040a4ac7c0192519f5186cf9ff5a6535bdfb5284fcce2cb450c8118c5903739d9934d3cb93845f6160b2408ef6641221d1ae2c24b84e88ae9ec6b281ec7857f0095c6f857392b1b001b9ad12a1df271279c1055847c46e34d7c0d7ca94a1541eaa6c48168281daa1516f817618498ec610b5d4804b50f7df451913075c3e7db95611c7ceaecc75611c5f94923f33f22a365d49c3c78c910c568cb4e566863210e1e03f9ba27be53a56525dd202618dfbc0406e766adf57b3819cb586c9c51ede0649c9c44b70e05f4f1918fa5d50e3ad41519e1577b535b4a4a2df1e638400fb727f53a8ddf51240eb6f62f18be8974082b31a4635fbcbd8eb3dac8e5c9a0b14cba448f4d11aecbab51cbaa62d878132cc583f1457ef4fbaa5340c964bb4f31e68b54b82d3e499717a9d1384e42070c194f6f1b368d829323d00714de5eabf2460d23db8e2850699ac181f0f52dc0e6f962143e50fafec42fd5dfe9b4dabd9e4c084225575c9bf3c9c4e061f69af44c03b78cca4f87678d2270c6bb413de2c942f39c286937082f6189d8871b71d4bba0869a69414b5f04aa63d1e059a4dc545593406109c9d9fb3b54be533d10bc81da692864b5e142ea5a4181f1cd45d3f8aec50b06140862f97998c172244a466a0907a96a05169c0e7042126cd37146be1a0bdcfc092c14a6f3531a3f2e487f3615d0bdb0d9f8ce7d2e8e02e197697fabbe0fab85b259d146c6ac44c1398b07675fd809b53a1151aa8548723bb89adde52c560d1a2616019376155877adb2546c801b9966b318dec5c3c723197907f5fe0008c101643e15f05520824bd403feda5d6efda7dcb2eec6345d21e40f968c697a2b9cd02041ffe882ed6e8788f3c87c66e563870bf13b676d3285a328a6499c7ba45093136a906ad78d79084713b493bc240d464960e888696658ea72d5ff7907c4d8304d5d31b798f5d37cb3551d161c9f62a4b6ddd181eabb87fb66d67a51afb05e63a5c95c62559a3ea44a136ba29f5f570271f6859568833a4f74e3aa3851ea7a1925b472eff182d5c58759492f3476bda7fa7136a7eb605bda528d15e77c608ed8a88216d49ff017db9619fa9eba5d6b9910d1daa2f136a59cff481f9fd9695695938097391dde55102aaa0c19293b93ca9382632fea22236688a226732336b8a8f7d3d0c850f740bc92487fac45a81ecfe90ec1920eef429ae5ee43ac21cd96cd0186a0d8040c71867869e08babfe3a007a0114b6da88db0dbf6a2095e94cd50393bc8f901b01f3f909f01c8d508ceab8396450fdd8a083b762a952ecc4043b49239772d1026dcdc6b0d6170585831dd5661f7c613c299c0b7f048e895070d5faccca7e8612466b63e2754fbe8ab2ad9628554835e5337727045560f299761cd4f9ff137c667445c0676ead42b4da0e61b84f5d46074f83db105bc1a791241c77a668e75c37a433caed0bc2fa642f1ab542055030cfccd55990c5fb3add7d59d66ee414a24f2071a87eb56b91d70ec4466baff7c8dc01812daafab1ada1f64213c39135670668455c769feef1a02c4cda5122c922210a1592314b4742ec2f44312879f8cae9dc9c143b57e9527fa15dcec4f41dff29350b6ae9abca810de2d94b6459ac158e18dffffe7bbe867d57399a483e86a20e51ee07d14f509ed16c4bf8095b4ebfcc16000b3927cda2a9f8e0c4fe9534b3593c09b027acb3bbad1b0c9efdf9f96f48aac585ae0775a7ace40bf4a1db3e79f21bfc9fe13998ea74005796b13690bba9b02c08cb2c6ada6ba1373b04392fa18a97380c39b802d3c98d5b456436bd4ffa4b8046a9c7e9a501bd2527f11f600cfb90d76ee02a805a297b1939cf0d85c2032c6b2cf4592c05518c56ea926a057af372678d7ef8b41cc57393f6cda378656541ca66a6447dedd8447c0f190c63794887dc56b3300d882acd1c9d742b1db00edd4ce450e14c047d327b39b1120490ee69e8e49dbc453438f911677ca1e94fe14a524a0e4fb033ec5cb1c1d6f0f54c489f7b61a7a8134e30c2bdd7809fff93087c08a0db4eeafda9e26adf545706cb0f852c687de206f8a3bdf43b3adb175ef666fe0026d5b4924b75080af9f5e14d37382207fc8ecaa17dfa6625a70f58c4f35a8300737ba7b7202b3bdaacb6e4b22303e6aee1134eaf7a757420df01a0325b89c784c7cbf0ac056728e1cee015ad808712e72572df2df07b9569dea31b86ae4345da758e9507f520832a536aab7d4083db534b14eba0c4631408e63720cf78633af7be966eefd18bd4f6d80a28f3d20742f5662652952bcb08a79ee32f60b5cf5e754ceb4266e596cd4e814c01a744f9ce964dff7d744eb52ea14ac3aada5668cb545587b4d832481203bc15a814aeba447ae8c711de62113b0eefa35c71fcb55883599b194c1d62c280633802e77a46c76d9a50fe68510f5d4c7f982ee9902c14e93f88fbfc1b8a59a206a152d462fb9b69c57c10a4fe4effc2b81dc1ea89e0b1c6feb1aa1776d1eb3a8ba77a4dc1abf412a500b9995c3003329449222821a99c119c32985ea46247fcab7a0401a9221893bc78cfdc1291c5039c3ed9a9aeaaaf2c8eb90222516c604b4dcbf6c34564177572ad839284bacfac637666ee23ff06bd0b0b148b548732bfdda9ed194200981d727128a0718f54a49f1d7aac94d9f0f22058a1b4e1f57349ef48ce1c59a4489b59e31e16ea823731e4edb8314d63dddd81edb8e4261d4859301d1d0edc4b8955e9530b4272ea7d5f4e5bf805fc47e53dab4db9672ebb79f1517b7c7666142494e002aba4b4d32f7b89e9f9d20545c7aee0bbf72a774855761760cf9532ee0fe4091101d8164763409ce906eca65101d2f4ade4ae4b074dd09c1b9e8ccf969652835b1fc6a09e229e010941a4f4f6d778d0c0c1bade16f92d2f3b4803e7426bcb4d87513bfb83b3be72f2429026df50f95d9c28760a1fceca1302deec809bba45f0b1d8b4ece463f9b5f20810f2b26110ce9a3a32aa59d14775a1261e83f4d3076e34f07868d692778a5f733aaeb3f95f5fd613328655343b83c24b3727750295363154144335383566f97cc569e3acf6b0199c360d9d43a212de86d57c1003e55f469c30b58c12d22110de9966da159f0a5192191a1e965cf84cae044829f537621d4bf3fea03331536ba9ce595b50c4876b089c36a49a771cc4e7626ca5e824149123aa6c8f660d477d15870cf91c945b4fc8873be270a9c05641a3b750cdd24cf47c16588b23fa53b5c64b629af9eb38a205fb9b38fdf43ae29fd15f0ac2f9d5f349c5e63c0e4f5fcf1d8b055e5ba5ffceeb895cbf1c2182ed808bcd06495472b7b9f711460e7998e1987b2a365827b146b351bc70f9aba63d8ec765e12738ac66379ca1d6977d5f3a44f31e5848a0327ed2690e119c96b3e0a44e985d3f7fc4a25df0f3622243a4dc5562a8e678d42fd5b40bdeab67e506219bf4a91a54900dab29f2cc1398cbf2ea5ed46b9ba1ae7702e890ac9fc230b61f2de2df379e80d4a7842ab10b9d96759df8b529f3e594b31435ba2ae04115418f3b7e6fe9725864962700eab577f00216b8e5208a88f27dd8cbcea14a41bb02771a7af0e4d0ba7245c1e624859ae330115b3a94f6b24aa3ce039ad52c018ed765d20fcc74bdecbbb7c639bdd11b6701993a9afd59c2f3dd742cbf659a4a3414d58720b7d3bdc9d5c1451f06c6d162a89d52ecf67d7194a3b24516f1f01553396be10eef0f5bf348b9cf98b7522d22f2971856f29aa6d738cbc54151519f16b8d4ec47397229761c32ad779ae020724430508b0b8eb2c12706d45b0a6e474ae795a4b0769248242f0de00eb0944155d8dea29df194ea03e7efd2a387cddb2136dd368ade74e8e611ada2d6a91cacf943d2ab03856973f3a88b15c5b6e7165078e957c0abe961797ace07b2ab59d2346962f50bc1b85f48452cff73a1ae70f4605ee619369d2d2c7bb9f893bde9bf1fbe7884bad2aa03b1558fda81470dd8aaaf5a3c02b8470a879a5846d040a1270d723b58d36785e1c78a620d621aa50ac71d573c77950ff70f68400f85d6623009f199804c018b554429904fa1873ac2cf649827098a95fd9aea551ace6c1a02c4e418658e443368ed5f045c3618315ffc79f1e9488307b59e969a9ac3e8a562218536147ecec19e38d86a1dab8c7ebc65dfa810d540abf4c10d5d5cd93da941de0d836ee899c80cc7d03627dde393743145a7ab635b6e064ec9be4d2c774d11ec4440b437954cbff86ec0054a3c4e7728f9d543dfd65e56e10ef015e491dc1171cdaa0a02344d8c12f58d49a0dbf8513a81026d1061a56008ed0f64ea72907fc890915bff4c7c6db46bd5a8a442f1e432900b264a73d858e7423167e4a6b31ee2b192829607201b8ecba30ee1d4050aa68ea79fcf518b22bf4a710341cf489070a380cd6281de37e8f46ac4d880e12ef65cbe1777ca2dee2944a64464b43c0be34b1f39cebf1ef429eb930f251eb9434319307d81a3a7c340e5d5a962a845cd9f5b652e49367f323db0d6934cb6b081b0e677f027ccb868291864885fc90279833ab750aba84058cb74b603a3caf55952c8640576ea3652ed60984df2da0116dcc730b502d026ee439b8ff1114994a7381e1e6d3ef0b4cc0a420758573869fd7791669b8b289392bad2c39cd236296e5b9252a6594f621c62fef8fd09971e02a15066fbed976d1578e6ba89814d0c6195e0c4cc944cba4d60527e1ce1c439b39d4b06399a97e1732db9ed62769cbe92e11ac9641cda74991652331e44edbd392a860836786d4fa6baa1478ac00380cf91f8af24bba1b7bfa11de250ab38dcfef98efaf02d95ca6a539612296d01cbec011a83591ef17a45e707cda15e2778bb5ac615222249107f102e83442497137c1b887ef2a3cc0b8f8f2ddb15c390a626948e39d0a57c021322ec03214853ccad3441e208e8682907144aa0540997518379f0c0310ef23921149812b585aee70947585fa4d1cba77d761e57106ce6d2d7574e5fdeb3b3710aa6752f6929a7d6386ae79f9ed2cd2260a365000ef8fb0edd0f7d54ea341023707231538350e3a121d6c1b88f7728c0d5d7f5dc1aaa89160bd7d4e59a415d45c736a2ee6091a09e2e31cc988fc5caba444d1ba04deeabb0367bd47593bf2c80e10509b94df31b1963bde3e6a19c811260e80037fcf22cccdc8c5a2b064efeb4b18ee4ad099fd277c8b405ae22de8bf4a8ba6d2062cb1fcbb3689b43f236783a64d641907ce99281f47a918ecbb3180372a0d385edd8a5c4c2a20a3e1a15c75d7a8a69d57b3f649511b72b19e8993c772042e9b20c04f6373f4512569780e2bca5b79c732a2726c9e82e63f7e04d01dcee22bf8252ab12df8582e94cb94556d5addadfc984fef57da751ece9e2e79a06172cc879892dbf4c7ebd1166fd5955154ee655ca604fb30e2cd8a8c6fe126944c3fb6ddc19f92e853fa30c05769915f953811c53db1fc130708dea31212c3fec3b2301ec60167ac867a4629ace9c9be2214ee66de0d5b419583be6d12d649b068119d1c43477a8ed3df2577a7f31ec9994a61c668c33172c11ee084f46b2b075ae03e52b9baf0ce2e2d742758118a6049bbb5de8ee0ce9a95ef967b6ac97ba2b4ef2d24b8b41cf69827c308330e3da9a5a264a2240e9c8dc4146a19b39cf4e0b49ea9534634236f46e0f2e6f16508bfdc40322726f6c9b14c12d0a9d7352ec6861c9f62cdb1fbbdd2dc66c04bae18406b35c9f63f64f0b3dcb305a886016f94ede4f25120de9cff65a8902a2deb126ee057ee1aeca05165ec3f18c731f4388897ca9248c481124d3708a20280f5b38d012813357e0883b7456bb0c70cb572887d24cce3443005fde1da451fb05c3c0068637baad14442ee14cbb7504b80b190741348a775049031133ac7f1a342e9a82ff3ed136847b6f4609016fe900dc18140a46b5c4ea0d1d776ec1df0724b2ac083b0422792d9520cf9d843227de168a05b9c5705e0d0b7cb340225739bda03d6d4cae5225183f03a0942af1fc0e0577a5d104a808332e1b01a4ae6d2196f09739506c302f735e023128d132ebd92970bcc2d65fdb762bb75338623cd183083b488a3f490d7847c63ac9a0cfc46b11168b4997e249a81654e638f3fb5a8abb2fbd34a264326a5cadf5f353d425d302f48ae5fb34c91c16c6a8c549d85d77414348042a5e824affbfe93db707ad35b21c137e0461add58b618f3e236f76108be427f1b88bff0418375a9cf04183a20c036d48df04b8e6371c55a893af76aeaed2807fefb430ba51e3af9855b7fbc6e4afa48707b0f35e9f44a3dea145806abacd25fda783d8ff9c2d67caab507048021cd83f128cc8fd1b52575bad13cd00714b486203b3ef484af0fa96f1e8f7cb38f5f2ba351896685d4e9520552438950fff15aeecf76c05dee3117c1a9e2890c9b78f3ad3915bb13107d2c45555f680437c0a964c8b4c4e3708ecd72085e07ec1c4c467379919f94e44f8b98110516f32e6ad70411592de9ec3b901e3c68ada09de8a551969cb05e8e34d9eb21f618e6e916c89a4eb6445798a7c5aa8a7d71cafaf465c78cc6fab18bdae71df7fd00da1f38837995c8853defeec18af6fa5be6d05e4bd28914def9e026da77ac1e0a5e78dce5cabb4371df91e233d906b52baaf866d1142ec04a20100a1fe8cbd99882c74e59a3a011063bce0d2c17da742e71baef6513e31a2837665967afd1cd0c3432a2d8ecae1b6a27b20375a048079b35f8fff9fd4e0ae5ad055672491f088e9ed52ac004792591d1d66eca1f1914378b3fd7b34409f0143fdaf9e38ef95b895441c26d8f5d9fc763ab84dd6cfd25095bda6c3f13b8de54666797b60d32c6460425f2a3483ab6d56ab2b9a81271ab002462be3b611fb6c3719fc6d3150a200d03c7bd563b2878d266f6b82748f30832c1fda3b7a334c3c2a8026f17fd377279823c7ec7503d8b09cd337e15db435a2590800ae41ab821fe63d40a64e1a32cb4a32bc4366adb00a15f0846f2123616a831360a37753841ba01ba975c9d813e19f266470416edae77f51aec27ea17fca524ccd7ff0634ade693deeba11e3295361709af7046e8bc921f85000c30695a5fed1e5380fcac19244116ccd488973571ca42f4b781f00e3d1154fb1181b6f65b0143a4719df95bb19a594a5f0d6d2c5cd0321a79ffa1b1439290687360bb88621b006eeb2e03e1112c9c7c9af5abc7f10ba066562f1cc51fd1466fa41cdcf1196b3681667396233772a73ddb05d484ca0d87095ffc4fe13cdf1d4212794f35a84f99566c694ac779c0fa5e5e50b6e00bc96b50364f4c0880d886a507b422cd69f1de99f0f9e6963f1946bccead7cf51f9b0bfd601349ca66777998e85596fecd797600a5a4e8561b0ae32c2d055c4bc5f9ad8fad2c15e4b9629c7c95e7d7580b808378ec68e47ffc7a9481ab4b51092b7848a9646dc2ca0f3ff593a9665e99c6f53f984466c32c2706e0bae963105468bdbb28812ff1ceb69e20d57845296482ce8ae4684c442b924230cfdd5acc32acc96d87d0aeb41970e5c832bf70b46187a5a2e92ac450f23ea90a60986ac89b3b28f285cbf386580716a45a9ea33a7a91824df8647036f55a303023e9e0b1acc4061b73a968fcfa74092d53b03ebdf0d40542a5995a9ec0ca604f3aef5a6e11ef166211d32fd5bf4e51a6d5d7e49474f51e89ca598793c2caf3c25c60f94f1632c044762aebc7d4417e1f1bb3f173fba06a3a9632041312a662bee7ee6fb42a50e06657047e69f83e17da145180dde225dccdbaf64c3086eda25326fac28b6fd98e1c64a7af7063700f7f5b2d980f65756cc0f84740ab357fb0c96c47b75bbc7de0ba1227fa86be000a9449b10c1ec7bc3bd37b2d4e49cf81ed2295066956dea49814b57d95430e24d9013ae349ac43dae505f70efc0a1f388669bcfbaed6617037367a58dfc7c9c756357be866db2bdd7b99227a48d866b83694dbaa9dd0caa471790c4da21e483117e1536bbc774613348f492c601be70886618d961c280315db220bb5eca86dc8e717e07953b6f0704a9e3bab9ab7e84ff030a8475808403b42531d78d6768078ac66b68a679ac6e08c81b6331ee4158940800a513e83ab8ceaf83e452a47a53b3638dbf0e7a233e80a55aada9872c686ab654b4034a942131a766776fba80008970c11d91fa923b6cb64797aa01d47c5796cf9764bd9935a0ed9d8f64dcd8842203d2c7b2640dc40b4e8e89f48d9cde7a5a8d35605c5ce1d533c90a5a67380e04d2de8ed4340c0bb528bf1ea8ed047fe9edfdd8a17fb9b15421a9d1e125a274b03cea67a4f89f67e4da51908dfcdd2874f82b77adc289cd5f06776e9363e13fbd55a6c86fec0897e1f1cf40c8705b7b6c0dc0b58e173f2d11ae073225f9ed69c3ef747ab746315041b147183defb5166b428c6e61ff267db2f70bc5a97c78556da89da2709ed002e9906b6f59deeffd1587d1334a523078c30039bd21a5e204b293bb968bf4f63624495729a2e6c781382f6f17ad7c71885104650ea4df3ca05d7269d24ed9b3d3d427d3cec949df6a5e62f8510626554c607f2acacc4cbc95c12573a08ea899d93c5ecb61a5b793332ff880ab4c12178532d2776478750b34cdeb68765288fbe9fa1d6ebb65beb8bd7bfbcab4d115c1c7ac53d66c5f08dd82be460fbe4e7a1b9d5e0ea317f7b121e2b7c50d0e55087165bacb7b40ecc7ce9c39b8334d8fc1b0a48b73fcb2a494ca990bc5eba90ca988cc21e4aa396b5253b9a59109adb1c821b26eb182abfbf8f984989c1c1a20ac3be7e02aae837fc4dc3d4c7c99cec06d8961fa37d1bc0960fa3715115128be7b4df9fda00c3b1a386cf7ad17622182e5a93d6eea03e04e4edf73bd8a1a89b37dec65f725e799e306292848a3f70fe231525f583ac80767e89384e6a3ad642c3beb0e2087cc62a5227bee0436bc81d34ede2bf057c923d33a92ad36707db8ef90ed0dec47cdb4e9b842eed15ca8f587df86615fcf49d96ae83bb7b5ac6810c055cc850b7aa66427bd876986392dd86e611d3f6e44a5dcc979236977f999b0c10f4358cf4e6edfd7bea0100039b108960715c73ac783db1b6991fb9fe1a5907a3b24571b1bd71d9f7bdc0c7628e1fbae4161963de7a74a5d1a63f0d00f02a8a5eb333f495e676a777c4c88f4e6729455d846f1b210420bf4c9850426f668d9212b77c3d7d9fed72433ddd630594e3ae2263e1d5a2b445b3ce0eeb6d609ac199a8b79e0da919d5b39934d03e75f44b738f5d2fd43c521ff0eba1cef879655ac5b1cbe7a420bedaa325bdaaf1902c360c28b99f603937a99365bdf830471162e9a147465a6d659e48fa03976c4858ae41e41011554b072c9a4cadc13ff3655eba7c7b0791717352ca450f9e85c23c5c1df66d736ff9b949734c0bccd035f77a8739ac16a07f339caea527d73d387ff2980231f7937e1d94c1a75d8037e3b578353f99bcf5af766645ddb36704213b54d51b52d7e5717f1b12b03b12a14becfe0702b20b80cb182bbf92a016641ea4a3ac7057a801a8a60669294c4a2fce56b8f8f48ac48eca0a201858d84af1fb58c2a5df01c0ef12e208c60e97c7bfbbc3f693b6784bbf0ff3036c2ecb53a510d817518f5293393da49f6202ab0f5a129c2db88c74baa1251591b8cee3e576527fe90a7a84e7fb06e9e357a0b1ca826a4fc32302216a6f847b97c9fa18671b46ac26aca8d8ed60529e85e385a462add37ece8be009443977ae7a028d87959284f285e360712b39b5485f7092e31b3ff13a0a4370a269ddf268b55daec6f73472f4523b87f5a22928adc39632c42b4461cb681597912ba77208cda606914cc69e0bb3fb476d6dc36ad50eb01fabc1a0b6114cacee653e400ed310ce2810373274caeb94d55a3cfcd738ce7d4da928595c6feb22e39f7234e9ebde920dcda3fbc4817fed71c14fae6e00685c5f58b07201faa6177b592a582a9137758a1b0aa4eea6842a3fa0dc94ae853d4f355c7695d97965f56ebca3aef956728fe0c73deccb7f3de0c03e7d9185fe71e5c98c340bac9108b283f49860657e626a36e61425d12e0e29af7f2d2fdf1bf015eb8d257e3f07c61ea56f632d901a08ffd2f052504e63d932b4c6c7746afef771ffd6cab95408db7e8f737c8ab90d57eca5e5c458e2929ca1271dcd6a579293f5495a8a6674b0633e70584330670da471ea99e8504a3fe00b679c9cc73e7939771f0fd805d0b1ddf906ce1f36c1b728e6fa239d484bfca86143dec718768fe48a8151c3197c44d448c8bbda3767849941c2d89723f5d1635474ba274ac4b4262cacb2fe388be5c18c0aaada2f16653e3eadbb0bf51ec20e846f16d537f427998f6bc8568e12e1c28d1d2566bcab999f6aae6e44fd1988e8b633586291e7115670a9a0cecad7a799f2206edb5c2db0875e9b02ea0c483ed5a2de15e07146d437816e3f56d40781c2154309d5cf5444d94cbff0d809692bf5b60fd3cb587b0f73b9ec51e45ac9b7ed42350998ff78e96a3bf21c71ab1a07375bfe31cd357db101ab55d0cb6a948a26606b7805444d54111f73bfc826a76e9d7dba4afd8afb48d20ab356582ca71a617e100591bace84ea52b9e90b02ce8a9c2da62411bdf047ba9a546d072d71533455c1f1f9553e64856e7c17e3423a8056b234654764baed26a484a9809819f6393fbc5e50f50531bb478ee01f5f94e6560746ee0a371b7076c24ac5b8a07c9dabb5393f50a534566aad223c2c09b05c878f3a447f4bc8cc9bdd9fb8f909f9e177bceb5778b664e6480865808a076e8f6fb354e5c5b3af473f7aa73da63238ac3e7f65f13cadd26926ef076c0123c9757427a42dccca7d7f3715b32c2a821efbda6a02b3352940b66058d0457d671b9f9242041cd5f267d59b76b18fe30f6973dad5b3be267db53c24e323c2a681948ba2e630e16591d20e4a7ef1a207591df5590b9d7364b9a2108964a7d4fc86e1c2aa5a658adc1e0a5ccd7594a8997a36884793757d6452ca302ba23b25c3f0a37a5e379ac33a67a90928c9c82fff695b3876d4a4dfec49bb3ea7514fee314599f2cb79fd9f70e9e4810424292a2109a716c103ffb6b3534b4ec8d0e5fb981729102ad54bf56b0a89dac85dfe7b22d7d850b1b997430139e30ba12625c749a30d1ca96ad924fda0bdc8344ba0bc201d59f507d8dd02b610aa07a809c3a1d960661f2d06d7c79c8772508d5825ac675c018d58f709709571847e6339e0b8a835ca018cd4d5351063a6584b21f56d0e1070a05b89884e2da167012c8e81801c02cad731752d20077213f04440b2c04e52e24a06186edb522c8432e64a20896d28fe4ea0d385c241e367a511f35c5fce0d4fa8285be9a13075e04b07f539df1d97ebeb9034c7ad227c747cbeb70e81ac845efc850f072b893571f523c3a8aef333ab81932a3358ad633744fcb2a17c13b10304d7a17d956c69dd6bc7f3eb0709562e2fcf6ac490f53c71eea5586e18286c95d95f10d4e1edfa1ed42c967bc0a601b3a633415ea41f8e55cf7a89859dd6bcf026cfb56af62cfbbf3ed80ccbcbf704eba0e66e0db7b5207ae47e5d092a81d5906cac2d4929813d1c0082664cbf427e4ed6c4de002c9b46d07b6ba067b01d4fb43415255e3cd05fa095f9b90b920ae4d9b6cf0b7661ea1bd1aeb9221d992309b136a72b88a3768b616e5bfdd7de1dc0a9d359838eb2ca60b9eb197178c9878ee93862b46ed0926d76ca46ac183d9a60b663e748e39b07b18c3037010e7e468118c5cb545adef750dfb7018f28b30f0650737b5a36ad33bde0517c0559bd718c775ae8240e1e505de4d599f1d68a4b16aa604fec6a213672d455d1c749617585f3b275e6151a451c4ba05f669c946aaed09191d2a6334eab87a90f4d9d8da0c5a86d2b80a566220950eda262ed7067ae7b49009a8c89f576b2f10857419c8c51728f3be17485fe717aca93446020b9dda356762f64d9d12b9be0345fe5e90c918e4670cb82702c07f56fc56167c9e3d83155fa06601fb0ce50bdce74707062845aca71cd53bc2bd1a1958be20a4d48bb8c99710acc2ab85e577820416213c8d32f815c3abf923b6ce5c48a971d755f9a05f907c464f7815c0407fc1bdf6c8ad3f2bda88ce5cfde7eb4cf19d64478b565875ef2e73b5d0fd0b9441083740ddcb013008b48fa91ebb3ec567b1b22ec8601d6010eac40ebaae8ca5da8081c61627567137c1fbc0e7896a72989efe4b7dde46ef2bbcf3f4a8acde232eeb063fd88ec89114a4919ef5986ccde786725721202df58f6236d4150c7cb863fa6468aa2c00c2e1391e0c88f8eea1e3e17530fd8203f357aa8323cecf54c1f0dde365c71fa977ddad090319dcc536652e0b2bc8bc3cf6ad9f721c296c292746e2df17260cf4bb473576f89c734ec583887f38c87a9733cda84b8a5c9b6bdb247f4a5adb3e8641b87aa5ba58aea252f00e5b4ab39c7a0c834d6208d3f2b6846a03cfe8461c08387570360c7680214c8bf712aa0d9e45b6c4508c3b3db69c4ef2d4c31b8aa8e90e833f8ba8686f2646515c33a3461a9579b086ef306855fac15041cf811830f26282bf3054a3a79873b6ee9c90e2ad0d2baada3cc392e73df71a22e51d7a1e88c1d7b57e82004a383f30c311f2cc904fe1af0dab0ad456c4e0fab74b2175850a16b47e2b523fd2a7b072065fad39e18091c87e2c84174a0cb21a965d6123ec175162a055dffe024a0dbfe50e3106d416014e3198d064d5301273c38a017471d1b4fb8a22dca7648b90bc5c0deb19e5f49f1dde1be21783a552c5180c5e7d03bb8c41b76b4b41637008345cc0852376cbc83377b805e9693d06faaeff2fcf90445067f93f6bc4a25f4774373e9b40558f01623d2ab69797c0405d8e40be91aaf7de00476c45ec1f03234bbdc9f8d4f20467159293703b0d46a842fdc414d3313682d69fb020644031f75bf77434b2a1c0daf2f10770460615334439d0fa1f19d48c7fd5597fb6a74fa22abcf881c5703cd6d906cba6c3e6dad2fff9d46293082656ec2b5e9201290c1d7887be0f28c9e08600970cf46a097b0d45a68b555c5666d04a9d4333d8e3f916991ed77f2b64ffe1835633c867c5ad814c2d3acd00aa6935866d1370369a3a01fe3cb1a386939666669d849f3c7a85202e4a2390308f90bfe30c0db8d98a14aea7780d281bc019646b10326aab7b82ccddd6f3f1c0bfc019dc0897fea9a54310bc4ca5658f7e43a8207e86a50cbe98abd385dcafaffffbc09c9d8a07a094704b0d1d94e9c1c5d04a192ce1a07c208b2a99686690a10356f9997cd1061cb27193c6bbf3d04afe99ca06525f30d552edb3b10c1c54ea1c8774ecbc700e257c193832e319fcff687688e602f61403ecb385c2edad26340345644fd29bf9730b57dab0b49216e849b55a2a493f612143618f412129daa280734e11449f10d0b6debc9c44507c6f68c4ecc666684957635f69a6f1838c2791a44a5a53006d27364393b8807dd1f803b0b187d9a305e60c08370080c11aeab0138ba027e70e0d46ee2d857e3d03df06c68307c508578778872460ce73a26f80797e9c24a087649cee9c3eab937c067aa5babf26135a130d9bcc11ca8e3648fca2bec243e4ac07a98bbf15d3bc6252257ba143448a1ea26fdc1da2180b881bac0190737f673507b05d0e6263acf65a0f5c70b7cecfa031b11241dfad57f86750d449118237bbdcc45112ad1340fd6c6600810603111e9e668cda4a5b7fb4c837edebe0de90cfef21fa3b1f10d57aa334574f228128bdf0eadf725424dd743db65ace2bdb1707926e08713d62ac009ddb438af2a0ddfb21a22efaca58536616c5f1219b84aeb4b9046bb4dcf97ae8153e5aa627e811434c19123efb365006c148efddd3bbe048071053b8b7488b037fa18443ff7ed3a13a8242e501794b587f59606cac2df10d3ffc8776166646ad7b2a201c6a2d9c25a20252238355aefca56faae146b1749c354ec7a0ad06458f3cb4474daa856a0ab0844f0a49133af7919da3efea370bc401be9fa16455fe520e0e9f7d957f90fa124110ad1a4a21c7cff050a03f060a8f8661349b098b64028e97d05290a5f3d99657de25d4e6a4d5939bdf4ad1dc14b4dd7f312f000cf8f7e4049fc47e63643ee3254afda6f3ff4507781573c4e6edc720ca539b22cdaed6f46aa0ef0b2990c1322adb7be9e9203a64b0880f5a40f2fba0154206840e4c6e405f6fae38c0ee0c1fd44157ecd652a27733f02ddd7c1d74450950b53fbac380a6f824233240a3e4646e11f141e92a15870919c8ca900c40534ae5b9307b4cc2c28b9141c0e6a6ac3c0cd97645b92dae25f09efdd20cea049294f3d12b845bb94c879b1afd9ef124ad27872d9f16d667390fa10a0495bbc31f40d21f180d4bc22421e840077cb3a0e504602144360c701e7fe029459055a6c45afe40754dff120481fc749ce7d0bc42847f30ce5e6f4d06ae62f05cd544406160db5cebc639bc0ac7333d962b3a7c28f03a57f6e4ed5660e5c9529301c1ef4f2b709363eb9d409feb6450364fb841c291f50a84115cf28d5a47d5887f6ab1e55b1842207b0602eea14f58c267906c37c67aa02f37e766d04f43c435a3f900eece8c58876cc47d6ad5f2ad3e81a4f2a7fabf9e008a06c2cdb7006e95e36901f6dd5f79e88cab20638bcf5f4493f2c0b2f435bdcefdcbde921c07d5ddaca85a6490df3ff4cc780ac9df9b48d4c772641990183276646760ccd877a206ad4481e420205e82801eb4858a39c9f3f6d477c6084bb341d1b648a0c148ac02065f7dcd44c83250c24eb555e8bf885519dc2b860cf4e0c83616ffce01b562f015d1a746ad2a4f5859061634f96484eb5b0f45a4eae98069ec6e135eae47f83b84f6498581bfea917f59dfe932e893876af5a591e0a0e063eed8f8c195568f7b4a98610f8e181bc2802cf7e57b4438e06f961afa5fba7ab47c884c0a33e012159055fc15209c64550c3d4478f11e2dd93ccd54ac7fdf2aa584e299e140964c0710969da706cf512a88b7cc189fb92cec2d9be6050920dc1cd0beff6658b32df0a2ab1e86c209c7f43ddc5d4e9303ea49d8a8f4ca0521bb5225b5907aa7b2ef15ba9adb80e0660634c9074527967c0101567c9c871119bc13888f936f2d0ac2116b9e5fe21402453380f5b10272ba0193b5ff7aaa863c788253c58c54093ab4ad1cb362ede86a01338707cec64a90953e3230df648d94df6f089f44b7008f01e9ed9d1b2eefe37b2a13dc0cb36bb182cef0974faf589e586d0b39ac905517de5f3daf71fa871a607efcc73b72ff2d5f8bac0c84014d73794a5b475c662f557f539f9908b23a1738a173c3c652979501a57c05831a3d65a51aeeb358a29d7b90c25d1428dc91dd1cbb109a32ed6de18836f81c06775064365f5a06b175d8fdb317f8c17dd89ed4a700886d3f59c4a2979b623f1193758f8681f5d5f8437e6861cebea22ef657fb4798dccc05669091133340bd6375062f03d258f259110cdf13777cf661b40c12a9c23998159e2468bfc3caeb6a232f83ff1022342841c851273240cc0084eb27a3021a791e70ed6a4a593a4299eeb24fa4f3c6b85202f47283825a9519047636c5577208f2aa1e0846f0ae000060b01e47623383491d7c9ac6ad412efd4fb7d186ead944bb0bd7dde35638f0c34e47cdc232ccc06db36efed21b076aeeb259fc325a2b4c984164842430a627d6ec8308ff0cee4791425c0181065f83ac4d25a312f0418a2cf96b3abdc631531ec89a7a20d6e21489b59b89c20d5e8eac43e60eeb61dc9d08a8bdd7b92aa9421fa7e603d08032f1c655990e31bb6bf65644ced5774dd86216b218a8640b70db4e02a1f83a8ad753702d7caa11e5eb29b886b2d711a0c1e7db04a03a460ea08150c6b535515a784029b7cf491404b112125f4f92151c2f197ece326464045b0ebe7102346878ea1d112ec17959230eea2019e476637beace82069f9fdb6c4c98bca2941de0d6b4eb58818206d229248944581850622004a39e0e5a41035b6310077d7f2712507fe2b2f802547280b6bc9aba9f82063abc25e2222801e4580de96060ad44300c4b1d1d5d7d1d5b04f42d476ea8b24d4e48969090b87cbb59d0207d814bcda1e5e1969abe101ad08a0936b18b7808f20e88d3caf217d854498977bbd41448b18a1f3f81345aaafe85aa9af66339b093d2a786fd671f62895c43039aa374f3d39f59ed1f65a7536969d2a48c0c1d13c307bd6d684035012103101d513fcc260c1cc86d018a826ca1da288a32f7a8b1470a93d98c47df93bae4e51e29c9369e7f3c8022d0dc433aba5762cddda3c34c3a8b0aa242960020772f57eb9d7d6ef98c0e71919368f053f9c581935b9f44c7867fe058f4f4f5e8ebebb022004ea54a086e207c508f8eae4e3b023996c3b52a2a2a4cfc070f40ef4dc948c2f621d1a0cb8e7719836f2a4b308b602342896ed06559fad5ba68405bce26d7b643058b8b79a22b8823e42eb51ac3211d3305fa1c433ace314c4f47e7565f34e07117788f55957b52c117cca74825110bb8e7303685441b80150c363e1092e326b35e17c6c524e486949918f3e57b7cf018627016c4981d28efcd9fc36c0a947a0ccccce4f4b8c89279cdd80d9eba069721430b94f04a75473e25254dfa0cc08e630e8e0644e194769c964d5ad3a2e2aa1c0d36ecb7ead5066f6b1628d76ddc1c0d36c1a634bc17a808630fae36561ab21368b74a3391f21fafcdde242630eda6794d08a93d19e3a7ad3d65cb74b68d0d8c1918813d17515a78bedb8428d3a019509531e7c41bd6ee1309268d276e670a41708c4d888f8893fb8d30c803954e396da5194228ce7a3583494801b5028ad8b5a0d122074899cc29fc791f9e7b803c37b319d50cf00a1d2b056f358972b834140d0b60d566b0ee33100a3616f04985fab5194c29836e06904db36cc24017db76d62c7d055b475291a65727aba1ad6e06d64673cda2d0f5ec01f5723696ba19c47cda373475fdd60cff990491cf4b17b81936f737216b4edd7cc02e88b09e510604513dab0ca8c41b7206f4df984a2297874c53cfb871cab16cb55a0be8d2dd7e9cd48e25c1a3c17136b2661a47a766e6ce18bb70e1f365f4a293da68a04ecfe0eed23b883e31f2cebd260eba022abd150854a4f73e0bba5f9965c4908a97def596398e9799fb276ef6103f97604cf8b0840821cbec1887eb135f20e8a3146c301741ff0bd721d5e896fcd305aad3a92d19988d9206c13b1483e372162bd8ba01352a57153c5528a38f927c8002e362a7e0f8fefe09063fda5c626874e1b4f28850ed7740c1b8042a9044b77355cf8c8875f42aff39e9ef1a77f462136dfc46aca3159d2a4bc6c504a83fbab9ea8afd2dbbd03e2f51f389360e25a9d2c3e82bd0388eb0157685ee89c626552f1b19e98ad53855a405695ed1403af9a08902ae4e9773ff257c1a964e9d7edecd4a2a801d79581afc192d1458738dda861f032b4d9c9ef848dc3814793ecf3867fec23f0176ad16dce55998487ebd891c3767017ab1e5d016df56fa659c5eb16ab8fb54aaa04522faa53aa8a62adfe58627b224deb508aad226265d5ab199a57ad5c32dd8fd802ebcf2ab05257ddbda381d25fb0c9ef05738857df4f3fe8fa0f2140f3a8bf75a03ff4127e7219d09e3e22e78cd43da5a20f37e79d53ce43bf55b5dfc8a3fcc43aeda6c44238b46794f3408a890fbb04437bd479d2a121d0878dda5192e7e18eb889ddab886f41c529be6b5c1a55492501c6a0fd3e2a214e7e83d8474dcd690aee46c0de9ed93ac0bb66bb977c994de1e2efde11cba3b49995679bae8c2f523f72cbeece59ac96f27a799f8ece69ec1cb9e5cb3f9ede4348b87fddc3378599b02db2973265807effd221a8fcf7170dfde93cfdd8e71faa1b716f06dbc0137225dd3c80f8715b04fa2cce2b980c3a5c386a65e5eedc5392d16dfd4d702f93f23bf05f8b0f6e3a66d672df28e1b06a5b120208511476b460c37451b16fb12e6cebdfafdda4054d7715490ea3f9eeed21bd8b0f426fd589e5b23ed28e6e405a3e6bc410d340e362cc3fe3b799754e8c7d62f8d7619ae71089adac2badf5c7f114cf8be6350e4e67f015ab90a832ba551e3ac9485262fc7218d5053f4953af72a6eb197c6a02455f9e66082a4a8e3a020a5d717d73fb13a9118a991867e45541147137a76d1fc722f48c452bea1112567e8588e0674e017fd6596eb12e4f6663ba3583fa029531acd7fca446d1f1c9ade2c40790277d66094bbb8e30ca306f1c00a61679c2d4c52f4bd15747d582080995c75ff355e94a3ad9d45684efd22da5decfabf7dccb452bd29aeb36066baadd720d94da640e4924d0864bb586e8cd359eec8771ce764d957c9155975a0a6047fc6727714f90fb8146b042890055840def4e5a0af729dc4d4a20ef7a3a6745c5552c836372c14de373f6c2d11947cd615378d8992bcd3ca8cc1c96ab055fb6ef48de883c9af4a561fc117d109d10353a51d155b03c34e47613248a96d10d585275e37567187770aadb75ae69d159616bf5c21d8e93685cf3559c6b26144ae5e88400696e18efcd3643edcd080ce480c2c37974b06e32bea3ae1e4298a5f2b9d8b4a61da7cd56f7a60aa36c53e697cb46fe09e0b85403db0a87fea22cb4b4f1ae409f5cc54af02214b98a9e33597b694abd5a571835dca177c0a2f13ea297c5177b3bd025cb2bd32f88f3df02aaa30a9a5603e9d5e04ab930b5754f3d803176b6d98987d5460c537287e7c0dd15798ef781e5acbca1a6f77c64978b2fe572963fa2a27bc45a91b89214b5991c7a7083fe96a299e5f593af689744c5eb3149ca5ccdf3aaa46ec205dc6a18ba91681942fa04286b621947b7a6ac5861ec83d4817dfa2e7c15d8cee6c0ea04b8e3884922e7643cd1a3e5168957a5ed838bfd36f1c38dfcb75b6522dd049e962ddeb664160b3e75409a30da7a32f8281928ea702cf62066ae8a678bf61d026cce808db32fb309f3a54d90f884d71d17252f4ef69f5dd8da18dde6455c3f1937b337a88ea1547f84d38361defe14f0b94998c0800d8a78cd5ec7c26a5e03a2e49dc8cd4c42abbfd37cd97c4db7e9884e295e8e554921b7151b214a1a4d2ea5b445be9d9c096c254c6d5af96c0841d40408ac20492849f777274ef0f8d9a4f55c651a11864a928ad7cc450222dff625711cfcab8486d17669c605850b29122ef832b43f99aad6c0d60428ec885f7331f1c75150b6f95fe05aecafc57f4f8f199e41788ed821203663229e8a24931fbeca879ad7b4fe6814f5a35920abeec8f74388534c391e8c5f45d8e19bd4f7944455383d3acc104f94893c92b33e61378b07a84caaf29947ba029ef7f27b5d0f87881c050668e24711dc89fa3558f8ecbbfcc093c0f71f3f80f4a8e821d8dcf0e1eee8c6da0ec3fa04945e4f3605a14de67fadc234a54d8d0ebad37170308e1cb3180a0183976908492fff42f154af6ca8d323293a23b8098ddf91e779f3254213132fb3ff66b121a0ffd4bb9f690a099371026d10bc6135b3fcddf006b684fc48f46c971155a0b42561121cddbfbf454b8f3bb09e580a223ddbe431464dd3044d63f20ab7f3a9291c10418c83dcb652c143e614434104bdd4a3ac8961d3c29a12fe8096920a7b48879e7645acb898c53dfb0a280d95533cda30d486823e3bc3d9292a3d1df5f30c23b64e16f1cd565a492da5fb176861032c02229f936b01cc79292576a4352ec9e3dc267463a2d7db62a26f1a60f183928b30d14db876b19fd9820d874a94c5776f170c59be62d56da0bb93dda4eca039b14474c9d820dc9966ab0c1d1c1036645d4be8dbd7d7f6013369898e646275375ee16b2925f06f311b15d1d002283b3d71c4dc0043fa22154380be3322ab2aa01c45deb72d41b6c7106b4ca3dda592b15ad3e71820110816dc9ddb75e462df9f5c698d5b121dccdda98e5c75c5cfb45bba5ee1eac18ceb951bcd0ccf13230c1d0721e7e893598f2eb590a5b8bfd568be383c0bec39ac8025fd28f0dcd4132e13047958f0c84b1381a1afd4ff0e60620eeeaf2ebb9c10b9fbbf82959f0f5d03e96934082686164e8e8d7bc2297620c16624470bc780731583f255f482efffbdc466b5001e8cd3b84c29980cbcaf33cc11f2a791c2363507ec2f78360cb003b048a4a16abb640a368c1d0be87f106490416748f80322c67d1be5f4f282a7f7ddda3920b48653fa799f8249564ed29510756a9f8158c69201c60977a34cfac10d79b3e9f187b331ef20a5810864483af7cfacb34f5a786965e4b01a9e3d2a408c9339071c89a4a0e147b62bf6f0003110a5b657eba90dcb3130367218645f81db6cbd7e41980fe53c9f792d26dcf08346903f337eec10b814afe4a580f187d8af327b83100206f5e468a806e5e471f2b49aa0ab14ff54fceac09c9ae7f72076a2943815a903affc2e1c154255b0767080d5b3f536edaf0ecce65fb910990d6e2bf3503427e29176b6816a492a0a5294955b2b773605ff3ef9097476e1881eab90f5d043904910161d63635a6218079a6bb1c456a3f4877765b2637f6c2085b379fbb7e2acb1c9b3fd0095d4f78e26e5ac1dd4979283061542ce26388031f54a8917cae4683bba7ccac18bf0710fd02c8b72bec861878064ef18cb60934f1261c85c32945081315717356c9b1e5ea101c74e03063b11ac0dd578c3fb58fb40af25437a08ab25709ceb93c2827289116ae0c5af0e0f44085db37879ec2f306340a18d0e8682b915c252f3ae68a465e255fbeb18c530576eb8acee815ce1af3a1c2abf874fed46e9d51d90f602425f24fd5c06bcc4b8a3db7dc1be7b4bc756379777bf822e53fae7782520b3a6f5f5f99e6a3edfb465b6e86826b63ed8f1cb216b8cdd0e909b273176a9367fa759a43debd3d10ad2ca3deb7e6b9a315bdc110c92c84913baee47856e4907259d271e9fa86301e01648718671aef63df04e2057e9debc1e97d56255f484457f283b77aced5fd3e20fb4d0251a8a99e5f519cc1b61ca0471dcb4d9c136331407c6be09008fedc90dd8e0f9391e311ed37ad25c3f9fddf52758465a1848d0f2241790514a18ad3f9aa75a582007b32d3467b8eba60fbe6dfbe3c765fb03e7579294426d02e862575eb76c8d9783744cb4ec2c4c28d7c7b16cb1b15f9bd1ae6452b5308e2f9694f8481b9db635fc904b5f05edacc9efe1bc72e16209cd16516b1dbda88867de5725a82d99aaa7d5c76ccaadf162813df80db771fe682b592948356983551615b3307e09250a0a29863961c0fc0b959b21ecb3495f6fcb139589afe8c08735b774b8bf031f79a8e697589b0230c713964a0a4520a98478d999aabe372c2dc724ea2347f77d7cbb4f0d9ff75189ae10c019d0063645de371d672d43a55ce0495de17a87f0427b79b1c66e37045d292cf858b830e4f662037cbdbf68cdc13032fe26adbca9747dfc0c84d748fdeb00fe383fa739a271637b6438224a2245d3a0ca1d3f3c727d8b1069b1cca533274341797ed101d20fd41210a989f701022429755453f8778c137cd328d0283da3cc4d1ea0bfb872c591fc6b8a1d48c75f0e51e49f9263431c96f53641c5c65c97403881456880e5a3cd573423f853fa80a5eafd98a73569cef1cc9e744494bf17f04791ece487e3d4634509571a797ce178a86958ae5979fc9c87a0ff98537b02e163f27b82c5050165133d3a63b52e7b17f620638a8120edfa93051222cb998816a378ce58c4dd04257b5afaca10233237932425d21d9e4e8bb4f311cf47842e8aef6b9d146470b7aeddcfc34ab36b12547e78c4dd083707fc3e3d4e584c54cdbb6e483a25ce2721cb884536ac71bca1d6dbc9e36f50a4b1a6ebad7165af8bd392586083f92495c22c719fcc40a00e67367a0d9e77a294884b76451cd00ff515a6de76553498c0308afd4956502159e919df0962c70fd057844d9a3f00c5b8d7a6ad4a8d5cf33e181a28958266d6a9c5141890a317b4b46ba9f34c5603b9551cea3ccc1b959be375df7a70cc4c770a1bea4d486cfaa5d508d7e4baeaae92f992858f5560e2f326ff060ac09daa56a6f3ecaa65fefdb057b866625d100fe3efb39e114bfdac7126b0be9ac0132f20036e551053eac3e0e74655a07fe3e2bf56f8b43e0d751996c932943b89ffa5c4fd966a7f8463b1ea0fd8a5303c53749d245305162c1c0ad68ddd8b8046c782c701c15ac2bd6126bd3c68e05f2aa558eb858f272e7742b7558ec9c90187477aac49ba691c33ae445010d7ed32c131052508090ee4d20a3bcffd317a491d45d8f151c0f004c56ad309d556413814c94dc298b1abd7b068c30f30f00cd62ccd41edafcb17b6c6de4e6dd95ac0d17a41eef86a84d5d701e58377ea2ae33df494e6b3cbbb7910d2ff0ad86c1e56aeb776e2026f6f9617594631262d89185ad25e080be740fbfebd93722841df34a768b5be2b443117b25bf6a404d4cbf8490c0a2eae778453eca0f3ba4979a1f09d9afbf4803c8531dd094561e30466cb0c90cec83ec86c41235f831bf51e1b861c16f94bb6af227df4a8989f3953cf7affc499cccfe1da55b236e1f4c0699bf0a1522af33c509f94aa63d2f66112dd97e265522f9951cb1959f85f67f25472e2246180f8567e5fea5fd96e3f6af64a76085f8a811e6b1e23394f208533318a551de352755f2eabe49a14e776f52e42250b941cc4a189af0010d7925f642a057e67708f5452728f61a93278ed95204ebd5111460215a8de8e8d1e983253bc8d20dc531cbd9677c77c119ccf21154c2d060be8f20092cbb8ca1292c99abc61524aecce6d30d7729f1ac72aeead4a46694c9a215de175d36f55903fc0b7de6aed53921b1642f1a57085cb3359bb2dc55eab5cab803f5c54ab7d62a21bb0452259345617ca3e22e061eed76436835a176320c4447b906c1545d77c4c9dfdee9ad53058d7e14ccc4353630c2b1bcd32e8d275b2f461a9bf1a9af45990bdcb538fdb05c4f6737aff3299b05367a2083f4b064e44505f9429f8ffbaf08c9098cf663c99b61e569bfba3d19150fcf622865de94da3e96fcf4fa9bc1d30f9eefda02c5f648a793abece4d6c91affc5105face3d8fc95252a361032fbb164611ba8e545b28ba6056ebd0d17c278081dfa8498fa1ed26b9694c9826b3609597bd082c8d4baa68d348f358bac6ba427bb1285c7baa7db3907857a3b6fdb2b4d044e68c4263f96acfebae7ea38feea349289c3238ff124f1442ba442164cc89bbd82820f6ff8556443563f4a1fee4243263eedac1c18f864c9f71e6e0f651976433ea8270fd3d3a24304d8b6fcfac8722c7100773f61be2cd90da32bdc032017e8ec32a9b35d60e55a9b073b2293241caf5c79bcaa1ae441e6b078414c6f7b488cd5c8c3ca58f62eb1285e65253c527901c21c12b8c3a6afa7a3abcb02f8cdb63a4b8e33344242f3bedcf7649bcb533a8d5ad81c7abb56bf5d98ad7fffd4ed6eb3015b93711121c21ad7396e08a8c941f66100698c7f2f781a61790396320be2a378858deec0640cba2bf282417d702dbff9afe8f462c63eb0d5a579820d546489a3eb62b28f3f25ed869c835d271a336639ab180b8d60c974bc6793f0431c16e0cae24ec060d35258b222f3b3cae7d909c4a3f4d3678703d7fc67df5f0c72bf9a39755772ca7b1fdc373bc2583a63c97d23c9850d37b75d2bb7973fe75f9d419d4ab07c3979c761c590181a6903c60d403a34548cdeca5f9b796f4ab487568f387c39c52cd9fbe776693584149b14c72f62860d13f907d2ed0c8833c8e82a5b3fc774b096253b7a3f7bd5f2008c7974bbc656fb3386f21faf8a52967cb7cc39780a5dec88017b3c4475b19b4f3449311966a50a6a87df4e6ca4b452f984a999fe25200bccfefec9de78b053846c1d6075b0a2c95312b225b2a1b3939f211d6652292e1f1afc66c962f3938fc130229f3e359f9834d739c216cc03f2cd98a60f71acca16c92d8ebe13088da336c524a3f642cf991fe6b17ef6ab693b549625d721b8790754df7ea388bd892f4baeb6f7e963f5f36ddbfbe074f6f908e35afac5055896505565b6e4fa532a4d65a3125ba8833f633c74bada91c238efe89c3cc86c0f05e03a669b05ad1a86851fc5c8db9e8af9853fd4a3508cdd31ab5c4e44b55d0752da699968c168d8bd2f8f107c3935fd462592bb86e41838535d50ab550d56623ab9badae781fda51e215582d593ac4d129854ebcd59133179818a0cde3814b1d1cb0d1193b5793425d02feb2eb899b7b49d0d13063c108ad753e03acad7c2478d29b90ee5eb28c245c4e442910c82891e25f186621733195aa51be0bd0e36bac09e28a318f355d1ccfcc671ce180198ddfb54d7414e66ad7a0cf4878488c954534e015f8a69499aa045b60f1b49eeb08ec926ca07b448fad03ee70a3578e1eee75bdb89e6abc8cd71661ab39c6300e997295610058d58519ec4e44808dad8da0683e0d34e7a9b8a4b2693a5f7bb6754ca671c21abb5f2a1a46e0cc018a80a0d0b39606c1d0c314457475f57878e3101be5b0c7c129323cbdb77a909e33176af1d91be09665e4395b095aacc24264b20991b2cefa83eab433d50436fccbe29d8370ea78f834dc7986c5d957bcb4293536284526354f71bf933d12a6197d9796e5e374c20037f3a33c174efeba5fcf0e2990622752beabd68f1bd3f4ce98488ea5625ae0c0caf4e13314ada0f064e4c4cce93906db6739f31831b9030f134b6041d010ae36bba86f66e6863d5e3f7ca042feb41f04523cdfef75c9948c3d35cf40c4838c67c2ecad408c48c7872b66e093151e9a2c9fb9d83cd3348474fe70c20c7001bc5e40360be4239aa453a140464300c542be5a44d5e5e87ffaf7346cb0db4c8490a9db59a549f9251931cd237d320522a46cedb78fdeb5b8970208e649743721dad0641633e64989e8e9d2bd08ac94568714c14e6dc390addfbb3d6407459fbf4df7f6d225a27ca90c19d2969c188965a1a28c4be727a2a45425a4252ea2500c2edb0176489044e4404010f521048a3743aae5f281834060b8213c20c4ad5cc4c339eda36104e2c160634460d030ac315a4440030ca288f9911890060240200064630378c087000219e71ab516da4fdfd50a6c96feb4d4bdade5b4a29a50c290e4c0f5b0f8ff66821071c01c099a58109a242db121312919dd112124986e041078c4094a5a39ad1f873442b600c84d700bed8bda17d76cf81f3f1d953d0b15180489d59731ae2b5b47701bc7eca7e5754519080b99a31d44592f10939e07c81b193800df4f7c892322c6ac050cba135017ab80dab6aa4c8321a03b43476c9311605b40d517e0531fc3504973152ba8cb9f970cbfaec395e707c761d2f769f7d044f871e4f1a3ebba9b5080468229715af5d6b2e64af1d855de69c73f6f1e5b3f0f33dc0dd5286f664c04ec889545c0c2c13037832fe0a0084e94de8298718dc0ebad566b7e5f8da45acfddd5b0a8d4d72d370b2c09d69d1cad28225ab8dc9673780871f75362178364c8d8dc98cadcce713589e6401f2d90bb04b123e5bace8006277940245d6598707920d6f8cd2956506290d879a4b329c73d1b6afa55829b689255a2e892b5e234097f0b59528afa1bcbe22f59aadf8da07302cd1b62f850da6a4b8176553ea9abe760d90afb53ee17593d46bed66deb170733a9c90f952a338a56a5da295641b1b75345a8d95f22b88cf596b433e3ff9eca6cf674fd53efb30abcadbe72c2b5e9ffd041db1325dbb7a92fc157bed228e5232f42426221d2381f9152799f02b8602533ca60063f774e4ace7c78513343832183bd45b877d9e242f9ada93f66b6ce8c9f5b295f0eb9d8b819d0f5696a3221e41ea7adb9e0c22c444d6238dce4a5d2f1c365e19bd92fe2a5da627fd3ab599bfde2710c2a72a73562d624079a2ae176a4912247249415cb40551d73b558ae10fd7d73b246644ba757bd25d407af2572bd78174bbdab95bbc965c71764a280c423bc9a5c2eb4901fc1b8fb92129614f6490d4d51a9f4c7287c28a11b7383058d4d51e9fe412f27a5f006c80c346591150d45b0a35c48b313a256926ece0b60fdec144f9c32502fb55e8f52b87cf2c8a3ee75cc522c9e716419f5b947d76137288307ea2123056c33bbb88d1e0fefcd3411049ad1697d15ff97ec58eeff55c00684f2f4dff3cc5c019d5ec0148c9d652edaf0f597d59fa87dcf31c7d85774aaf2119bf5d9360fabed47627a7582c9a30168bea852b163ffb08684f8ec23b184a08cbeb10af1d00688f3602954378a65f26bda6370e71c30abd1283eb0cbe89ab90b861051d74eda9cf3371c30a24be475c35f8d0497c5996c75eaf67ecdddc2fa75edfac1a6ed6ed183060c08001ca098356ca90c1877e66c89041230d87434d34bcb99f4103e50ff7d1b4185cdbda5f34b1e3df17d2788d9f343434c69a9a9aa99a9bfb3450f943f0e6fef9fbfde6fe040281487f696888fede0766a0fce1deffe5221038f770f9c3fdfd701b1008c46bf9c345cb1fd05eaf979dcef3cc4af9c3bddfdb7bf388ab9e2bcb122e7fb839ebb6bffac307d71f887a2d7fb8f7ab20295ddf4e627073c5bc838d452092e83a89a4443acd5d2f8ba5530974bd24da6850e4f1aad7f754deb1aa528904d7b686468a19ffba072cf66bd735891a56609cbd4975791da96bfae48720342988a2e09218c15064518942466e66b063c9dc0fae281e49f7e6384df91969a97137068c0d1436ac0bc1e8807110b3210a22dca08aa7cf679f854e7cf61052206236f77248fbec3afb0612a248b941462cc6922612b09ed2822dd09a5c742d3db903b2f7a200c20df229fbec1a32891e484698208122c30891baeaf47a9f2ff147a0d2f2d91990e6ab1c4d4a7e186139425273ce190060c99c1cc09afd3a62ec577b6524dd27ecc92d24144b8a7eac3ce93a3c349e7415528079d21f0074f5a463b06242bd359b2a9f2561121956eccae4cc98540d909ecbcf3903e0f3d2671f33058a8e8a91a8216af612ec5c83f6649fd6afc210bf72c0e9fa15840805bcdc934fba99263d5965e2ca93eea4e7c478b2ea8495273dc893c5489934322d8ca2ea70bea4c64848fdc0a2a2c1c20c101397f4d24791a6c4d488cf9e6a62d6934fbae98454552289c3670752997d914fbaa9f50f4ad567474d10d6e507d25091a2290f25508b8522c94b5c91129416b524a6a5d85f2f35d1f728ec9e5d5514ea9188a176248b4ba392e46477205a79a5248a44861cdf7e92b66ea84efb0ecb089fdd0c6223bd8361b5ed82e328ec0b0aa13030374e6e2701ace26408d159b1827677ec22521cf1b3937abfb024040565a9850f92ff209292205257a648891f5748cedc5ddc459fbbb93e7c76ad77b14609902b4631209f36754d7771884574f0b1f67bd2cdbc9342941861542dbab6ce54521fb5111fcb1c29463b93948f0f682295c5a484929914ea3a5c40131421428cac92185135407a084af080bc83d1f287eba46311223ce93c584408222c565594279d015844f9a4a779c79e05fd680197038809322a793cf2a1a5cc902c415b2a495c5bb06dbf89f878a594da5fb1f1fa3616d5a3495454fabb9df47d2a3b2b8fc267bfe663233ed614bf84cbc1ba4b9393ed2ddc5be20b22ceec1767bdc9d2e4f9b23d7da8b5a74fcb68e4f3dd7baf0f85416ab58c3ee7989c73467f18d6f6defb07ccf056abc916d0579665094c3dbc3da6984a7d3c1e8f97d2f49ad0f8cef33c69825a4d8e047d288aa2c19aa0921a1f100804d608bd7eca843e1a1a1a1aa18d8dafa6a6a6c606835613270c3e1b1b1b1b0c199a20693519d26a527ca1efecf1d01f061b434a86febc3e9fcf4754215fff01c91ba0ab807deec3bfdf6ad52ebe2c60113e167c3e9fcfe763018b08b27055041d5f25f87c20d047b4f1d4c6cf0cdebb414bdfeff7fb112dce61e1fe8f851a07faefe72bcf7fbbe7d985fe739b1b8b3ffc52ffa1ae1de8343734fef37b13a491f688eb0bd997896b0d4a5cadda75fd9af882deeadbc4a0af3c62d0e73b892fe8b9deaf892fe04f892b89ec230efdcfcd9bd27f3758e8e84dcf24aebef7f9bc7763a35d7863e3bd9b53e82ae4f3fa4c0cbe741dda3840fb5c7883c16d6e84448bc177d3f3d3bc597d6fbaf046e30fa6f7f28f97b1f36ef09b37d9cb167c2ce01f6812418cfe6c5d81179ef49dbdd41fb4db17e08c394aeb96b8628c7149e69d6fce58043e8158e7f6edbb26a4ae77dcbc8fc3c1ade38b9a90ae3a3cd62b45f1b187182174b11ee77513d235158ab0378f775cc5fe00095814f2eab36026e0ee86f75d1e24dadeb2bdeb9beb5a6bbcf6eb0b2a60d224319226b73e22f78d36d244fae89e707de700496b8cc91b9f793711a933e23799899ad854e632fff5e1757bddc42c4e4b2c87c2d4675e274bd3124dbbb630e2affb9c26694d9c834dbcb35eb35fd3e723cdbeea5f2f79af0fff7a89c47dc92c266f137b373dabf6eb0bb92c795a97e50f5e6b6b7d3b79637d93ae6f5692f44d92d9ed8d0a7906e5e76c5f677b4d222649d7e525ab7076039445e9f5229ed5f769dee940e4803f60c797c3e3324949e99a833d87e7258df43eefbebe29d06f9a0a3d67f0cc4104dfe02080b0799eb387900322844e0e881028eabd9bf585efdd1e71838d6be2baa1023fcf1b6c882bfcb2fd17fe7a6fed390d06bf36da730550ef79de60435c01750c376bcf4f111c15c133314803e867795111b4a3c4208d54135bf0b9452b208267e20b447094f882103c7b0b3e4d6cc1f7221077087ef242e0907a26ae1b4000211d6af0a186e1d087bae7e5d0b3d630f4d4cba167dd23aee9f0e60c4362064f33649b334d53df5e937349433bcfaff3fc5eed429ed0e6afff56dde1665f0c7e6fd6167cbd9b1b68a10b89ab8d6bd737bd0a68e27aefea7b1cf49e6b0c2bd838cf37b1055fa64981be9b1c10264f27079f7b90896bcf4f3fcf1b10a803b2d639405d05ac3fa3c45b816de2e7b90af841e86011bd33c7f702d69ad8b3dbb421e06092af18686fbf621c7fedf0d84d78fb1503e953e0b9b973ddfadca19b373cbf373f56d3b5f79cc723f66e822fd2d2edf3bceb8cd26737af6ad1154ccfa55f9457a22b94fe82d28725895640afa06950da13b4bf307c6b4b18e2125d21fffa832f4b37cbd25f309fe7432c22e7e7dde019e42fbdbcb9b6843b344bb7c620a6b7c6a0a8c72ec4223694c419dcd74e83fb4d77153efe8a01f41503e87da9f3eede41a6f7f28ad6cd5f75da675f51bb7601fb21fbbadf6ea2056c09d9b35a4f6e92f25ebb0b5ac3e98c4dcf79bbce6ac532fcaaf36257c9b5aff7e94bb740dec435c39b6e4bd0f8492736dde0d737974929ef5c22a55bccee020eb41cb910c37284a80e8ab4719e37fdf0c6b776e8deac3a7ffd2c8fcc374223b36e8f3e6f8b258cd43a897cf1cfb9374480f8824ddcb0ddbafd4bdcb0890dd0bfed63c77fde54dfdc1b1c31e9081e9c2317e2b393778eff85cd57d6b3d19fc07dfa998d30babe00bcfe431d60535edf782dbbc84658c4eeb9b63c6ddcd6d000796e6fd6f433ee815632eff65e0f04df660578eee222a067f21c03311bd5a440dddb3e014bbc20107b9bb881a7826fa8e15ebfdaf67a2078d63dbb896950b8ed0a02516bab35107dc47d5df79c3cf27937f6cd7b7b19f7201359257bb1ce48d7dedece466e9db866a3cfce73dbdb3debab4934cd34e3ebd9decd23f27226ba27ec9ee79beb196e4ec77083fabec9f6f19ab0864843c4bfbdf7fefdb6f5cdcbf65e228ff4dc731ef105bd08906e7fbe7f9eddddfafac27e9eef74f4c6c7431d60fd053ca29b68057c6f9db8beb0ed76df496e53074fea09dcf5054c92ab6f933716b36111f6fed66b1aee300daf65a3f55e7bb3d199e370f8b2dca587e04b279dc8e330f485a0059c10b480a385237cbdfa9ec7e39dde028fb71dd0ebb5a00516be475c4bb727eab9e4f934cf01a7a7cf23ae2dfc7693685b106618c23cc25ef1e01c9b2dde381863bc7d38e76abc7142e09cab71cece5bf3f0dc347d763b267926f18ef8ebd7a78345f0fc7a082cc2f4dede9b98a383c3d3c4a69368f10753e3f4dc49310e0892244b2f9df4aca342ce79d2e8b42048ad5691da2bc94a324e3ec0bb9085d4e6072c7dda2e25f1a50474f20e4fceda4760110cc03b5635c9fd2b0f1eddfbf306d349bc49f46111a8eb16a044d2c9f48674e0cd697383fae31cadd19ea661b6166081a8019c23769d9e700e19724d445ac48082040ef5a09f6810456d6aa03da5d07112da2324daf4c2017d9a3aa681f6a4a88720463f5a5f4a60c1a192805134da52b32154a745d54099855ebaefcb975ebe84fbb28402038bd0132466c658a9a51c16166757453bb3196a30bdc1479b1942c732d01e21fa66f4288abe1179d431128aa250c7764408450917463730d3aafd00a20506d891a32f31ee17b0ac232bb441e646d1d35816225daeba824818f901a466c38914313954218cfba585028c82c2b22c85716155e4d650d02519a96bfa5c8288912440d5b021e52503c718f5c5970f061dbb407b8236a589332e63949cbc080a6ef101c367c2cf179159d095245b6a59a68efa99a2692a749b52485ccd22ed05c6075d071d13a13dc1f40643f0a4cc14186a46b52655032c6011a9971e82b2ed4ba8a12fcb1c617e0499531222e38a5a7a098468192c1db7b8556e5d3e4d1db3407bd2122aef60280c3a07b3ce39eb9c669d89607dd6809572ced926173f67c839e79c7316fb9c8539e79c73cf05319f77de207482347d0e660cc49c7f3ebbce39e7bc862118e4f041b7313ee81a8b30c0078343bc3e4804c7071daf407b82410c2eb818d5a09b160fa13dc1b5602a4ca1a07cea3a4d85ecf854c89c90afa54162eaa6c52ad09ef40a7bfd0d72f736368e83d01e1b201dac0a72e48542c729d01e611afcd4f10fda939669f955a2953f8eaa7e60b9a802c2e54bc728ee9694924f5a42a1c247393ceaf6c8a3ae5137514fd1a7258f3a3ee1659a840b09365102edc950249a90d92981b31194a5e5a237d5d20425cfd47650f18104664a0d063592d47111ed497db07814f57b87f6a0656abf9891894b6a01e6890e9718cd52566ed518729685c812ac2f6970c946fd9eb9c72a1e62be745df6c8fad2af1d56ba27d3799ee7799eeec2da9f67799ee7793a0fd29fe8a980f33cdffe3c4fb7e779a6574e743c8a8047fdd6a13d289a1251bf66d01e74158695b9120bfbd26f9912b5722aaa65e997eec672f05f41800d995d8159a9f2611776e22a2c6cae2c97bc15299249c7a03d67aa1343ca4deec4969158873a1b664e514b76692716362c3c12bef88c95619161a3284637a5c99095202628c4c85d7d69f9b2f41b87f69470178ec8e7f3f99c830fec7d3e9fcf87d14aab39a61bc696f50003c753cc7b9f7b6f73933b6f7263995dfdf6fb64dc7be7eddb79bb0b647eef2bbf5ea0316b4d5b45c680e4beecba2437223e256e2cc9b1f7de20b687e0e1fa6deebdb7a67ebbde7b63b133ec3c71fe24f3f9f3248bf2a7a73efe3ccbcabce5980880f7e007964a109fa2fac18b5b0d0051e82519f605400138209c88e178e23526843566e58b56ba62e351df1efce0c0fd22c525000f80f7e0830b2000c41fdc1d619a8aaa0f7e402c7ef00f44ac3424ae58ecaf6720ae58e9d75dc4405c4b241be24ac209892bd91624aee45a4a5c493494b89262dbb7f63ed73e2c3123ef7322e46578baa13d19ed2ca14ab352e94bacaf372c302cacb0a42ea72e16b96c417b360c86be407919b3a548886669c6da9c8caf3914eaaaf3eec356aa7851b24328b9407182abeac8c40b1c549c0f1b9dd68cf36ceb693943ccb2f75a9393bdf7ce58d09e1cc686f69424e86012c70696032ee803483c4c5c6dcdb811b46fb946c52a876f0e0b53d65c9a18aa124f66ecc88ac67e2ca9a79efbeb5b29ef582cc2967eaf60792515c0cadabd627b1d5bba9164061d152c754d7f0a102ca7ea84cc0c2a5dd892bad47d96e5f655b1e18003e2c9d3932767c65e9459f3edbdf7de7b0381fbbdcb725f2a68cf1e009715017e0d411496e5b3a7313e1bc5aa7c66323a29f3d9ef94edbbe5ed8165e323ae784d0ada93af94a0d63b8345ae1835c58c9b141340124d25aa54599ac2e54d0c8d72a15c301f3c692894d1d455d59831aa0b8e22133246a07640b9e5d5c96a518628b121b19426171595457785f54c4f3c9a287dc15146c55728dae9f8ecebed4b331247b8a08c76c72ea520b7bc509c6a58301443a4d2e4b8b404a9f796a13df9eb4802d9cef944c093a2182dc8dc2892a1aee9a701496263256575e449d21519d85996be313561175c9ad7d66f4fbf743c19962bc819fdf8a20b72820b6f6cd19084c22d2f3e415eccb688a9e70dbe2a4b276200e07afbed7abbd9d5159696650a4f27d817367201f992ca0b07453f4e6b4636bb30be332f65494972cc9d75491ba266cfd97b6f8da20b6f8519c1b54f9fddc5fce176702273ec42336ee5d83a4fbdb5af435593de799ea7d6228e9ad442d372f11b84dfdba8f5f4db78cc3ad1b39f23d851db10b0d3fa5230b3628352515775f5252ecc4959a2c415af615d9fdf2bbb3ffff46be53bf70f27beec2bab8c59517e5395c6bb7bbdddebf5cc9e6dfb5e8fe8c4f77abd5e0fad87c601f71c844e082c627fcf79aa7c4f43afd7ebb98dfbdef77abded83855f72f812e7cb1565be2c4b164e5fb6287ed9a2ec4bbfc61cb1528c9c2a7ee9b709da538a956520428c08fa49cb0aa2a2224a48284be84ad61092152c9894687f8b9ffd2e417b7289cf9c88f815e7c7ea490a18bf53b8fd065af2dbaf929c5cb67df69b0409dad3339e3d041cd11c99427b7a3f2ebe34b2f72def8f2fbfbdf72e82a24a4c6aef11b68815d9813445c8f196b7ec05b02b8b0f0e323f37acecad4f983e765851be7b26a0ca4a4af4f5880c417bceb21c21172da2a5880bb12632e8644001b30309c7893067eaaae3986ccc0b9570073b4c9cad28ebe213f582a4aa2436184c414ad4bcf7ded90785809e8920d867c709407cfbf8d454e4f1d41686249f3cd16d7001068ad0d1d8c387effce133bb2a6e9d9550ca2a216782eb84d209b6271d11529ea0bc2832c60c94696e2077bf3708bc9a9851adb919d1a2eebd85d2ddc2feca218de6d711764b766c678c7733b0eea87cce2a4e7df6ebe3425d28139a9860e202ecf84446cc878f298323545a47a624b5e062efed714708128105d784c7de7befbd77e9c401cbcb1700bcf8f286f596950342a7c643d4d47cb1a8f13535353535615f7356545992506951f1d9ef0e97b2a666bf3a4c26b4a7a6b46a11c30bd3993913683987050172ee24b815614938b29ace1c260088f8188e720055685d34c884608135659d34d8820223b3426c87db121516a40d4a9728ef6023190b2159bc8c54b9ab53d7e12dafedd9af0f922564040b8a9d4f1b929a3da7e7f706dad353f2a58d6dcf6c292b5a6254d1c385baeafce96575647218fb026416860564a8d7ab21a250e32659798141020d0148113375f723cc93295d3da00477c0c32d2987d2d0538e7dced061611a987c763de587955b9d8cb23cc92c15b3f06831a4c68631178f1c5ac89c0919a3a28c5df57a489a878b91b02e3f31d26dc53eb87fb83983dca165792f0a47f783f1b7eb6dc585058c74dff206f1edbdb7110e115e6c1740cdc89261e4eac7d4960fb35e88133a7cac5d192b43e2c26acb890e4579e726a7b23c8952b1a52a26e068d1e2dc408a6832115fe11c7c2ccb93c5cef2fbb79b4b3ebffdae1896b7bc59e7cf0e4d34357708edc9a75fa1ab84f565e957c5de245a44b9b80224ee8d4ad7de7bef7dfccde3c7ef5d866c0aac8c8715321f757b09cab22c6bf61984f66ca1141c1b4fa0dc78caba6154aa0194d36386c267c790131456aed60f18553784bac8a08c6151963745960d16bfaf7ecaf48ae01483b465398605a5e4f76fbf284a5082a0f1105884cdd3d0d08ca510317c9aa7f90262a3a8aaace82c8a4ad3f5a3c8cbc40a2f59d4edb34fb204fc8a03c202530f36d66d00bf5a395be7b35ea3cbe405c3abcbe72caf315f5c6029b2c0a0f438b29515e656022b4b6c4c9613aea5ac27749ffdfaa03d39ef1326d01e9a1bb458c1c2461526533ab07894fd5008e92afb42c17502acd7736b51b0d706f942b37bb758425dadb546d343174a77d03d8d234c678d35d65a9fda5d70d320f46be759d2da39b8e8117cadb5d6a5f124f33dd73d2761f64ea2b1e73fd0a531974b3cbe745d96a5fb60581acbf2587a0f74b9b3f776fbe3f756e2f27b6fe7816f02fcf61de82d2634f3f02b881f9154a606970d251b739f3de706d867d7b901e6b3eb00edc93bab75312ae60007688f2ea17870b9d381844897194577dff26aabb476575a4928aa0cd51883afce1bd800edf7f617b43e6dd6318dadc5d69522a1bb6f69656503ff10231504cb4b0d1c5a9fbd1a944e839bb4e437fcf619e4ec03235e72398e439c2c51579dafb26261f2e4e04227356469ebc80519f8dc7e15d2704a693879ed3118d26416658c6f4f5882deb4a8ab4ece39e74cf5b9e8f3fecc002a488ebe1cf530baa2ee4d832626761261d0c256171959aff5950cb9d73396bcf617e80300d5d882d6a28b102b75d5190e5f6badb5d6cea3b5d656b4c814cf843075495235d11a4fe28ad72c8f8ea7d6b4ab35f6dc05684f8f68d32bfc8c03e2fefc1a6284bdf2c2fad592a97db619cdb62e84bcdea1cfeb35fa6cf365b2eb8d52621454750583c567e751e1011638a2f1d9592841d7676f810b659f3d0746568a6b404644313efb45523ab2738ae104eac95e218df9ec2d306e1e2852ad7bc415afb140ebb7ebed2bd0d68c19b11c654cd0526c5100101c280f92c05cf4006b91c5ca265a155cbd587aad3d052ad8bc638d9f802730cdf6d951508a01810e02f840b7550f7c20100824a28d3d41e3f0e381c79599baab3ab02bbb1d9ffd043a3b26c0a9d94dc0d3d37a88778f38e76aef1e09ca38d0f510b1765546eab58fa047c2865b965d508b21a017f7a6e0549541a8cd7061c3c7952d3ad49c7b447c249a5b04e6159d8910dc5ff6dfefe71c7e56fee73f1f621109f85f98a8b238135355be98a8bf174c8a96987d91b11aa36e9f3df4d76d1408d09eac3fe0019e520ce70cf439bb7945b7e5751922af9d85e193090d742fcc3a906a29516959b5813971c47844931a081dc65081f9da1aca32b35a732c5e6b37e7b0bc760ea418378db9d9c0083a93cc93d7ae81b42c411720518e64c415b5dd9b11f0d98791cf193e0375414539d2a872e5c4cd694ee17e98214942542caad9cd0ca4638c0778ed18b8d7e9870d1ac61710236701f30a0bcb588bb22a2d6ee48c020f181988a458754d35d538ab80982d0150808bd3f148d1118f2d72391fe08986dc9b0a2f4a9830b94245cd190de3098851fa20812047d008cce540c0096b0504fc2a942380dc92d7fe8061b9021b5a132542a0888018797c34a50bc0f262c78f14a62f636ace18cd682ccbec80b823af1be03dde1213962d635f734d5a7fce79e7f35397cf992d2a890e197647626acebd340f7f2571051273aefd1157f324ae69af6717f14eba8186618412f0dacd34ccf1b50fe3965e3b039cc41197a6a4256971328888f88183e6cfa0deaccad4b061660160a83e737d76331d7a8eaa43428ac848982073476b2acad2005730e8449958a2a4ea84d931c33346dc9f48637f622171f95346d59f9e8370dec6edcaf3365546575438a9717764a7da35e76d5ce76dbed0aabc8d4dd5dbb8dbe0800c1b50131a4e56f8a8369e81d01e1b20100804f1c0280f040281c023180f3c32f240cf2974705e48f52bcf0bdd0065664cd1c5d9e052445d735ee836ef34002b491693284f8688a9abce0b85421f366179a1d0f30fda234cd314e753b73e699ac200f2a96714684f6ac207d98c1e5144711e9ed4780f1e8cefc1cdbc430163902e544277268eaa07cf3e5ee3ab01e1ad8bafa971d3a8c9d7d4b8777d8de713393c2ca314cfe3f1789e4da03dbc123f9f734e761d1c82a7ea3386bc24e39c4d6461ce350ac81998b34dce39e71a315ece39e79cfd08e9793ccf45b3a6a6a6a6c6f19dc3407b1b1bc7678040c7765a3060bc50e8b80eed119ee7799ee7799ea7633368cf6914f5e99332a60fbc549049e27292ebc20564856e1263cb179718389a909ca09b437bf28b295f964cbe2c4bb5749dbca3d5d279dabea4299794b854c3ea6ad75ca0fbb22c9194b48c866aca5b6631b4373373868a9492dd132c29e6608451c1258612b5bcdb12c40a0a2d25547266746491d3b2455dad11e8da0492f11fae3f5d9f63b4d65a6badc5e49c17689988d68b2872714b4f243a0e83f610b3cc8cde344dd3344dd37433ab8c4d074cceb1f55904b8371e2a282dfee6c6f117b4e786685333edc4a3288aa270e8a3ce53f728aa0445d112288aa28e0211f518c204e3ac58877a097d8da1c26a82437de75761da8d11b0bc9c5469487f9ea71b10840d71b6028beaec89510e2ab4b0cf8ebbb892b18aecea4a131d175b189d840734b69cc879e92197c6650bda83fe7877c76d688f53b5d9780d1a7ca8c15d83e7e49d5483060d6e6ab06e580bdaa373ce393bce8285e7f8f31d5c7770cc666a85f9a1f087487e987f38242a168b27d4e1d0436011bc1fba99776cc264c0f50873e68b511dca41d90a12630637f49c963b58271fb25f557dce576a585852593f59699f5d87276beeb3aba0b575e5882f91ab174cec848c59d280567bb4625c5b501c797d657ced2a84516129aaeb09dc8d326eaa766c65ad0ada3324da942728e945702d82e335b44704a24d4d1740771ca3d62992fcfe6d237b1781404abf6b7691bdcfbd6bf6c60a5af11bb8a57ea37b0f714b31d776c2f8a44bdd51646ca0d81205c9c716a38e2a3da07ebedc414982822b06d30c2c2a6a0acefc5425751923c46bd3c15d25e948a1c30cd0d4a36554569ecfaee316638a93af715de3580ada5343b4a989c5c03d910144c5cbd3d69faea3fe84b216c6065c17195c4dd4d3b19a63a5587a20d07114537338da20e4b50fef5e3b86828290285b6598c418e2a6aee9cf0a2218ba544d998a41c4ac875a3a4e731c553e04d721383e437b4220dad45482f1bfdf90fffd7ebfdf12dbff6a7e427ebffdfbfd7eee82d8ff687e6e2fd1ffdcfeaed2ff7a604ebee29e4851c9507f562ccc0f144729259cb6d435fd5f9834a7222674b490faa95d090d297d69d9d1f673fbfbb9ce1299d75ae350321be61a1fb494b296527c765c566232b467df08f3a5872f1d8fa13de513b4e77763c773e0e0d809dac3814b0ead2ce17b3dcfb947503435bb565ead1b472954af77f67a6ef66e1871617c22a34b89a31e50b17bc2c5448e226359580ced39350084ed49cb0f921a449e5ebb0ef23a8a2bca158f2d4890c854ed384ccb8692862f1d83e59d168c908099d12209c90a1588c867d7403ecf600b0226899b0a246a76fce55a35ea7e6fc75eb86bcb4af145f1a13edf99cf7ac9b9bdcfb5cf4d3147dee7e9d0c4fbc650791f1924eff391d17adfdc8df7cd8d799f632eb4c72737e53364c890c133784edeb94ac562d1849a218387c0227a9f214386140744881127fc6abde0c27c760dc6f839cc8acf616a84018315e60e2bee0c4b2ec867c75b684f16c379cdf3daeabc76382cafb5e6d2d2f6da4dedf8a885f66420da9467cc6fd8e0380bedd940b4a929a4ea83c11f1f74cf3bb79893777482ae13028bc81f749eb30f8ef8e053d06dd0ae7d5007836ee61d176cbdd0123325636a4d0d5699fd5c8991438894baa61f2402a465664716d21529c19b2c3e655234d1920125e85842c25e6bc75842a8321c28bacf8eaf865616aeaa4873939de666e5791abf680f06efadf73506bf294904e1661741205a28675d7624154d49c54500c99d99991a46984869d1f8e831e54c8d31585fcc98a19100d21204eba8895c9622d5062b2832642cd86888991bf260c14c0e3353594c7654c62522c718473232a07e9401399dec18bab9b1b23052cc2c3571810a2e4acad78ab5165f7a4d4f2e3531528507959c99b728262e987848c5387bf12208da6fc70a27452c88b46cb9b2c447d152955695294aca70f8741045b066a49323c245884e25dd95b4f0fa3132f4d5e5a7ecc88f49026e626727e08aae4425c9e4ceda31f8a96f6e8a7f77f1075bc65e90516204e544253715d99427c91c1d9e947145488a21b6a9202ae98e916ce09993738fb88170ce750cc4edf396da8be37fc475f8e4d29fc455c70641714edc641481bda93de26a3ee9040494e5f523c8c9492d897755be4c7c7dbd6139513771f52761b059799114c595a46ae29af3245519d193a4e323d67cb460f06e0daeadc1b6a01c017e0dae41b1c741a55f83377e0ddaf835685c83c7c7412f0c9cb8b2e2c8c2c6d31635005dd49e00296631a3a4ae41a0a0189f322c2340dc6aa86b10e929b075876386152c2ce6d435a8e4634c927163aa079bba0683fc54650991b1ba22455d83d848437387425d69d61ed390ea4a4313805f69eaa068c41ed3a0d5f89526e9571a1abfd218698e8f69bc1e3b4d31ef582a5943b8003121c5879521a38d2d2b5864b14853579ab0ba2cb7a6292a49a0d49506894998a1145dba3c91a91b5c7819a9a47d659d51571a2835868b30476070f1e8a1ae3462697a97ae3d4ed30fbfa64aa9523ae3d754c6af29d2af699a7a3df6b49877480409ab0a2a33da2232a662402d288ad31521aa19ea9a021d31b23d412be195425d531ca422b4245b25669851d774042b369ccc8e45c850a86b3a77a445ecadace9c40c754d4b23107807647b0c04ba01e51e0331119008a816e357e0d1af4018bf028d2b3085ba02bd1e3bb098776ac8b0d4cdb88b232aa11af199fa814221c66c4d5d81408f79a4609105a30cd60b7505ea58f3cae2c2c89a5012750566d1a93989f9c41d7da82b102a0519653c5a240122c384ba02a7cab59fddafdd0da9bfb69f1cf1d71f2efe70f167f4ebefc5af3f17bffe8cebeff83fafc7fe2be61ddb36244a95324534843c5187b47865918244b5445d7f408f5920e1c264c6149f28eafab37a62c6eb8c92949016eafafba931c5a2ac89cb8c13eafa837a0714a9ab7342a598444985b22760a8bbc76e57940d45e5fc57b46e0d25fb8baa15fd8a12fd8a3aa1c6153d3e0915ea8a7a3d76b4987748489d4892e348652eed47c56c59724479b9c214a6ae285013a6ba2c4ecac0505734061328426f5f594c6ea82baaf4654ff8ba3bd5d5e7f3f9e63afcea13f389f95afcea63f1ab6fc5af3ee3ea3b3ef6793d765f31efd82aa4b72d4d62ac48518dd0aec6e4f84124860b75f501593429c71d57762bb87c51571f1209ce8a14c7a2bed8aea8ab4fa9a9471d1d11ba2364a6d4d51707c535b7228bad098cbafa96d81396dce99c6b8fcfacaee72927c2afa7f1349e43bf9e42bf9e2a7e3dbda4fc7a9e5e8ffd2ce61d12299c7cead8ace8dcda54b8aab7212fd098a690d4f5042a809d1818234d704057ea7a226928d2230b59b1051323ea7a1ea0eb8693bab5221219753db99a2489fb528644c90b753da77ed8137edcf57a3db8b9e1af3da8deeda11139f5b2fcdaebd5b027d4b8f351575e8daf3c1e4f2ec3af3c25deca432372e21957def131cfebaeb027dc1577a5ba9aa66962f8d5243289cca05f4da05fcd14bf9ac6d53c3e36bd7e40462d4d8917236535d4b41d603c8cfc1032c6495d4daa1f546d679e947022a5ae269257d2162a5ecab07e9ad4d55422808b2d2546aaa459995357336c0e8891112c5cea6afaa0b4bba351d7b22c4b9b5fcbba5b2c4b14bf963ebf965e73bf96c7c7a5d76312498c2419d3c5a26acecb0c8e78d98001747687455d4b1e4ec8cc7a4451c1c385ba964b4f90242de5882a81435dcb335225887abced809ba2aee5928f152341c7aa2a6eea5a4ec1c09ee0e5aea7ae2449c2cd3d5e491748128dc88934aee4f131e925027b82943b17eade6e5beefc75df75a39df8753bed17dbebcfb8c13111c38a4277a4ae1b0984955a8f1a545490a6d4752b4dc09e60bfeed4deb8ea35dda6e51eafda025a0ccdc4af9aa844f1576d5cf5f1b1f67a5bc6a98696b8a01a5098a8d8cb8dad34b9b5b51a403c2d5e5e7051011627455d35520b50ca188b594c1091a1ae5ae9b109a92e348c62772ed45543bd0898ccd88d09d93a7254573d95809c803b3554b56b869bfb5cf72ae40f62c67cf76b2672cac6351f1f67afc7b60e8b2e488657d6113b1968814a030394238b8cbae612700977412ac658eeb10ed639f32b267242f22b3e3efe157b9da0c8172ca7144c27c0d4a4224977735fb4661851570c848010ec09d7d76bf738843bbc5eb7c7ea7ae51edfbab7fbf512d5fd7acdfc7a8d5f56d7eb317e4b0b5303cec5d814752856d4648b09ae2668ea7a814804155f9818538106d6425d2fd2d316a4b223596ef0b8a2aeb7ed0ec810193f9864e850d70b5522eeaa055894ad2c32541f5804a0f684eb38cd21582b168b3fea0ea9627f01ef68157b0f2c0af67199c7748f5d052c420249656c53c21cc9a106a4621d5d5db850a9c052a453b10ff38e0d138475e5e9c8ab4d858aa3ac6015db9a2c29aaab5bbbb4baad2b28c4f860a1622c53494a647101d7a5ae3c8f4b235a0acbb22ccb6059fa75e2f2655a96655996c6d258966959ba0b4abe2ccbb2e402d2f125992fdd71b0081b2f3d07efa4c56211482d5d2704cfd097c0b29c91c4ca440d2d2b2fb504935372e5eb8595a2b214e37aa1bfd0f114da231462e8bdd0b111b44788d9c4d0f860113328dcf9a063292db71685f6a469508c889c8f4751c7436c1c0b31d19e720d156670ba3352d445ca570b754d7f4c0fa517cc8a51b09809c3fa5183dc8100224c4df861f513fe407bca38b0013cadd9dd318d7912671666c5508549d529b9342626688455f1d1234c5c141ea5c5b236b70594e3c8c2a8ae3adf41eecb2ab12f4b4f5b8072e643c793b2133df494d4bec54e617c1c9b4a2a35aca524b2629838ab2e3643b2292f724aaea08dedb85887962ba6d40423287a838f281a0ce6d038c0109d2dddf4fe90f500e2d6456e489b088eaaecba5a8cbd7d51579d0f03c64acd4c614a0d2e6e6a604f193801a423c8270d8c11a57d03eeca06ca831a67eacab4a2905d39010c8a901339e2c4c6dcf4c2bd4d952470d4c91dd390966401a281beb29fe05219d26710de1ac9dcda7434ef924e40c83937ee3dd73da2999708b15e733e3bceb9bac42b75016637334e4e9a7708b5d6aed422038e3b8ecd5bae1b6dbfba0e59848b4f512e96649469a2140404595d52e8c8e2a38825051c2b9848813b4a4ddda9504c601c4d9983c3216484d17972bef7faf0ba8e452f987bcf7b75ef9abc33bd341df274e0bdcd33558ca29cd19048c468196a6bc2c484baddee9024a9a214a66bbffd760ebeaddade7beb13e4d67b8fb509710e11cb6fe7709a449c1cd7b10bf2c148c59ae8c69a8258d1b1e6c488902e2bb425294842f0e8e878ae83aab1d2800363ea2b889818a30c7547e951c50b4eeb0b99249c1cf739e923ea6c2359d3efb81434dbcd10abebecdca28e359ea64e08ae1b767fbe358e49f955e77f40489b75a4317e47153f341ade1e55743df0ed31880c9a0909f8ebf6ba797d78e521a9889920268666fc9859ed38d285e2ccc86d2b4bda0cf2f8648c919cdc18aa32b94888471148d3b1079dd469e623b664caca0bd80d3507dc92ad20435e4cb123bcc5ba6af1c0b7585745231dee75f3fa3085bdf70ebefd6221f4f769cd4955912b5e6a7032520ffed7bab6737fc7d06a480d69f1b343edc00a2622213a1338ded4ebc3eb36d5b9561dd63c230d2d6cc811c90c99a434203da6bc886371438431296069912a1324c4ecc6097040d125b111c762089a182469367658f13012f3801c4d9a7cb05dd962a688ddd26e8a70f31a34d0fcbec9226fcef92b4ad967fc1987463abcc3c934acd17ccb852445861c18a3131c2d846821c1b268674bbab6c84d291143e1f4258ec895aa277130c8516685e48c9b58d85418b2c7991ad29799af1543547d226cc8451329634fb490495203cad0d8590f3035f0f28686648b50518a29b8e194931748b8dc38fa42802e62702ca5384b222201aa23b3155057aa24791b61234dad686736038c27f99e102912977a975f5e6fa991565ce882ae4a5cf9005399598ee3118ba421cee160d3b6a89959dba1246ccecb56143634a015715b3d7e08a9d9edf06bcbca56d2af4223e42def754bda5c2fb47a465d49bcb45829d1a30ca78b95295645b064714aad70904257a602ae8a96745965a5ad5400d78b1e43c8ab1f8a05f83aecbdf787b75d67ac1ebfed3a53c6496f12aed7e5d866e632265cee767831032a2e21291107054caacdcde6edd7718ceb78e6dbd9755e6fce64faa42689668f0536d14a0165f4761384b75f4261cf7bfb3534e4c1ba7dc10c2166755bc084e142024910181132556e536c4610a990832535c6e9969cac93e34c1122654ad6e24610b2b6c328264808c8ceaf3c6b8e0e0c21098031049b6fbf60f030ed170c1e7f731089c2fce6a97b6f00bc3db6b8f1346f8f44559e36d97625f4322116dc41921334a5a4af1c407840615b8c438ea8487a1a9e287e3678a1583ad988d688c32383978f91cd190c6e41af135450ad35bcf53a91e5cb3a02d4f23a28c5ebf4f53128c66ba2aa75076ed061dd484a521b53b5f16a0d495d9c992c672469665923e19862ca031399191734a284ccb0e1414c092b4e51b0a09881ad7c47a0301a6d80b7c714511ec3db23d08fed058b568f9e56144d0a7565bc70719a82b204624a13912c51277c8e6078fba565f4beb75f5a4044d84a986af20526881d9b91ed338c1f2668725350a684210b406abda40cbddf2b7fbdd4eafe3a079cbf8e55a4d8cd7bff9ada7d588845d8afd33d1e8f38d43ef49c6cb59b9ae81cbc6d4ee75ce23a7c6b9421b404b5e30d0eec5af9ecc2680dc7db982e202ada636e49c9b75f5b3dd21e22c8dedd4fc8721f5914fdded942d180b6b79b677eefbdabc49d5d6792984d4da88e9a74587d15a93b871a518d3931684437674ff33a11e433bc3daea02a832c49deacd94907214803e3179044f3eebdddeaad497293fb622ce536f11a901600693805cac37673afad709145e5c7437044cb8dbb1e71744dd49d663402b503051c1b1395ac295c7ec71f2aa89b1126485b4d34c2d4eda6240b151c51566460a97b935beb7c5c816616e0edf167e97f6fb3a6cc59d9ba312c48a8f8f09acb32c59a114a6261451455c5090ccc4de62caf39ac4d7b245223e5e8a48f4f25103228e0ac9c7073a242d546202b6c277cb88e49d6a7b5d6f94e6bad79c6985a6b0baa855ba7a950cbec7f6fbfb48ebfa63da8d26e32c63653f8bcf5e2b39797eca65038ecbdfdda0a3a96195353d1257fb1b1653f80c2c44519f97163cd7284622ceeca8ab2f32d970aaf22648ed24548eea649850c1d4c36b824a9ba84da992f2b424149cc549e35256db6c2d35aef736fd3b7a985d05ef75e731d5f93aad6da4d7d8bc2b75c2acedef76baaa59c9684749bbe1dc29640661ddfc4d5a7374eb39989660b32d2d57ccce17d2052d357bc4dbd4dd2247def4d1239e01c13e7acbebfbe0acb4dc4a9bef1996e7f354d2287215b4ae2efcecbc4a3933f58cff1e165ba9804be796c97be847e1da9a2b59bba24fa5273fb6a6edeb0e8b5363363a754b583b0a6d65b6b2d1c0edf5295a1fb55a879d7ccc61fd69e89da6abfc4a65ebaa3d528df355d6836f5dcf49e49ba70c793cec1896b0b384f7aa691dabf5ea664da93592549b72649240e71cefd9b0af75bae2118bfa6be874e5dfa3ac26ed242d15ae391d160c78e696b6f4ca68c7952b3d60e225aa4bc6c6dbda924d197ee1bdd64ba769328bc699f8f449f9d03d710dbe71a35a3808850919b950f2e3eaa07a44401997d854d51a166e2da024fcec537c29b21fe701dc35bae21b05f8524ce6560ea884059c17436042d431d588b1e39aabcaa40704144c6628797a1b57b0676a445cae94c0e266f291671cda4d2b49414e0034e1c4932e52ea88a18755bc1282a22819252dcf0f60be9c5936fbfa4c6a44eec1bef1b8d4ded9668122dceb122b4cef8f3dd1d02300deebf80ffdaeb0eb8f8e605fc17ceba6dc1ebe7edf55baf14401897f9de6b7a695af4caf9bd54784064e708aeb232f26e37b73cc35b2ea0259fbee502fab1a54eac6f99326d72af6bf69d735ec9bd3d7f586fd6bc77495c872449a664ce66fe606a93687591ad9e68e645400fc55bb7004fdeb146bb00bd7d588415a1f7defbf7ab808fbcb0986fbf8ce67ed529e6e11bf1f68879188045e0b77ac8a989429c93829d198bb7c42c87a9cdc6b21ebc2526a9490734f19ce41153fce1762893b6b74912b191d4a6b8cc8f0e65867c76d3ad107fb8ee7bcb3534a30c8e5fd3bf253665d8369e5bd310db66c2b9138ff318e1d0724cf735d3cde39ae96b4337dd74311ed3b470451e1e1e1e1e1e1e539bda5cbbd7bcd7bce6bda6bee635af79afa94d6d5ed3534f754eeae64d531da78b4dcd93335cbbd8d4e131af79f335f5f0e2f46233d57eaf69e6e438a5a6a92f36b5ce1dea1c3779cca136af79af9993ded4d4663ad4437d4d9da34da7cb939adaf59aa9ef1dea6b4ccd54eb983cd74dd3d43cc3344d53d31ceaa11eeaa176f7abc3a3c3a387d7bcc31cad738766aa7d62f7def49ada8a6953f3985adf6b9a9684d4fce2c3ede2c5f256cbdba1fd8a821a4335e46c8bbaf2bce53167e3448b201a56d455e76d5892b9371e4d82f850a7545942442a8ecb1575b5c7b25f2d1ad9afd698f3f60dc593b9a42b512b549b82ca14a92c4851c2a8d6daf4edd85beb25b06e470605585a17b6353b246e8bdc54912b46d65a9e8b1552b82801b3e5cd16d5c802232a06da9b15e22257a54b89981475bd725436a82879c1c44444bd6c2fc0e034d6d7ab16039e6f8107edc9af57cc89bd4a62d66d8d259d7493f4148bd8e0a40b874c2989a394f3a44e083aa55f7990d475c49357470ebe5a31b21b5f7eb5683a56edad857b522ec7d2af76ced6f95c20117cbd444173bfde9bf4a4af97c9a983af172aea98f6eb9d3a52d7abe4495fefd5f1c92b46d696f5eb452b52d7abf6e4653341ca41e1f1eb9d1352d75bf724f6c14044aa5f31d293469cf424931394d18d5ff1140c75c54aaec48ed07ec56832d415ab3d4942e1b627492c5783c9af786e85bae2ba27b3cf9319e849d2b311d3d4af1929273d99999c707ccd5051665cfc9aa752254fe6ab7c0480af598cec45dcaf194d67cd6a996d275d73db93192ecba59124d50d146e4c55b9617fba8fe0e769e3e7d9a4c69fbd33c9799270deb5d3b6fd5c78f1a7efbc60e799c354020aad145b57c26c80a967da12108f33b22244d435b5470d14017b6164266d0cd902985ecc9d298991b1cbc9c971dbf439ae7372b09a98f91ce3d8e778b681f6e4106dea241038fc75c403ed15700ce81668dda6dc1ee81a78e4ea815847b890c840d2e581aec373e281ae4292ae07fa036e3cd02da064ee81cec292ae077a09981e88c5a4ee819e6ba03dc0243e724367ce502c11a96bfa24b8c5224820e1eb49f85961a468c78b283dc8a824784e427b4820dad46dced8d894606333e26d6cdc12bd8d6b1b37a5b6bc8da75261dea608dcdb18c97a1b1b23716f33c5f5369e69a03d362360490dfd08238c3082e71968cf08449b7a9681f69c500f00d700f08c84f60080685313eb85d27bf0e0f51eee7bf0e0391e80ee610577268ee058789dd1a91ec2e882d4ccdaeaa8b05cd43d10e83906d5d28df3f47c84f69c3078caa83d0e8e6723b407876853b34cd6f3303cafeb79ee38bcd3792816b429b626a11e402acf8a992070766b5ac2ce00d04dfd799e7454fe2c93f4a7e71740201008f4eca28847287c218717ba700d31e28542277ba16ba19ba992f1853e547a21d652da0b855838945e98e3e78539a8bcd03311da233ccef834e5fa344d53a1a7a9f36c7d7aa65b690a4c539b347533ef6808d222ea0c8809ab899a325df9d27a1165034b25f2642c320624ab4b8a840e09da0d2c4f8ad4f586d4dbd8786e918147209068ed68cbdd97981c52d435e78165deae6029a9a212a4ae3a37763c1008fcf2caf140a06716680fd07863e88542cf2bd01ee16d32a309135b5c142a2b9a748c6427aed69fee2c84a0980342922446a49e9e87b210da930600e76b407c4d88af195133a2c692cd40c161c6d2d7f8d0bbbec67366d47d8deb2cf91ae7a161f735aec203747c0d565295aff1ac02eda931c2f25a67bd76c7d109c1d3f53ad5585a7bd0627abb60f65a83bed258235d25f23a8579adad34d636fa54b361b64099500245cb5913950ad7da8f24b136b724ea9abebef216c6280a52122532fa465397991730e6c27409cd3911612d255142312309521063939d9538a6187068c727aa14a6205fecb89808f2a413709e1cd640fc19e2cf11476f7fba3edd8c51e44f4f03a688a874aa19a93339c81465350400000073160030200c0a88c582d13c11c4527a061480106ab2525e4a1a4803e220c861148498318618030c30060c01ccd04c1b00f62a0284654235f05124d754493236ec89ba8a96e8e682896d2801b18a9ea34dc862248c5ff0af1f59fe5e00dbe67e07ea77fb276e08d54b83b6fa14b5ddc27b7368e8fc04850d6ab21e6016d90b62d6036d14018292ecdcd440b1c0a68c043eca77a68f1b503ee4b0f33ed80b6e1323c22bbe262565f660ebb707fd884f3e50ea0a49ae1bb395212da9badb1a66ab862b8311b1256f1bc4b0007a722d1d7ee2d2d39cd615d57848da806668055cca51cc07b0ae6bee79c3db45e5871d03f82c2fa627ef9d03f35372b319e37a9f889c9d0e3716b1cf7d17262a635c32686cb134d7f22248e891ae2f3810b7c661c219cbc0b7c64c8e863ef8c52b8520aaf2937de4cca05519088e3ae13215e01c6f5ceaed42b0f528a267aa11f25c6f86615e30ac7ec288a25f3d4ddd367623693fd738b9232b7e0db58597b36c0520935c5f5463f5a6999b940acee05420a9547688c181d3141b114b91244ce424ddae74506ef32c0ef992e26ae2375bddb347563ae90dbbee5a8062f944a56f328f104598e0beffe52dc456323614c23fbd2acb7b9f0e85bb8692e8eaa6506b0accfa2b0693e768d355f5ffc1231fc3f085f5abb103215ed40995b6885cb8d6f9aa63c3d724c054cb1eddcce17078d4b8fe28a89b39a9eb374c91b4a134e46e2ddf37c8ed0a53006a9a1818dc0379ca77d82cee8379502a02bb8c18db2c91720af52a606fcb0289ad538da5fd370800aa81a81cb7bd253a05862b93a4b4643915963f343efc5b5dbcdb525f8aba432e0fd074604ac5037f7255a99301d6ce11401eab7a684977e800396d3bd90e3b1182b60159d922e9679fc6669e688db35423fc2d09cee3df90afaaadf291a97774679ff0e4ce4152dd2ce489cf39c7cbf00f1e491b200592de2a7063d3204168b7a4892956a05a2521c8141b24c95f016b6dd25d08cd3768ae8fbb3a48840e99499e543e55d765777af118926071a1240d80c48746e04875980dc4f49215e886be5419c4f2ceedc53cbdd21ee001cfdf4cf98a638c28cf551a3a6f0130d5ceb524fa36ce60dd9fee2ddab9cf677c1a588724a2cf486e70b55bfbcb5ace9850cd3891b03a9273aeb58039c2c630a72c62503157afc3f7df96b0e8412e0bf484e812e975e15ef75ded7437f31c85ff2c1017a0275fb37d6f284b35be7198e9ae64b8a0bccdd6b64e6210cfd894d64df5672d24d6e95e7b90b6fe682dbd160bf08f61b04bccc30464f159bc516a9fca7f08770282c183eac91d3d7190ff86d80883ca9545f3d6982cf161b32a9b89243378d52afec98275b5602363aa966c18bd549d951628d1ba654aac11c6fe827a604c0c5e9ee3e623392e661b83ddc902d7361903720c82fa15a42e51bd8dc56091ec23982d3b18dc6ac83d4e2d2b8983dd7695387d5f8dad4334d2a1109230d1d7d0810c54a6e023eb559172f135c5d5c04e417d56df7c976776f53459ebad2a49469afd73cfab35e2b42955654927b4d3bd03d42ef92125e2d6eb51e054ba53ad26b88d0f8cf5d22ed191d39c5e644dd64a301d0394815898a5976ebaf6b16094f2cc4517b99e637d9a1620e57dc3bb5ab047274a30368c7ac7213cafa46eb7c3b477afb5c93f22f35e8ea8853558b87e8a5551882dbb60a55551bab8f974ffbc0c62c02e33a7cd72a705e185bd5d1e054bf487343609bed59e81b1596c40106677b5adac540fdbc89c2a249e7e4f7915e324157be08ade196b3d110df32a8f980727e4a39bc959215c3293e4a9b1d163f265f938301f855da3d1c28519c4704929dc0cfb4d590f7b769e977ca2e898bbab0fa66d600b3d92e5e797b08a38106a08e10746ad6b0074f056336b49ab368515e1727feb2110d9b88550864ec90ce1bd3fd4121a317b08fbde17b69a954ccc0f41b08c00d8c5c0e7563262e5c368ecf4e2c64a0f136957f01413308e62726cc70b323a8cb9b1bfae79a73ac0ce53e9354d783c969f605df9a5f6e02b6a60e084a1fea3a65bca20566b44b0837c96250e9bfc4cc0e63b1f847c117873aca8d31f8b3c03f195200af892cf9b38b0deb6baed49fdf36873293e424d35eabd69feead8cbc2472ca64b15498bf458b91205b73cf972302678415791f6408145330e43258bd158085ba630715d432554adf1e5256c0dd91e3c678b0b6831481141421831ce8efb6240092ccb9b61358430cf8ec61a14e3cb95181526c38b63985d65f83a25ba683b5763602a12d7010b3f9d13aeeb334df3c0f4b4ebe2f2a9532a453abd23417814f5a2e88cba936caa1e0585ef5375c023f2a66b3be608cdf7559204d9e34e3e3b11da5c5069217eea1c526b8fc440fbbf497b80c0a4f39713e5262c44ce072549dd74046b94cd0f3dc1c63ce7c78c25937a080f16e02f5e776ca7b6ef0007ceb951ee2086b7467239bc446a7982b614e1857818b8c4620f57e980e4814bd1d7eb920e118b8c54e8900bc772179e077050574a7b4d9ba37dc94680023000b2c98eb7d84214768291a0887882861b71bc172e80c47eff6a48f5d10b040a6cb15a47b5f8e1223473839a2b28568c400f64fcc6d7ed3ad3268bcf1ed54f36e3e0bb4615cc5d9065af6233d6a7f49acfb5bf9467cfb10ef75b98ea0c6cc2e6b79ce8ef89405746e4a80188a176d491be364aba52ca203fabbad8ee87f5ec8f802081309718e3596b09055dc690df9a0afb6a9b47403c79bc1013aa166adec8d9eb005345a5d66c7855b7cc5f1cf44bbe37ec55cd82c355dcc1f7590d7e13c96d306cb193ed4ecae10ecb71f0220e9e2553adaf2bd5839f4abe5ba9f59ca937f1344f159b862cfdf13b9815f0673dc515e5c9fabf199a51b0de1b17376e41541b0544174200319e211741e6b4194533f306b6362a773ec1e247f06c8c3576c4ac60334a0f9666c7401d26538113865ba88f3a32568b1e501a373ceb012a4995a19513ef5b2993ac2df42c226d8fcaf4f86820ffb66332f20ab74d8a4df29429f632e54436bc03dac03e04351a25ec10bef852c61585212043ae8dd4a0b0599bf49c01e1257155867c77028082514a997ad447209aff2768d902aeaccbc2dad42e20ede408f0c909e398f9af7f899d4e2b3c8a32c168054ad52f3c4706ee1a80fd629ced72b59c8d5dda06f1396766e6151943502a6ef41b2def18069330898f47c73fe4be0862e2c829cbbae25d395131cca27e02857aba980ac89610dc04c447ea6c6021dfb9cae346f9478ee5e86f9b23294fc4d65cb4fee775b33ae3ffdfa9270e3063d06f3d3dcdc0d584f2d7c34382099a1a35a5eb8c52bab7e213f8077687908713fa57f6b04215f4f38304da0f115deead0e4242583b28961f12cffcd9761bbf4c21319cc10077638b54973f3b1fcb6e222c682666b555dbcce48f7fb6c11ed11ce7d38e0e880f49d90b6d23d4d4ba218aba41a8d38a9edab016f5eaa5f3b7f99a0500c004e0e33656ce34c201225b3ced76f83fb95dadcfc142d15d183d3072e34cfa939a2817b32e985442c31ea4bac3814fd80541b45219e042d699aa699375188fe40714f3b7f5a6cf882b87e25ce466640891528a0b59d02096735448a0d08224405072a8706a944dceb79af02dea5e70f516acdec03648fb9a2a35314d5c303509e1dc3d76aab0a10509bc310ad2a89244217b571b063ab30c30c36cec58fd5d85af392153def463f79c760f5c6c34bdefae4a7132bb858aaaf6c072418797c862d21e0fcc8318fb44459d13c3f5f3003338ec26915465c4f059d7f34ec358525a535e1a77a9d829c379e30547b903ff65b3a9b5db793e00fe8615ef54190d1711b333e91dc0704f360fc0cc6269aa9a454ab667e5de6ecc2b03b6e0a4697a799fa8aaf169e52854d9e202bc57ea31fa48323ca84ad8f0d46c77d61b8c51e51b418526f03291561c90f652a33f5662694e19d5dc6634144e04a31ef2dc1e91da5bc8258ac0347e487b7b8812085d88ef34c0495dbbc5b4bc8895327e4ffcb83bb41477ce47b2a8f50cea98ee387f1936ee723b8a5c6d1b5eddaa1284b4386e1a2411e7a19859917b7c448bd32608592d281d7032f34f79a52bec6252a73bb5a7892f896b4de111399e102c2aeceeea5695cfadf70ced82a6768b42ae913eaf38b147b2a5cd1a0afb34c68fd8c272d322447ff05ab3e3cfde05603b25760dda616ff9d40c3ab0e91ab76107a4315f9333331c1887cc4b72ad0f99cd9d88b228354f46ffaf2a3f3043439d5cb20ad7a1a1dc3ead159e7691b4b87cf1d2062aa23ae1c6b00137d6dc1c8b8d7fd9bd5ee4318a5bc323f29e9d3a6da54c740f695afae957852192825b0a2d2c5b0cf10b2410d74fc144e181005932e23d191a8488c0c16ae68df8667c8047ebffef3472a40343364c4173aca467424ad11153d0f8da3012668029a0cbf6576842a4b8cd483581dbc34ae42a35aecdd0c80aafce7ae784e8ed7abb1f36241cdc707f49e58180594bd885fc84564561afa727b96f8434628b2a359f1aa837e6a604f7d0bb207255182a0ecc487f9bb5a5a6a893c3c0e029020c8dc2dc0780a3db025f6df4a96a12e3ba95a9ff1b75cb9e17638af62da335968ac557fb90a18674e06b5e61577d24b88f3ba3acafd568533937d03b847627929b08d555c4e026cfcbb5238e8f1ea54dccbdcba3538615bf3849158b848bfc9f1126646b12c3abc028c972e0c2615fb7eaf039a42f02a1d6860a19213b40a106f30a54702bcee2a9876365a4d555d6084cce7f0313bef8da21d5501839f4ee29edc11f2a4d1097fee8d042196f19c661a34d73b912d50028bcca8371651b176f3d971153638e36ba83d3a84f82666056cf8276fb233c92ba60234e95bc95bac18093d14607ddf632b521c3fe05d6ed67147517d612d3be6968dae5f6a1f7b9a1b707464fbf7c84d66d9da6e546077cd9cc4c8b45613fba0ef17e1cb52e4518cdd15884aa0facb81308a47886576ad5039de31b711aa6eb0813dc18cab0c6d061bbd3ce8492749778a6e9d2e869e715aa87f4935a6778f20b6f2d671a09f52c165664768374de16873882d22f839971a2ccb0d42a447837d7d2a8a903101d85b3c346055d73b1fefdd2097acbd94efbeead2cb1c4fadd0da336c2337003ddcbf57ce7879d84948fe3f352fb61998e14f32e880cdca9ed8689196384498d4ee4315c68f7d704c04458fa4ff7138739db74ba068e9da7b69a7594aad1a0d0c87a089669f987e15df7f5078a954078526b4e6059d1019c015709d7a8d945ed6e5c83a793d8cc67688ce1d4a96ee8f703f3ca833ae4cf4dce51bf2e664387699ef0a0f17409cd38a0e2abad90f91a6f82cb4ee951b1b0550db54c1f21f815233b9bb492107b750bd83d7537d349eb1ff49a845043c30f9a7dffeaa6669c8d7600573373b228607b0062242a7eb220fc7ac9326f5ac031c19352f3909d41b361814db19c3a8821919416355833cb5146d2ab8b72bdc00952bafd38ca81e888fc7c4ba326685db28e50d8395e703ce03c327c0c4719e85874352123945c4207780c5b4fc1adb312e420021da907df59c66eae083a3247390154fdf28459c4baaad0a2d351dbc3b8c06f0fa39a1fca58e89948bbaa04145415e882d7bae3421d010b680335d4baadde2c00e9185959b1931a8a1b626ca8c01772bc1bf2c42d59675223436be8f09c435db2435a1a6d6937ac1bd057a0f809da00a96b1aab69937604dafbf9de688cbe7824ea264a33656c4d0294b9f853949899aa8a1fbef8e10a558ca5afa070cc06339806f5fc54268613c1c9ef77b2686ce880ca57d4b4cd8e31f4f633b7d3873862e84fe97285ea70bb697b69986032719efc9cbf97d2bc23ace9264432811a7b611d5701ec4e064e1e87a25806bfd624c4f5ec9f30909d2a12d0f8e9aef15e82c5ff9b4b71babf4236d179d9e2666859d062124939ba10cf490495016eb2b64904828a355e11362002b72eab410799e6a34517815e528dd215f7a5411b6a288a24c692154e2f1cae9fac0bcfccc374ce79d3937809e03569bab05ebe04577eb505dc6914c546116184c247f799194df59b8b84f74691dc981f2162ee0fd2487316b730cfa1645cb65ebac481e0d432b2d230122ca532b47f048a3908883322a63edf83f989c0c1633722df693805d73b8b35c61f848c156bd8eb7041040bf5a924c5cb5344ed2e0e8ac6a438bef13289d79bd6b9e1c84330f19547e18f075f612faa25d1a1c849c604c04fa307869406ecfc193e0a7628d8b0bbe837e2bec0af4e542864d973bc936b6de3f4ccae293a4a305afc3e7390e18a0a414bbf5e087f459c0576a2658faed638a4d2c32797fe9b32d8c64f049748bc11e6cddf4ebea6cf1a00caaca83772b7b195e3156c398637a9e527dfccb97686c8de0734f633a10eda245664609dfa9bc42b046f9c743ae3f045b3baa2222ee39ade60f5e8c396d23ee4e3fe094fa2e126647d2d0a43bcd4e078df356ea0a1d1afd144386fc7df931735ee3c0635c88b463fab086fbddec733ca0377f9ba51651eed534bb7e37795fc0913077890020fa4a8eb37fb2d7879c8118931209f1780068ec15108a08b222003c319c939c3d110dcf7e7bbdf5f3c365d7fd3033ba4a5a3f972be21904c2b64cff829f5b070ab2421b9b12093af3a0cf0d6e0b1c062d199129dbbf32e6acc1d229f0852722b011cc1332a58806676d19907de02d8518fb468f0bece2aa75009ec9bac4d417aa26a173c4105aa2d01787e12cf6c7caf1b008d022c579849f30133d72a4f21d7a48361de8f585c75f2ee6058322f92777dcf4a099f4e0047b1e70b964cd883bbb6f81e3816a00c5e8e15c01d8c2d1932abe88ceb1ed83a974709fd9595d610e1f5df5bb88403d9a55b1647c739d57cc6f566318cb467cf63853f1ed20b2a48b27e2910286caeafa8a0b6086027aea52c46b63d41cab5c3ed7b1430f2bc90b5f1a2b92b8b6b4bb4f995660f3be2fd90cd8e4c624db51c0d9339ac7ac25d996f3911074359b2ce8ba8293ef025311f23b40a6e1165ceb8daed56cb5829aa19574c791c759cc1ade308111fdabcf5a6fd141875694eab79f3ac6993b7445ca4420410597696c127335195e893f34a98ef7bb33363b2ee802daa894c2d386e617a11a9e7277a92caad352702ca7b741328e4cf2510e0f6396190003c0f32fac9ec8a55d1192379ca7d0913edfb34a3cc645a6e2227c4cfcfef4c1d7bb9c947aada568a0ad74186b75b1ba3a4e472daba8a40551421faeda8c57792c34a63c3dcb7d435db0b3e2bb0b4c903598d3750282c12db10335cc711a644ffd2f8ade3c79b7243311253bbb10f42a85f018f0e30555abc068e480e0dbffd897bbbddc503f23852cff8966a55e1f76da65eae15331d81aee4dea0c8619f65130b1d661fd880bba4cb45813c85eeb019699693561a721d69ca67547197b714ecbfd152942ed86cb23560409ee13efa1d50a0c746d3725a3458ead8a61a120e605d666814fd3dfcb0d20e026b9e2e9b2f3316dc04a44bacf678367a89d18919ebad0288552e4483d104d6d832a5e55aaca6e0757b0471057cac14b0f62c9e5a0c6924f261456d9c463ab4b37940621563f34a05969fe207406ba29526d668709ca85662a18eaf9a183d83dcaa209bd020b666ad800f4e1d683d133ac8b6e979abe8eca9dd00f534a429a57f27ea18882d9bc557c6e586d703c4a637fe07f09784f59335bf99a89d9e9c65222f56a190c97ce9f5354751b08697345e0263de5b1faf38461216e101fb7ca1670f670b5df833c4dbb5aae5b22bd98ee30d4b5d693538bd91452d8e913b688d5caec78e4fa55a72191cba7b3cf74844cca3a1540fc742b863b57631d1f9defae03c291cb5433532ad5435b4b2e1a003da226f30c24adacf0677d0a4dec0ca2e5b65cf06b66a9cfd1c520f75f9ce9cbc6596b5f9205dcc4f11d1143d6ca53bb984987d2719da9eb49f18ddfbc25e2a94caf993e54ff9e1d2ffc396087de671931ab770229892da9f70ae0bd9337afb4ddf248b08037ad40c067f5f0bf90a672cae21f65f08f3e77a7d8a45c709d7efdc9abe4ceff9b6b0ae9aa99d743d1707ca305257ceb14853a19b2f534a6384f4a0bcd914522c2fbf3f3338722177370d776862f939538d8f21ee9d4b90bd8bd0c88e837a15c70c083240035eb9694c374514a895a136df02e3e5772acc3600894178bfc81e11098cc3e10c92f78353c68136ddeada03581c16ff88a509e55c98ca52a9e0e33d091a754a2aa99ca12f0ab620b15a56d4bdbaddfd8ef24551cdfdaf486e57209da34482d3b0fd6fe6dc349c3f843a1c76a3062defb21b9cee47105c4a6255d963e129bfef0a377e249dfd3bb465c34d23763ae400ee1529d7472d88fc967a954a806361402ab1844a75a407c495db536c283de0cc3860d7470114e197ff0f9211c883a780703fab8d202af80bd908d67bfcb90233b232babef44b68507bade7d0ffba219fbfe54404345c5083a5ef5d4f57ad4c1e0f74af7bcb2717b9f4f9ee4606bce3efcd20ca6e2225e5771d1f332913338dbade00d10b4f7a1188d7f69d85f330e4bcd2c8bbfe990e4a257eb6dd076670fc3181731e35b08c53c6e7ac3e7a0f1c52a54c634a7c980966ca334f28474dde7283a98d69e4a4ca4c26806a4b3990d2c339f181147b07443820c6d02a3196e7186126e29f764814d9ddfda438ea62ead184865a83091b237b961bf01060e8f1941ad84bedbc0cb3ec5d72896ded40d5060c2f88abcbf38bbfbb1eb41087f5216e07b57edc6f335350a3c924625192b7c0cd6133b26cf30dda06a5ae9dbbd98f5963e4a2e315cba72104f986189bc70fb7193ef7bfb45d0de59b63978bc1c441a4ac31ddd4f96e44d1cfe03b8bcd96c9ce0ccd703e40c36773b2fd0259150eb83d7253c39094de2c181d6a55de5941203f2c3d11fa7975a24406c513ba1c051d4f33a4cdb3eadb4d229a299f850b2fa227e918f81f4cfe50272b7fdf9a1d9a81fb2ccde3e4a98f64906f2d18964795391346c87c3825bf14c1270e6d12ad36eeac73807c3b24e766809905ad114b14e3f291e0e56aeafb4d7ecfe393d60084722374c3c2a1847d2ae1fd0b52aa68c9e038336b662d7b867678d4c704f801ae98cc1c0dba253974045eade9d836d2f5d246705cb5b566757be9502ad2bd87094868fa65864ebe61b693180c1f126c5e70e9c213d0a0c7137522221a6e7a2cf7163ec6c3c42d2af595a9cd15415ae93cde70f9f4b949c535e5f0cc8ede03fce697e1ab49066bd84ddc38b4ea196d8e1f74f9eb1b4b08888ae3d52160d012a473c5bae84e52e335cc239d5ba45629ac70b16bf851170a8284ed20730426eb6bac1a86262b73616097c57e05bcb43e21850c0df74ffc17427c138d466baaed77f9b1e8693c7fb7ed9b4216ebcce70987266cc5e9b841b74de5a00ea6b2c185c916de5eadc343640583509a6293d78ecbdf774e5b352ccb4fc6dae6c02354c43918f1d700016812ad9c5b4ba54318b821cc76468552b236fb9e3be0eb4bb8b69feecfb4cd22215425e5690b069db680e12247729d085ea513cd83b1a256e353a2e2fd5d8ffcf1cf0e7a761f9240c8e1a46061077545c3453aca4477ca4bcfecdb3de93343e6a7d69721f69f47a5a0e98c4c04df235eb205bffed5cad9ec22ac320a71bae1e01d3b89562e22410c4d90ffc54e586622513249948614156ee71cede842c80af6ac46d943d084a632b720f1ba543c9c680562d68f436afe4547522387985935ba03a32ce33c31d9bddc8118be29fdbe8477953032891ff53f77e5c1ddc2f5ceeec55cd539320b7056bbf9fae7c0f79582cfd141badbc92f47d80c850609888016aedd0c4b0e4b62a1b9d11834091c99b48507105cea1bfcede9cfc1c6ebbcdd85b2183b2f8dbbaf0b35aec58d8d0d84762707241b7ba8bdc04ff58079fabdef8df21201566ecd82ed353c8f5105411bbefb7c015ce1ab1b32a8a7eed8f4eb42ec35178dae40a16c8c2ec30e3f42b747dd1a377b0c925583f0c5bd172e86204c793bc314d22a60cc20b89bd9156f41eb97dd43c00d21281fb807a0ef3dc7f16638eaa198cfb2dcf6edb7f3d467d6cfc09728e4b8978bf76dea17b53c0eff1c841446600a4656b935b16b1bf61f76984c83be1fec31e1a0b02824f4bfa32093d3050a650d28d168d41358698af38f72422d85494bf0385b4478cdc86460c6299089281a89be2d1172e7ccc6c40b6662a648f7bd04e98b181f31a2614fe0bf6df0e0f51333fd1b553c30115567bc5bd7906a196d5b92e456dbb186fc91271ca9c52c2347db21c4ab46b9436c0bb6e427944abaf58f876397c325c38fee0523acbe466bef3442e6795fe9fae6606b49bd094d32a032c74c11fc30c111fa4cb84b5a437215d3d30a6af49f97a95b6edf09238d8fed52dcb533748aef42a7a9fbaab1b50b3ba8e12b6f4474ac851525e202d07fd8d8ddd54344e616025eefe2d3e5e62dd9d1738a08666e4f1728ad8d65e5b3d39e470d992b01dcd8300062e66ed74e01aa4bc7848930e4d56cee5dd6b0d0bafea0b787b15062857fc70aa1f43aa251f60b7fb6e2b5d3ce4642fa1a017a633606421987201bbd889f61f911562239a8f6f7a6f65edf954a2b7ec08db6122a48b0b7725688830be17e0a92e4da36de1206d36f7b43c9bb4a80960dd98524897c182d21a61850092010910c1215962e29a38a0744b23242722e24972646e7a6b768210b52a3da10ad70247a7f565eab5ce4a4e96c8150a2f2a35ad3b0f1d8d42aa194c804b72b422921d6c59e97aafaff67edd04075a09e298ad00922e1b4787e657c1f801a2d0a50a2b5e5a8230f6ec33b45c07327181afe917a75b5d7d8d6814e887ec03f4b3334a2cc9d0bcc5a677310e96a6cea4b12dbfa84adac9adc222fe51f762a4163af0b94fed0dbd8f26283f6ff733e8e402c52687679da8a2effdeaecbcaca2d7276dc1e914ede3105cb3709408cd3786b11b3044c8d18229354e28e297cbe649661664274295f01db6e8a7f55e7701ead1cbdccf3bf6376ebece7464aa4cd0b44e17ab8ae5bb1be790725b29c3471ad816d461c1c37c1bde38833eb106239b739591c931f28fb986d07ef757f377f7cac81a2576b3ae8230c4a6f06ae97e3100da9b13246c98ae0f979d428ca603311bc3cf18695df73d6e74d61bf08e2438b314d474df7cdebd606d24dd31fe811dc2883cf9e8d5a4910c10840c97d9debfbab0b08d18ce31ad41a8b75f42af4ce20644dc774204088c14227447f22c1bf109aa02f8cdd777fe3a0683455f02df859368e36311c2b76bca91c54ee2e7c19990f0e5d85aed81c155946c1bf644c3fe10dbf00744f082e35f1fb6963fde9c416d1d3e4c67e52e75f1622bd9231cb4ec8710b07b70c765a67e3795aca28211ac30f74f635204f5726ed70518eb945ac39bdb22ac22957d309f0fff805eb6c5a02e9c1cb97034563458eb9882c6b51cff38b5c179fa4926a1ffe1a2eb4216c2db8736fe1ba44334bd87fe5ea76d2caf27b7623d3bf8ab9910bd09bd1d03d7a8780868f45a924f050160cc03f53fa0358b950d1f80602a3dc5686e79c7e526e1f52b89947ef2aa74fd2b919a94fcb783e0b8b5b24992e4d197fdcc1941183d47f93b37dcf762e00152d1f1ccbc3e81c93cef4fa758a86552cd2093226bdb341506020ec42a23e4ffd18df28aea802e5380dfe697af1d7ce43697f91287a0c2d2a06f2048f549c90e1c94f284797084421ef2f9716da848085d0eafae3dce80a5088aaa708df5ccc10117f3beb5884603034a1cabca82613185d348288769994667845697425b8113401aa6afdabe00a827e175efd4e603bac8afee81bbd1b7d42756dc6646f332f58599155413b997d01b3e3263044f70187cc0f10f3b40a55cb9836392796bfca817a6bd1a3f2859ee7abac4896fc58cf188e50496c4fa305325ecde34b8629fb5fc689ed34e8b5327018f53bd12dcb2e43bcf3ee4222da54f2d6931d7c4340e98dfb6e21683caceaa5e4c1be5c6ca3d72ee39aba066e2193e24ef28443204a3738308573795231bfc4ab2c9d3ffc9515d6b78a3ef1f14497d7f557c070f66255ffc1aa6fbec75fbc30b87924f361c973b05ef57fde420e9250d2089a10515ccc1ef1e45c4f11d377c2d143797307cb8364998b75bf2a3e5228d10b72368bb9bba993ef043a653ca4e8ff16b33f7b08958e36617e4f6bc2eb41d3e82de369ff9eea17ae04cb7569cdec20c286078ac286b2627d967a4060c6869c7aa1bc12dbcd540a938cd1727ca4fd67d7dbcaa6f49a64ef3980595e4ae81addbb05d19ab283f325104e26d4adea1a174d01bf14a5d9fc726ce133ccb744313f5b25580f0bd06bf5ef25e6ff158a2d35a0c0edb2220eaf349db8c06900669704fbe29b988f4752d7a331427f775316b5d9c06e93a8ca35b26b5fe1b09aecae783517a1ed24ea472415412fc012b5d481f2e6dfe814dd426788a55d54a709723dd0002690a9ea42a123f4862547e9de1ce91eb38ba16dc19cac97227c4ed1d0acb0324037324408934f3ea33a6b760c7e29d0faa43e47b6b234616915745109520e2932309e5b844c281422b6e8302787a5ca160d13aca244da473a9ea0e2d65b49f11b02c206eeb1b610ea5d85a864be6a9e8280330b9eba1182d31097ca2684fa8bd83ff8600f1727877650480b7196949dff049260d8c1f66116a44bcb1615bd62a639c50b98bb6bed5fd2d3350af487fa44985f4ddcd2dfde6eb48700822b6f1c82b41832448912f6ac22be4e6f9b4d6600e5f12ee93050994463d040528792bb62c19ca0f641e8ebc6cb84b0d38ca7d5c314df7cc228dd8ad79c2535bc5b21ad7412f4b6d06a3803a5275a2a2597f1ed5011d1b2bf7adf2943e647a83183d5a5d5426ecb5dad2b61a6867128714a38d9e8dfda92466219c3c74ba78f8101d1b9a5b265e9095904cdf88e878e01e021f4408ee675490633301100231a12977249f5830ac09d7c651cba34211226a164783b59b63d6d34dbfe476916e4625a38a21304f67e1410a76ef0accd7429546597cde1d7944b76ef38a78e754a33e7b0c2509c68c7fb6072cf5516e8dd520a1cc37a6918add3c53e53481357dcad1ae3ff4f8a09ccd4554fb9aea424884b9b1bebb9d069bcb49362ef368cc340f8674ef3a0dd0671eb1e0b2030235c4b685fd307dfd710c0c496b7669b5a1f5b6fe7264637a0b1c1779bb85f92e7281737b766a6462547b823eda23028dd29979aae066c2e874a56e55217353e14bfe628ec0094483161faef6a7bbd81f7544ee47cac647969bc1b6f710f1508ac87443882e7ba2444cec7db249ecf1cb24301f1b45c3c4ba4d9f482b340e2d3a29c1a1839f5e9fae6b72386d81d4814f58c363e9291028084f41332364123619a1f054aaf0ec3f8d14c1dc8a907a4ef891403597be096624e0045a960bc1606720b0c8516345fd4596f9d3708090e1a01dce36643d5378f393ddcac6268a052c05ba4e3e29aa4a80144afecdd0f8bf988c34c73282e9ee077a0f20f8b50a9823ed7cc0ff46171256e410a87f0b088c869d9c12123a646308446041bca7e8b5c3f6625ec3b58005af690594c195ad46bb1aef5904ff12b3d80610fd23f84b29f75351f4bb27ac74077227026fe88ba7efd10445188a0f52d005450a56c09525a680355ed0adc47432a0fdf18324025fa660e63a8f535142be8d414b027d4ec8076f1453e8626d012add11aa632dbc6f04466742e433037d41267b476c4ade82c2cef33f0bfac07e080686e8920665932a660732f26919d0cd02d01cd05de1d8bc96e5259206540537836adbf15bd0b6ff1ce373b173ee7456536618d0793fed66d807ab4af21a23950bfe5cc73ff5914ba52983ac133eb286b09f18230ce470a569480a0ee1805d7ee46dae62698de5311c0491f6452cdcfd6cff89bf223c02914f0b8f1c6a8297b246a24b5c4e314913cc40a3dfc17813974ca038912d37448b9424cae1999fd7204558f9b144b960fbdec683264d38791c9c046ad4a7a7ed68f1e7f8ce9619f67b1b7c8770b07c0a692a2a59bda80a0c06f665a66a2534e5bbf544d7718863a9d726218feb55b196723191389e6ea19ccd5b063189873b9e90cfe1c9a33ad88a16d783c552756b49423f773ed5794b1139d038863c31cebe42a121353a8482a889f612b357cdcfd7e13124020a1834c50546369b6e7aebd98df2eab97e7286479934e5ef3319a926e0d50a7dc1e155c400c59a2a6716476046d8ee34d4a0d81900531ffcf3514927c68ff91956de35a91fdb12d1976a70d429b512fcc997030f715ca6dbd0bb4c50f859149590dc25ddb98a2eeed1a6234a28d8e7dfa9c2d354b9bc9291840a508741066d5f301fcfc3866fb8914bd81fd6a0e24f9f3704d4b8b9a68d3c34967c6d4c3bc75e1d552268bd0164225172cf33448fa5818d9b7d4918b37a71b6544ece326c82345e8d311029381392641c65291f53c5684c501477213fd9a4712e0f38180cd5aa7cc1a94b52ef28ad9cac31deec5b82f3b3d4d6a00953a99d88e10d11228ef1991d8df0b5020a0ee2f4d6d309fdc420c04943204ba595910cdb58c3cdfed85f958e9e2d0388814f6025268cd7a0edb1dd2bc2282aa0e0d3fb7aa035422768eab1f04abeb00b863c2423ca539e1ad783824d894faa744b7729e8eeac34d0f387c807dd951a0c41e785669b42b78d2fdb5f4f2521a326e25654472485dea4b805752cbc59f17e67e2ff3325aa0d7e4db6e2c130d18eea94cc2fe18f9e34045eeb7857c760cc6c4e5061855d281dd348db51f6e0fedef1630a1539088f99ce909a5ce8f823e882aee0c9b31ab7555af5f5d4917f1fa70cadbafce3ae01656c755332e28a7889f1b3a4617f1a1991ce2caaa6a9e802c3f0e97ae8ed401d60cf4f3bec681a51f5974b05c04c6700bd7d1a2e23f513df311b963d09d72b55d1501ae84180be1715700a22f34d026f1eabcb281f64209bfc7254d2c66144c89d842657a94a29727094dc265c07c7fc8a1967ff53260bcf0fb26fbcd816fc8ccf8385d8086d814a70022d1e671adf1b456b5915bbb0342fabad883c9360d77e27ca0a0baae072784a753a06f6d0dd5a6ccdd227e99257b4bbc638c63a4f611d0068d07a2727216a79a90b457bed6422eaca4ea98f77ba9f9f3e215fa8ae0f026f68f34be5f1b5311c2e61f4a17936840963d14b0d4aa3a6ee4cc8b8699ffc66d3a9ec85bf216c7f5018ddee11affeb2dffeedfdac52845f5776e55022318e772fe24900868ebe29895f71670500917d713908175565320bb653a7b0c282af680f1822182580cddf16aba89a49ea62232b9149c3992d9eb6735e0fcde59f365746e11ac2f9bdd63659042e8ce220d20a06e6ca0f0792e06034aadfa62a7202a9bb500b6ea1ab49a30639733edd7e72a1107e101816efad8339b1191b0b0cb43d20585737942850163ddb08a3364ca62de4dcaa4120dc413141bc0b0a759d287101471b7ab52492da97bec8aa083f04f18fa0bd2a0d43d2a4232c72ade8062664e3c402b3b76aec5c0c90d21b6acd1a2401f2157839232d1c5fe3baf7f5cbc94517816f3171766eca5c888bc5fde82210c0697967b0fe226937319675e3d7290741f6727c4cec8dcf11942991870085c93895959fffae11ae108a6796c753277d4987dfd50f027d7073eb4f91cad30d2a0aeb09ec3b30adc4ac715f5474b919287fa3db252fc4b79129147dc90caef2b0da8385cd331454a0d945d00121c97623a8d42107ce7d2fd4986429c61fff462f84fca849672fd8d4ef319426acf5603d9dd6aa0d60a951d045556607d4d656fe9849a249f4fb08c53bce970ca4aeb0f1063a408b3eec7fbc75a55042a5fea39a1060c769c85b0c26cd09ac3fe86b7e14e1b175f83fac7adb60a4c9219c4f0e6e5f9286c3c01a073f1931fab9b94022aecb9fba3746ba8a32f5f54516a33762772e6cffcc72e3e49e2044a7c08be3b26bf7ce9160b50d42898cbe97f3345af3f79e92842d89b1199cc835dba6bc9c48ea95f8edb753cb5064cce03e9bf2103bdaddd61f812a94f6cdcca68111973446db1e6b4fd9f75307c30bc80e5d430191d110b106d17b59d1e0e9003f073d86dd44dd75bbb32b18aa96457d820543f6c6c9b40a222a60ca8cb95f93167d48adc08815e71c914213a326a60ac98eb835468661cd7823398f4786c64628c77bc4015ebfa32a9ecba77bcee775f3635de529ca5198de0214033441f8d519162283fa910b67638dceba515a30d00b752798f43d9f9458f3d50a321d2992071f1860fc6c7abc48a0574941156d6324c664a75f2b503811b0a46e8a5526edc8e2d118e1b987d88fc1b448b2f5de4fa84c3bb40cdee8a9e163633f3d102f55584c4932adbe67c8900fccdd327169e39c5c47961c2e41d3381b1676a478651b8baa6f003bcf2a02343ebaa318849000866a5b566a9e82dc497cd238a8046aaecb29698b74697ad31e54360f65ac36a682c806ad97ca6b105f4a39de94740cfa48ff0673720d9241c5baa334ac12887806ef5067a036b5176631754c993282bb18db6ef7088bb40a2a761dfa10ed2815241708e3a76a6989d7c10f1bd2d8d26aa706af8ea3522778a3b57a02034c407f17733e88eb9107c53c6ae346f3c768cb77b022eb0d59a4a2ba55c2fb028bed46e2bdb82ddf112b37786b63b2eb96a5bfa2653864cfad50be6599c5041813c557b7c09407d35dc6a3853a2ecfc3223762409807a64535534c1b114f44cf9736869472181c1cad82f6ba458eca9323cfcb09827f34da8080e227dc440073b870b25a3d45ff50672434311733d3d295e5444b64f3dc1780fe21a2fd1ab5f1da9ee835499d107b562fe13cf88ec64e39456e365d643a9fda47e75bd0462a6bb5146f84e1d6d449c8ebfc51e4cb05d7ecfbb9f2add61dd24b9753bb5d2196f534595da1443ac25aa4ca69c1a59a733766856e082b84a0e9989809f767ed0d008608090e7a21d8a82c0fcf62c4c2d2cf92ec2554a3af5c826acd941f5f5a856dfdd2715e540d23d5b205e82a0265a14f1c2f34e23a41fcb9fde0fae981e32f5c1308efb8c0862ea7cfa405dabcc1695c43453520d2bbec0055176b00d3b800b0d9525682b270a3a8eb17d8d81726b3d9592c6aa224d0229343b1096c42fe27fbb9dde544d80b2c5dda682bd2bb1f4e757d101b542207f20eb79ff244b9be1f9a3c6e482dd8f51e357d3e2169ba040e7c187d816c1284117585930ea0141ea8ce47c9be11da82cab60fea16f897a8c064f02893fe5dfd8b85a7ba47cdf0b458e608d515109b547936a5627a11216f070ed52d929c24a55ded2fe67152962931ab1bc43b840ac7b20561248c8123f3891615605c8ec30e4063720ab8b511bdc318a404b83eb5ada1219217adb0344f4aefc352d7ebf6b65f815286b051dda8702ba52d04e45549de289ead646772a3d09d65422b91080a7411c87a6808373c3900d72262d4d93215126a0ba2ee5134f6d91b7197e3ad1f5008414a9dedb67bacd4e81659495ccb1417436bb099a07c7b8bce2493469082353d1d64939183bfc04141a08c4dc719fe6043c8f29e6ad9a6814560c0a9da638b9222d4cfebe55fe5df9b65da15819ef5f4b6ac2593e8c6bd1cd9f98982506aa99470e5ddd2779c5bc9e5c2218797db33b8b148016a172c9af7b35afe47e5888ba8140a74243c2b6a038b24c36b3bfb7de3fe66209aed2240dbccbf5d75b67e5ddd1dc427b9b21c78808c4c9209639960075a7b9cd0a2a31b3baa5ee4c0074df164c598fc0cb71ec975a11ae79446d4508efa90e4104a366b84dac388c8a4220099014e6460f949cb0f678439fc86d744d0a0d0f12402637186bc57675241ac4d62cf4c51c32b6d8c868f0b67eb99111099a041f40b823f5049e86656f5a6236c8d2ef1478854cb147de9a06973bf0d945943c5827c1bb5c2021a5dcd35857a1390875b10b52968bf4dc78bc66c90ae3d275f1244f94dfdb1f86b3c8d6552bb2d8deb0a098110e8b99e0c18d9f8f2242784cb2ed8006f8d06ff39b76e4455c4f674b006de7f1e76c5c514100f01bad8ef985034f8b08c049fbf329c49d19a394f8582ecebb520bb57ef908a472f4394f979c1df104a21f387a90d7a6da3d97f703206d35d04fa61d99be772985b6f5e3bf78db10067d28f6da7221f7c22914d7a4c3e938e070448e7e58f340125d6be8218871e04fd941ea3d451caa8fe18f2b0e35317a7959883cd1dad66534304f4e626ff85528c729cea3331de039b0a914449a434d4e3c9590ab44e9a102c6436be52393208f6afb70028e2b63bd302887b4e7feaf70e1f082329f3f3675ef771c7a94fe3bffee58b0ae68cb5a40deaef15fd3ce47ac55a8af489bf6001398e6bbefa6dd8a10b2e6582c216f2becd4c468b2ebf03807cfc0d1f856c485cd931d26598af29405edc3f94afc9ed66edf9639821e291d3470a231b8ce83c7a0360bc807b3e76dcbdc38d50e9e6ec8deeb9100afe26390712b43bbbbf0cf725d4e9bd905e4037e30fbc0b9e670c34973f3d459e6b4644c8d4ef32559113ed692f26630919d0557aa18a923236b8553978631b88dd52528d60e0dfb186d3ed9e298ebc07cef95f60bccf011e0b8017bc4269145ab2332e747f9631fea0e8bc1a517906d96a3ab826b5c00427cce0b95f4e06742e01dcf8b9db8e45d8a86ec5597e3eb7b0e0b54d5b30d38e91f55fec853bd999ff08179410c254cf9a187b7422fe843e84271465c8ffad2d7a295fc96cd69096a68f5c8dae5c6bc4b4a6b8e726969f736f297c424641c74058f553b23eb2962ff312d4cd14909f379d04ad7d9b007331f0df916d0e0955083166e041670fd14ffaa2c5d81e7ce84347078a9a47613c408dc1caac6dfbc3eef4fcfb149c0af58d5cccec90c0636c9764993c0b0c73dc16fbaa79b670ab19ad283dba1d75afb66333e7d417be20ed33a4748301f1cf8b83e09235a1426c4243c458682e3303ecf6939ea2e36048308512f107d27f2a0460921c953f8bc21f8e3a84cd36a2e03aaa7cec1bb14350325958e2a301ab7a293ce4b23714b716ac16ba603389a79573ab2d62554353dd5989e154d33b5197f2bfcd032d73733651eabf78dcc50d79bdaf5e6898596204b1e92d72b99faa6c53053c418d882a64c5f432e39cebdaf644a614e9d8c904c6c88f942220a8de523cc12509853b0345503a7d331a60c026bc96a7ab7567cc2934661166d6aa1c2bc892a28ba56a70500556818b87506a1b64a103b73ffab28d89ac46308468612dd421762b38954586056a25e291ed9c596c942bb81a1ecda61e56570c8305696817c545d135314c691be30ff89af07cb8f2531870b20bb142441538aee11d83f4f0321c00d2964ab9eabc65bfbf93a40435b80175eaa6c2e93a65bd07a726b3a6862eb8a82538c50d71ba24a1a3db4902b11db51978ef8599a369e970ce9ec89c946dbde88766fc414ee8724330c1b88ffc82eeb1a5ae2bc2ab5269d6332ead57d647c49ee17777a7a24078d449e23ba4012ba9d049958d71e5ce14eefca544eb2525d1c5e81b53851783d898fb968e7b78b08f7e41dd7ecd74c547e7e80ee9e9c2d0b6b971cea07dac18814437e4c11f93b6f8a5d05224a19f8087c49bcd444d768f9e87bc27678dd9f243ad1f5cf83ff864c4808762a06f98e6e3eacc3d53baa08ee30c88069347ec7242f68bd41a696fc418062bca407bd61fafe4819626f750b15a1d89155b4cc77e3460f82e55e366ea656dc275a3469285ef3074a611c7b170aa34a5a64b6af7608f79a37fcc4f7a058fff8bfb43d49c0cae476840cbf994cfe9715763a431a8606f000d4f792c0e05e763838ae976ff94420de2a10ad390712610114f3cf25e5c4fc88e3eef27c7716e5b145af1de384916b20129c0cf9640f35e082ee89f56921669debd00fb960e027f9cab1616521798ac13ede777476c4a4e1073d5f02195efbab20f7e8aaafd6dc22b91ad232a1097606eac9d97bf0327be0969bf51e52585a64bc2fd815dda6b1cadffd2fed086353a3a9a59b0f4cbc9e0c53f9e4c10bc0b1394b3f1a8448dc1558931d84fa38994bddebc6a034490a9179cb8c75bf2033de45330457bf64a8f1c209161502f3050d7ef411ed83351b37e791dc08a56223a00dbce2a814b63ee75fb196d2a8848df8115e626c4916def2e37be0f1fe3bb599bbed904102e1027236271402e53615f4e99f0cdb7c2a8de989fd932d1f7d64dcb457d3c9e75073b807d9889a5ee7c8f2e89f03e52f5a4638f69c3aa6a18b5cb442fe52bebcccc3c96d879a4f783fff7695d321cb4f43a3276b79e084fa880cfd81ff818a03c7ce8256fbd5ca8df4c80e401a4afeb0a66173b9a49925107b83c0925312cd99d1c5360116d5ad48556a0aab39abbb6d90eede4f4e3004e88cd0801ab2cba68164e01e667e7d024a2045e8f33c3b73e79d4612d667f5cf00f6e5cef2b41e68283ae3dc8aedbbab56def447b0c02d8c7da2f0dc3246ded85327f4f57746ecfacb9978d088b15a47c836479c86c9813fd8a6bd74bf9997d5efcda5c32b3a87a2664b9c6d6fa3300e66f2ae472f54440c1b265345ea806c91074f6da72e52016cce6539f4ee8ada988a1152a8c9806ce95dad119aa2331d185a0d16f7ba94998593c82391333d94238568a1be54d5d5babef9fecae8688acc16e83758c0403dd10641af6427fc09a42e38829e1ee9e264da2e27ea949c7dba13f77c93c070914e53a11ddd759935ae402cc6c62ef3241fe127d35d0ef12a7bcb37242c4a1a31937de8d7c8f895527a2d61e083fca2b9e5ca506960599a6a9a133e250ca884e6debcb4bac0f32c179a3ef27a3bd95da1100eada73b42a631991b1586d560b69dd05dc62ea26abcf3cb367ac01660bfdaeee5b14c490bfd21b87553056b44307de136ce1f445999125107f6ce8aa87e581f9a78009ad697e996d0d0c1f864af9a37d73041aec926729e0c02227448f4593d9b663843f18208d1729dc44096ba2c8af4081b16400988cb52aadf6e683d191bd301c0ba0bb73fa2faf9349bd29f0349eb93203c787acc56328894bc4efe44ea1a3a0ccbb0508d1f83674cad61a49af8e3fabecb12d416b65f0c9075ee93bca82db388dcd3379bb6929b1ae9c0d4348ba49c52c653c955f4f116e2079f12156df0f6939221f008b619913141f45b56b579eedf516f34afa5301b0de9a825c8c1be6b44bf32c580f302d74f908b459431d70806babacba233b45ac90324bae34343de4e8a8a6cd0c2e5bc6ec13c8a8c19a32b171f088e25b20479119010d3c2db7d122418aefc76e8cb0f3af012cc0cff2d841c5385b6321a2b0047e93eb37dff071163e0a248e23c511ce1b6618822571329bb54e48a6c9ca6db66e0c21cea710a24474e26c78b90da2ec2ab9cf6d8b3e171c1b764f007dede138ddfe41f0a1ab94f088de610a38110ce86ecfe1408f791f8eb9cc25081a5ff494e418ad81393dea97123e9d01e1c62922d2e44a297bd9d5ecbe575709cbfad622354b9dfe653ac6037622f6c35f123f0a40f3b9eb03082ec9e3a317fdffab46504ff46dc81e13e242ae2f194c673c2af7b44f7e4a7787973740530468d536833af265bf9873da65e5b9770b8be86d7e4e13fcb433d783e3cbbc8ba28ba50ef7e5639061f1eb76bf055d0951a157f970de123a17b72d389b33b4d4608c67fc0b217b32e526a7bccf1fd11d9605859a9dff431c85d3ffac3afa8c786a0fa0fd89e64cbb6c448d61988b82989392d63e60462d74e74b5d5d63f122bf877a7d0ef6a37e4e79e8058fc87cd423a2bff5ae33434026da653fb10c527818ad3ce7c2b8a855c78dd5b71105fbe8fd647b5b93a602ad0d4ac0d8fecd04fd8a04eb75aac75cc3e4ffe7603f9060a85386b8cf2a880976ebef84f7e18bc09449c7717e587f55101d78f5e4195ed1308579ceea42db60cdeb8156fd61589ce589e69d361f34f551b54d2c4710fa588ff76b71c0c70b963a9880790b3a1b97076208312373682991b5b1f956509d5ee8a74093b31329bbc2e854888bebc8a42b5b3f5def1251b7f8102db6377c350427aa1b83151592b2bc45cea8bc3a434f1996317ca6dee86c11c0282f1eb1156d9e972b5856487d00532d777869d099001c5ef8a171ff2ac447599d18145578ea0103e20a187169d244048df967ee13c3b94dc5e6ef7175262fb00ac33c68afcfb45d388eddb4fe167c85351f3dda85ec7ffe8a41ff707cbe50fda1f544d2ce0164b08549fdf1f21bbde199579086cfb4295498664e5ec9ace2d33b46ca977d597d0f168e2c25661a58bb889c9d7cd7a2485f87014f2f45c00ea560225d2f6234ce58782845145c10741eea1adb25da0574ad1a602aec7be5813b50a14bd35b832fe47f621820bb8cf3bc85200e1c36a39ec6998a2a4b2b00f5ec0131d6be454a52550ac2546852d3f624b23dcebfab92148190c6f825ac45aa2ce7b8149a59a7e6ef4733355a5c930bd41f26437380f3d27e377885d747b460902604e2b13380231bc8bb1990a3459c98fcec34943cd49ac44fb61e61bc2465259d5406c76a4beca62c12a855f33b04573c4f61827a82c911360cb91b55028aa11027ea2451bf153f35adc2cdec017587291a6feeeacc5d1215f3f691f4e959766431a5de891924a1641ad8fadb6d41dde1178bc07997004052a5d8234d4f910ceac7afc38249eca4676400553f9932b737666363e1eb289d5790d563432ab76298b800edfb6816c1fbaeaf700027aae0f81ca665852d1708a614f4495428ad7f3a54bf98ae45fa30ade673de5fd845817a95fab4878f2f962886b22094a5a6911cc1397cd8134d548c2897ff12bd624317376bad8fd7eb5585d45af02276b0050bb236a103eb96815a5a9acc08dce80150d93e17b1be72c65dc6a026428162390d712c4ec25d86a43cf228dcc808b21c2744b7a8653e143c86cad7008f84cbe2c938a59f4a22b3c10efaa1813d99e54598629a8cc65fc36cce693ab4e1cf232ec6926e4e5a57edc71c46c1cb83aa0da3c51c485f2bdb36e18eb51954eaae3b98cd336d03c02fabf080eae6530a6c8fdf4b48d3eced987a2992edd06edc5588dfc9276de7c37854b9228b5230ddc43b289d5d63f1a69712edf8747b417e0270471cb9f0a5a1952ed637f857b1ad39ec525a5ec25a620845929c7cc370413b5945238c3665e58358812b3d0f26d436da2083d3b8d55b687c131220ce43bd3434b089f78161cd2d684921f5e4f78429517e2a5033442d158c7711ad75513f3463588ee382dd4cb1f700194eb9315d356161753960a22547d22ed78b213dae4e6267c256b53082464ac721bc9c8035b03c12531c810c9dcb825eb0aef0136d16abea49c31329612aba5f4bf0298453386fcbc77b7778f50792cc1a91a886b200c9bafe829652015e40267f6e65ce6ea87230c9aaa6e6c275d6335c04c473647b67871139a16db997b9e79a8032061b2bcfc6323d0fd18696af83a65e7f2d3819111cef071764e60ad946c5d8c7c6d6ffd605e793db8005e7f53767b37a22301fce9c41a5f47858e4eb888174f1a8540a64a05560f5d057d9c87174e607f623bee451c334ba88826f50fa9448d626b7208e1411df5ef0b48d2c6c2900d2f63209b847681a173a51e6920c5abfab9957a20d1ce7ed61ef0f7abc3d5c730d22443e32ac30a4c3a6a87b6c5d26f74feee8048f9d77bde7bf77bde7d07fc0ee24bac5d366351da57d9fedf25eafd1e111c13431ec677293c66b5549b9834876adf441d484febc85eca5d04bc91f20dcc9891880c5a4e14ffd2225251ac02fedfeaa45efd1248a441885da2c4ac69eb73bc104b44895ba017e68c41551e05cc9aae30d71417b32d0189286396f4ba3962a44a89125952ade7c78d22026babd481ca774a43321ece3829b77568e193a00ad2c507f29a952f23d035718e526441bf9a0375245d3e54828cc5cd2bdb8777f4a3516f11f20dd1c8168842f7ebdf87b628c2b17102503f21724f6e7f5e99623a29985d1c6f16063e1f72c215b2c5e1dfe90ed5709fe72179d3ac836809e773b81d96a025300e063796443aa1988c1574187d83add737261d4976bdb73ba470b46b224524074aa7b220f6fef160e1c999881f4a2938ea86d8d0ff7e3e5087b70ee68a34298e6029059e9c0d28d2962da4172210a94c58f88ebd1ad9dde4ab3daf58356437dd8b7924b60310f314bef811c31a6616d32217531e9b7c8aeace26a3461469cbffe44c47a950ca1e604f92593b87c83882da5bcf389e92f21481f572d4242d019e41df2d161f93d4661a2686bb61d9cbef7c26bb2a3b57952bfea704b2ca0bd45f7ef15fc6786c4b23863e9f5b1dda5d9462818d422a9e33dd885c0006a7bdd1e37202c0949bda9c973daadbc1b8ca69dc09cf523b1f761c797c72a098525bb620d7f4fc4a82d5c12e8c9e5f56cbd2513bc37071fdf11d0da38c7222236a3fa2d5b0b5719e5545b1a30e348e985ea7d7a41fafe69704f1654d8da4872e3e55eba09359bb1bed43af174d5339ce8a5c389dfe6a7b92d27820ac616d808957b4be8da06f3a8101ae41a5bec5290c251d0f60307d030e35474b16b6976951b8717989a1ef712d9dba81770bf53db236236838b5747586a9741adde54c46c99ff4c6434fefc7ac5ec8b07c9b25496806f5bc4c8ad3e46c34aa4ccaf476b032d0c0cced98c87c66bb8eab11e47cc609faa2d02558cfb7e7b18b016e865e11afd3fd1a9b0d69ddfe7963b35b412add3c2e38d8fa1c3598a581ce2ecbe26754513a7eae9cc43dcfaaa109d6c3f588114e623d91083f9265e9c1a9516a65deb6910d6b1c8bb0bb2951ce3ee685f07e892228674cc3fd7acaf504079f98f656d4bd8846eca7b9ed8d33828889c0d1313886089f7865ce0aa069a736b7b4dac7904a83902245259169e8eb8e3a75f42e126c732fc1b19f1c0f07d16e160858b0701421339d73902b12ad44a9d9335ce348c84a5b3faa1fb1eac478100c102394a8f6f3cc54267f1b63d98de44dfcb221264bcccdbeecba3b4ea74c4269d0fdaa14f95a21443ea9c2e7800ed9ada093f4297847dd6fdaa8f3bf4e33d00324ba5768ff107ee03614aa1fa9b16c4015f337dc4e6d600edc75a39c1b5db6439bcd031bf2768a6ff2c0f1d14ef2702ec022ec12f11f9e75628a28be9d46fbe43cd0f0b82fd347e12ab6fbc50f6608069bcd3276ee1452fabe0eb4b4b4467da5c0e266526f4747327edd125f946ecc286da4602a84386e23dc921ed00303e6ffff7144c0505b02222f9507dd9598dc411e8f93dacf84c70b03b561be0ac403df84a8abfdd662cbaa210b5059125f6f2148c2e8b48159e0fff1f15fa504747f1c5a8d303208ac6590180da845d1816521ad7d8a2a7143e08b5993703741157b0e9f3a46bdf267b3b80cec8a950d064ba0867f1248ce7dac2ab75735707b90b507529b2d802f60facd837964f3e8a5de57de616e89af573b6605a73dd614be517e4f616c014a2540cdd216c2a2dea22dea7d34e9a6eeefaf52406268acb4813829bbb9dbda2a16b5d3a5c136b6debe7e9932503fa7c66be20187662cfdb692db5d3e789869dcb9f9cf34b76fe3f9b2a8a1ad95bf7ea494eb741a7b932d61d1c4c8bd7956df516f41c0572256cc824c30e29e63160e431f980abd524cc5e885a8ad3b1ed0f411ee60150d04a7ce256a9b17fd1ea7adc43e77d560e7b96767131d48fa74ad719a67c386765384b67bb8bdfcd876abaeaac10d76ff367a0aa0d3c493ac247fdb0b7d0d228537b191c0fce9b333c31184bcc084f9ff68bd3c6a68fe54fcd85c5c220b15afb694907ee6a258e236a4c95b4459e65ca3b64826c866eb7291915d9b7d9a4a617850ec730130fe261f5758b7c837ba2c0e1801d0cd7ae940c89b61a124e7ea5b090c63b308da18edf75612d98d8b1846735997c3c75efe408405e6dda4604084f70d272190dce490c886b00f93ec2361e89c536bf6d2f4ba9f29dbbf59b328cfab7a03b5728ac4007b571aa5d087b9d6be807196776089b5e79c901718fd7623b1893da64d487f83d85085d84906b94837ada33ddee3f6f6b3eeeb62de048f49111d870ccf70f2e87f278f2a89b68037da5b2b519f41740badb208ae35c7c4e98b0080c613d7b85a1a1437bb4694ddcaab09cae0e9c51811f233d2867d8527e804171e58904c1b452b355e19258ff6a92e27143fbe218d60680e9bf212db3412b42835e009b05eccd11e1d22ff496bdbb8734adeb84f17c04765447426708a3243cc7a45e30c64de82c460b64645e12327f8948e6b3144146badba15c6fbdd0de71583e69040ad694a190c4d92133027499c4b13583a18df7002db99abe43c8209881b0c78dff0ee4e922228aa254566c0e8e0267011bde8b4d296b0726d4b3e4cccf584738a32e57773adc63dd164c48948f1c6dd945463e9f9e41e21d636c54d902c37bccd177e9af7bd7376c9d950766167deb23415b7ba0d020534cfb16aab6451bfb75de00622702e64fc7ab4b5e4533e894850317da135b63f9c2474bbe4c810da9041cf32519461a13a23011c808cd439eecf8805b7973948234538620734171c8628407436cea14b8e803b7fb10c26e197591447979c25169042cb22712c1915411ab9a39d8502348861e4570760d188959096d89a11916bbae41460b8ec43314e6ae786bb781fc409d248fb761b5667e273aa14a72d896065e9c3869193ec3d656900650deca408e53c8754d00661762e8807f21123e81438f56aee1b02796a0d4cce753503080e53947b793b9985426c6e4380c49957fbac13b122e2a255cf52c0db8971b804cdfcfe7a436c7740ce51e6880f6679b030392498d038ecc2d86e997fe3edd6b5a3e3d80e5e320ec3df508e687ba5c74d2a42174cb9cd4282f86865bd30535ed7c1d9b723a1e4af251dedeb92e39a22df8a85a07e3b2ff4f305c7fe92e0da7c8e62e7bda0ed0262a15868c1c0e0fdabe31db6379818515a62ede8c2880a8d8d519558a49bb28b71836975557c8cef10b4efac4ed0d8e6b4a3dc433a6c73df4068a6073e57e624664c4993b708531a8a42820a7f77a5cc65783f9009121e2c2f7e319a240774920f0cb4f8b9d05ef3b104c3799808763615cf5932c17010e78bf6f6d938fccdbf2409f2a38010b0a7b4d74c495764c34e8a403b37df9e0dd5f198f92ba82cd496e351c1a23f66874a06ccfa0181e53351e446d76978579c70b9e5b5332e6ccddb07495b30b5e3ba3de2f055b714e5e1a931d2d00b7579946bddbaccd02b4b7475cd3df1cfc8ae955ec9ca0335d109371ecfb9fb72d01b708f3c4c205ab6d94480bf619c6d709c88b440a60ee8753878a6604ce2013fcf405d74b67633d454d9a380eb611f47b56bd7ef898b0b28404b82a5e11d2e2c056734e03a9ae39d27db0de2bb9bed6b3386a1ee526cdd3053aa31a6946591ef64f38014feb3a55f4144ec84e602472707d03c04685869e0ea380633015e873c120cf49743d278eaa21032e9f0ca1120cdd732143c491f01fcde60e196af3e1e06a08eed160cf0b51cf3b90d8efc46e423546bd900edc4411b6ee99f1bb2bb4e74fbb249196eb962df7079c6d48f94e085f179382557ad53afaee7797d14408b44b5f1aa173b39b695722bfaa1ac0f819b460e60f49fd54088e5b583a270d34a9ff078058569c532ada47ed84a522cada4b9609dca1df50a0c65450517919027ead70b0b0d934d91cef0f0429a3643fc40d0182aa74e395da5c6857af3e62aa722f037c6bf91b46b18b740f6417a5896484787d52e3cc454d5d72936c01948360620e9569816b264bee0930b57effc1170e788d1900b3426880680f4a7db7ad742a84d4517cafddf0479d439d820b38118d999cec1a2302bb5749191a5c6afd839b819a385650652da33cbf1323f411a2299cc42eafac05883912fccc29331232d8ef6770bc29e986238b6be73da6098bb51c671e0bd5c056030c01cb2fbfe0b2f550593ee6e507811e3c6a166e3cc2d536967d365e4df6d549d5e6470a3307bb13a0a856517d78adbff92da3a49622b94ae569ead59f77e62aba269e34d7832633c66421a1abb6d50b3564a23fb3fb4e2835ec3054ddd85d4019417ab35d8b04089182bf8402235434bc49ecb0b472b5dbb20e6a5d129c33c4bc586084314bd40e9b318b48ee3fb968a987a14856398615a9fce1682942f3de55f3f6c068e7a894a12e6c287f881442639f962f1fc9d00f19727dd5428cc4a9d8f37b0a6a542ca5ff75866e6add0468971cb453dd1060fb9f6a0120af68d5951c2e390979601b95c8940d4ee968b6f544e2820abb181d406bfa1e7cefdc1fe8a7565b24420e84d78de2b7366d0136c9f8fa544fa6d1ddf965283b8b137357306112e698ef5adbb26104e6d3e02483e7c680fa7a3f2ec015f4560fd38141e5c0c182bfbd6dcda1f0ed3f9db59d3bcadbef02b16a5a623a7d0efa7560b3a9a2a42530d5f8ee199f6a2d4727f742d8e49a81f0a6f49e28e159ca7ddf4298315e60293a4589071d3b823e6b294c3a1e653798e164c5957a10c9d9d0806a7857cafa357a4ea5cf43bf2586bacb82c63915893d3b721737371eb205342272c40279a71f94b86c21423c1bab6b3ad2c356e5ab48cb566b57c663b32ff38a3712e842ad615923fec59cacbe4c10a294334b36c2172c84bd6b3f239bee1278c9e9ba1cb49295098ee984434e6c18a7b48456bd091ea9737541be401ed39c9475fc0fe140df03c58c134c9bf2628318978b0d2d8b8c7877ff42751c714797cedfa7d99b629a280cd6eee15f6aa962151af646a4b714016152894c8d40f26f158f4f0c498a1601d5ec5bb2383ef95bb014665135b8bcd7ed782c8c9a405155c5ea1b660549ac97cd085d3358f3592d225cb9571f8d33d170856d547b39d85012fc9558098cf7dfc7eed09b01a17d9f849c7e3a735953120b8d765559175d22287b6c2b1ca362c81bc4e328045b6d94ec420590191f85cd68fcaa776ea2274329613d7ef9cb2d53b3bd272eae80507d8eb273798f5c4857f21ee125b47ae0ad9b108976379dc9495fb5b697462cefae6d8fa54e1ac1f5334b1df23c2b98d4dae515346dc94c27952e646dd95946ce599101288066311474244d1d4bcee85cf45adc3d78497507b0a2d3ddd92d176ed48f9e8d520068793f8ac76bddd44f71922d368490c50a07bdbd2f7ad102dee49d016d3a0a232fa1c705429c55c642a54adab7405c080b609bf7a83c80aabccccde1e7712170ac1e11f264414fd53dfd20bd2b108cf9b019f0a6a6d1fc453da021cb6b6f7808a4b8a8a10cdfc3e04b62c84c103823dae65e2d1d4c687eeef107f049cfaa5104a0c441ef779ff60dc6b18b00fb3d39dc2f705ab1405bd3e7023b5f9eb5d0a9c0f9e1891bc4b9b29f1465c037ceba949c57dc6c9c386727d7c2983e8dda57c5e4528f1860ad0b87cbaa17f43d0c8f5a567f688e220eb50c15b51232873664024ce85ba4ff3d1037973a03be4c4e79a2453922d97637bb6e95667b214452a1948d5beb38d18a5ef09c0bbe99405974028c955baa91217767d7aa858e12d091d59bf0c24cf9506381a49ec90eeb7b5494e8cdaf0f368b1271144447bee5243b9af58f82dd8ed369e55dbb879c187170dfdb8866f346e51560f025ef02060dfe22562948b55f7a5906e6fd69eab56c7451de580701b5f458348fcf85c0dc993daa092415b965e2497a1793dc3275a954d89dc8edc8ae4cead8d5f88a88cc33bee3f6ee7f24b07c71fdabe8606af678cb5bdb9b06477cbfdf7cd2a82ef02b26bc80de25a213cc28d66cf298d5e66fb5d0969c20419742956f040d02f634fbf2bb4c7e2c34c91e3a0b7bfe4c27948d0c4249a943c2ec9010bb25b923c737d49d1de553be7e36f4bd7b6a55db3121ccbee143e5c76cd2e7274421be68dacdeec88e8f609cbfb18c6c0e40622f299403e03400c5d683ef5c7525324b4b51f08c37b7eea1957f5de3dd1ad1f2acb21be82b6e613d2d347dd184cbc710d6df5e540cc8edfd75daa59a5488fc449928f7c1db4bf1dd3d94a2a67846c06bec6242287226134a81391926a100eaeaf23450f244849215f243a269b504d39c9f0a83e1c43730357b2a1e41b69e59f21590606f74382e62c3d79cabfef678caea223b4085b7c1b29e22c08d962f68b1ec062354c39737208bd5b7fc089e2b38f050a6d6420710214871f794f7ef709629da7f26689c09daf128bd1330d69f4fe4f637d90a4ada6366dd13b97cf7693a02556e7637015f1596e90b551c07ba7677526dbfb66ccb8d30caa1d37f072dd9968d20010ee167cd352a46e0500e9a5849676116e7f2c3c0cfba71e97007645f71857b1058bd70ccbcf3c85a5173f0f90d6f9bcd4582007f1fbebf431ca8c675bb4cb91384eea6bca1b9c9e34301ba0ee374b915304d8ebc77149ab620468bb9d4ba8516a1a59b947f5d228902cb0424a40da0c9f0f6e473f241ee6e0d7320aa7cdd578a04490d06937ed22a84bde4d9d88940fc60c940a8463ed39f63684db5787f469ea9942c366065e47ddcb858cadb433d4875f29aa03fc2702da9ad9585e60caa4a40e8a6203a45dc72979d6367bcfcffb068def823779d7a1d5a8fa7645c3341c46e89f5d6629bca097e02f12b005bf7dec2dcc47611c7fe3300f6bbeeadbc231ec12dc071f73d30a2828e2a3ecf90db1cbd8dade52ca94e4de7b07c205530577054e943e4c63ac797a3c4a3c3e9e20ae00eade6bc1ad56abc1287dd319038302e62f78abeaf619de030cb144f0ddb83722b5b75aed56a20743a2aa8407b11da62fa5253526867a0cadac7c44f168c264adaf879bf052dbaaba497491f8a2d137aa6f5b96476caab75aede613648545316c0290c8ca97da56d59df39df896705aefbdf73c9fb4a0424feec93d3800be17585f35e5de04c4941b2601b51b0378f44a9818bb6a00b371e4182c3f9a7a080fcb192828c5f75e34938fb333c16e2f5691cbb7020e29ebdd2ff7de2b73da42fa9befbd5831ab6aad02197f63ff1fe849d3b5bd6b2bb2ab7745eedece8c63bd97d6b3b7f302fdbe32e79c8182a78d5c7da327ae40a350db796bb057d9ce321e1632a8988a6364b123a388920f327783a12033866430f59a8c32b744efb699d75bef7ea995a88ae26923673236752b7cd6bb5f76d37aef257105c5ea46944fe64a69d215c24bc9459250e6568881316be5aef1826e310a303c3930d3850956355ae29c52e19f1e3a63e5f2707937c16b1585efb4912bd65f4ac981b9c5240ede537f2945839d6975f7facb262d7ebabbdb9c367c3b7700c0d4ea89e109e289a25567acf7eefd6fa2cbc497d626be5bcf9cbe86d6bb6b56a68d94497165dac855037c818c099bd346ae56aa57a2bea6afd5572deb8d58bd6a89071d5353c62f104b2b641c048928428e3fa282622b29b4d1d900b9304d0b732242cf60fac8129795fc1df9c1559644b660c01911a23b1122fb5b347ee72ac7ddf192300c52e4068e1c494511248fac184cc20e4b60e4769278794b222cfcad5b6b7f44460c953f53e72b3062094e0724761cab3ef014ec2951e38bd293c064833088b188089783b47584c9496744f04f11c21c3536c70647208a07c60f26462100e576488ae02082e4a4c75a6badb5d85a6badd6339d1240ee631d5913c7b1945608b03523888aa5163b608440cb458f264c528884a7bfdf3205182b288af507c23142231ce7799e3a5a1ef8df10ab3581ea54d91f189a683ef0b638d490b13034f4f48458e1240a8867e6558cd55a6bd5752661257f58362f8c3b9dcbeac57e44425061d223c9280a94260e5732de634224f5fb30a57384cca281ea22bb98b185bb7b56149ab391a13ba97f583d6eec9795234daf6c257f5852b124fb2f7906c9e493aa93750c9db3b203b45a6b6d01763aa03a44429d291f0288a519564aa4764d38200308e24372a2d483e3010474a28a2851f53889323f64709301c54f930c3b4b3a289d6a560c7b4d423a1900814207488f08b003c9046a968d3c60e48d118496272b15c8e102d762c6083d4460c900421792118f0f74587bd8e05275437477187c34e574f0c5b851410f9f974e948f9f0f8e2c0975807211ae58daa3d3df1121add39f112a278415b6923fa38c34aac35a063948d19201e4c747d68484838987202ec7c6024c0e6bc961e70738c5338542a6247144b4f9ed009d8e64caad3ed534d8a65996e5564b9cdb6ba3866463001e906469a7a8899026562692ec78a1f3254207e46a0aa678e48cedfbffbf308968b5f6626bdd6d562bf4caadd6270104411ebba61ed2548c9e22276a6c15f1b2f12163addbbb001248018975ac9de43065c087a9a3f5dcb8c4e195044e95809200d3ac9192b2525502c000d78a9a165b1a381891f117345e4a48423c719d5d96143a05b9f55093dafcc8ac942f5b2c7564f7b9b15dc927a95ec6a159c935611a636c690af0f0e44f0bcc9a05410fc67fd96aaa9e17a41568554a46d89a4aa3d16a410f393ccc5835a065d35594e50d0e90d80fab9fbea1470b51c0e09542438d0e863c153ca91141538477745385e4e79989c699852d3e3855762186359657f249ea88a5bd7c0d4e4a4a48f04089a2528ed10f125c325932322c0141da8043e6dd2681b0407cd67325ab3c30ae1a5893080d05b070f4bca64e7465b800122c2ce30e4a040f05e0aa86c43822c161c3e4802694c4840a8608277c312888394f6c92d79a6686167987114c0716256418d921e545140b28112ad21585c593103c21b1849b4046ec05017b67f45e20265677a621d3461e3101a9acee05a6ab0c259cb28e757eecee04c446423903024d1b49657575018875e6137f2cc77e78ad907a1b57124aa9cb784e30044353d34c2141a6d5f6cee952d4f1631fc2c141a78d446333d9fdfdddffde7befbdf7e6fb3f9441df21622695ccde6e4c621f2ecab47a81a878f1ffbdf70eb5a5e9dadeb5dd2f00c9659a9d3da23f6161d162d2746def9a0b9c8dd1ad94b3fde278da48a85e117d8fc94236f7de0b2f86a7f9a49573efbdf7bd56c10d8c71cb14ad5b045994a8ac28aefc98c958e4b450c7807c2a3022800171c978a9c51756e4a04ad400c0a835010fc74382cf8671386117a5724145f0a8456937bbc0822b077621c806ca8746ab5aab17520c7bb6265c10c2e912aae76ae502af67aa1456d2a9d8441dd2373f5fc78fc3f0ee8255906923d7172e2171c228ecd8be3128ab1e6ae34415ecd662fb6315f649b33058859574fa40446afe08bee32d26cdf9fa756a61617c15d114dd14e514f914f18a866272ce99e7d9dde70462824ca465da48a22e4cf4a55fd0a82f6e76aeb130c1218bc8d1520445901421b038b0d891f10e4cba74281141878dcfe79f5356b08b2b17582eb4541c8cefcdfe182b5973ce39e75c4b62f224a02751799295275d79125692d62da9cbb4912bbe522750e9e89effe2f31ca2e240540f64f540578fef3dff6615aca20366c9e34a02876e56df391f9788dc6ffefc39fffb5a4e9567957957998795795a99d795795f375f91cc54322a71dd2cedd6f6e77bafbe6bf6f94cac39e79c8fb44c1bd9e5168ebe686e722cc4f0b9c59458e2398a801c3e8c5545787a0491f1c0e767042122a207e6d0695765893966beb10088b4fe184cbd689a91fcffff82d2095d27ca430b6bc625a18a4943cb93cb1566719af1f815a0aee5accada1764d151af32dbaad6aa0ddc4a6ce6ffe68df32dbfac514346abed75d78caf6333efeeee3727e1d876c2a9748891615245198c7418137a54ab503b580ddecdc2a0d4070773e8e95e43e7de275c06d6e080e5b51a7514c5e4e3b5fa7ed9ee6e02c101e2e801cd101e193716510089800374f5f7efc3d4567b6f08eec47585354456ab368e7f96e512f404fa4e1b697f450c67f96516d6aa30f83031b7db3ea7409837a090cbfabbfb35cd2158ca265b90cae9f712d844d4166c284d8580019748b8fffdfbeeee4e811138834c97d3a65d4c8486167409e625cb3288691218a67144a18e542e8d5e2ea6df34c452137c86ee32c5ac8ca06fc1721e7d448ba9d59ec5c9e06281c9a64b6661461085d6eaf4e388650376d2479a98829574bac219a14b61e5455b5a7c357869050d336bb8d4ade4138d56a95b3185d9e58161bd1b68d67bef7d79c15a5557d2f7ab9aa5a60d0683fe9e71bea1d66a37a8118aebb5d93afed98abbbba6f9326d24cd4d8e0f6f28030d471926df9b92486a7937034c7ca33430c5deb31308295a480bd20a68851c9581b8de6df6ac3708a2834b03867438f2d068501abe4c618958f1fbcaa0249675f38bd134c6faff3062ea28855db680c221c2a240e0a838262f760f3eff41e75fa1b8feffffbfd6b2edb20afb9f8cbaf5ff7fe3f727f3fe8d3dd0fcffdb9c36d2ffcf9bb51f99e0ffbf9641073babcf47f370fdf7fdaede184f94c207e3fa511fa4b8d46835f3bf8ceaa0a4d27eef0fc45d7300cf70d35490688cd1ebb9192b2a88450e00c860836e89041970c86dd3f7f3fa0e411974e85920b332831aaed346ae5897c1960c6c7ca78dc46b0a1b272739988fe0deaac71ecd698ea2a62f666dabaaba597a7e2368d60b8bcddcfc308dd70c64cd7bcde89a73ce398be899365209fb74501d6e62ad68daae08634c1a9bb99f55ec2f5cc92f5b55778fb187abe7e88343f7d2cab5c9f8e2c701284dd7f6aee976a68dd405615d4feb94d47467657c7b1c9990b713091a34576531fe1ec20ab0cf0870f52c7482034c078df486d595cb408454d342c649232682512a8c49ba2b19bff7de33662def94f4ef5b7f77ff6fb1dddd3d68dac82126eaa26adc0c40c255c3b77cb1b53d6e28781e2cd346f268619e2ecdf3a5de8df3c5ee4e22709aaeed5dfb31fe70fd1c77b89d4d0d85cd8a130a1e167c71beff3c4058b134c7d7807b0d36af21f71a74af617773f4e28bafd962757c6245ccaffb678c854d4ef7ff73864c1b79c404a4b2ba5a73b0b28eacffbf7f9f051002f8c6ee14503035c19c69956b259d74984e2e8c595898544aeb9e5b49df166ff532570197cc87fb1964f44bee30b53b25a5618c491f0f256bae58fd9e8072f18d6b7b9b2a76392478034a60ac63b4d62a5655ad6aade297004d3dc804dd69232388cd389af1d150c226b12fa7660f62179ff1b4911804863feaa933123b1df30f2741ff97e51109092d5aadd29d36d205a7471b58a88ea898663bcd6e7494ad68158795e3b8721c582db1198f9a9d39446823222eda6a4c62281f54106a0845842a4219a1b858defd8511a84cd14cddace07bddcca611d24c8649dce4336d6453106b0d046484c71881d804480ba02941be36d0d0e4c18f07c2430824f71afd4c88cdc899e1a372f13fce6ed4c10586e4e3483c471a92a926cf4888adf2c0eb399064e2890811d68d0c0d9cc8ea021a414706d0e74fe8a0e9639792aa5aab347ca78d8ce1143ba3afba92a1421365c5ce91192e4210da7102466faa460f143f374b556363b17f99cb22ae18fa3bbefbe3eb63429ee5a52bd34662612ddda52e7d6d9ada2ae3e616f3bb484a63123f0d9f884f4514c59415205a021a5068b0521212fc4171f143211443c808e23c8b9258ae5699ba98be68d0e8acf25afe86ceb4913776186b1ae86601bbd7cf7c16e5680f5ee52056504b5f3549cbed6fe786ad7b63d6d2cd6c16dd605b63b8d620fad34061626eb7db866261de8dafffbf698e70de6be308c49d58af08bc8187e0570b5d861a2b111b1842d13dd5a8c8b0227ffe7c9e5b2ca6d598c44226d346025721950e22d651fc23b6d4ea1ae3d4422f3e85dc61cdddf8c6dc881fa43273b7def7611d3b9ee47c54159778c850a087c4071bc62127bb7f2718c5bcfe947e861fee50672681173d1f0ed2d30c29af9ae10b224529ca92985deb000fd61863ad25b0a1aa403611a84c709153a1762890c0a238e1e94b02179e84272f6f814174794a026359e19b55f1d05961816b7922b6e8144596272a980a906fe5e987ca846cf4d39084985051e5a96705d6a2277bea4096f28ab1ede65386f4d1ffff17c51746d811cd43bd34490caf30a24262d7c5300f65af4287a7dc8fd748b56b50d0099655c59007e33ea46bcf07e42035ae445c9a4256ad56aba9b78fc1d09cca9800d31863cdf365da481a7ca3d50b24d0cf402ef6056f55dd3bbd1da51ddf4e7067e8d8020c25a203f584a91a63ada3d1dde872743e3a9ea5e18430335ff056d5ddc3eb19ea39ea61ea01d6d4f3029a7962b9bc5acba1f21c2bcfb9a2912bad4693a5c8a9601c180e28f76eecf438364f52680010420745a6d59e35a22fe48a4f5f44d98510c62158c5f4665bfa30ec062d7371256d34b1597d9fd6b74483a238dbe8fa1206e9c0dd7da66aadddda7802bcbe316cd36e898de2ce32635338a208452b6858891da02b633378a603c672c05ed417accf73a6ce6cce15215ba5ae0f66c53080529c9003c230f03262f0c438c207136362f6aeed18d29847c0ffd8b7f305f97a3e259fcf17f40d53742beb51c78efd3afe7cbbd21b658ce28a3a0671419b602ea8cb19fb52ffa0e569bab6774d864f4650c6500611e3cf8f73682f2a16198bb88a8e186ffc3ef427987517ea843b6190b02754fad8cc257db6b761626eb7985f9069237b4a177ebe447278ccdd82b0342dda917f3bbe52e2cd62fcc35abd7144318a16bd68b9241eaee4af87551edafb42153bfc5acd3f2b9ad5dddddd1d5b77ad8b5438f993f2adbeeff541e49df7de1b15e3dd7d9cb88f59588a7a52abd59f6ff7eeeeeeeeee37b2f81dc8681a49c9b491bee090288354bcddeb2bc270a3a2316de44d8e0f6f4886ea088dc918e70ce3986dd0b8bbbb3bdeeeeeee1e4483ffbafe2df1eebd31882e348d196f69cb45d69657550a32c8ae08a885505211a3bd19617ca7603667a5f4d1ec849e5a1da23b6da410290467722f4dfe0d39e6ff7baf5236edbd38977350ecc6dc6e3e184e3c67e110217bafe9a68bb414d22523e4ab0021ee3dfa05dda0c422b0fb6387c8931fbf2fdf00b660400161ca87cd1386b3b6effd05d9c5648670dd7befbdb73601108a24bd417948fa3d1c04f31fb8cb235bae9f3a6580049d850d532688ceca5c118faecc8f630d6190a9846f465d52972b158c49e00739ab6f34cb6e4d54da045e6959fc2f000100318995561268846bf5d35472c7ffd748eb888ad6910655c36b27e484140e1947613482464e93ab0f885a45f78b1f9661bea6de2c03b570ab6fd3ddddddf7968fbbbb04371fcbb047d3dbe3ffff5ca9a336e87de2c19c69d577da4877778789b9dd68264ec6d8fd46d125b60025f7f7ffc461982d1719b885f4e238f9312e815d5ce993e1692357100142c1380cf4c2717f5723100163ac7fc462412a532a30264399524baa842bd642863639c27ed2cc354c7f487830e6c2a133f06592cb55372daed5263bece5722cc262614990d58ad1c68a052741472a16ba7409a4d27aeba8559f13d1ec6634668cfee3a42fc2d1b491389b9c6e97fe4d782f1a424d6a3928fd7bfeff4aa33806511c83afdbce8f1f7f512f2fb9dc8d6f0dfb3bc9e656d5ed81b7def758cc4809dbf766202e68bd270ca20eeccf47860aeec62f5b55651568d703ce59731a0bcb07fe02acd9610ab4decd0df026c47a2b90b977015384725c228a5744990b0523eb075290203a7edeb7cb939bc3d29b2f8a81609e92b80411c5f9c56a2dd147b66385c5b54b01a36a27632bf0f1e304c70a1d280e646c8dc43dd65aeb80124a7e29ec12e742121f62d65a5b334251b73953b5567d74a78dc4db5acb63b4c5d346ce60d284d93d13f210576b2bf4e87a76d6da99aa756cfb69adb518cb58e89bf153b0589b065031c6f72ddc182d0cf3f0588dcdf3c888afcd682c8dfd66b0dcfbf4a3b8fa464fe37bce7e617ec7dc68d8bfb82c7f45599dcd5ce93cfbdfbfbf63f1d94baa04df30c69e5bd0b406eaee982b65a94ae2c339a0b52ce369236f5650e38c5e17ebef9757b186a78d5431c214bb12d6dc7db7eccac7e18f31d88acda719c5bcac8be44e1bb9629ca40959fc47f3ae25c9316da40f6fe868260953be9d1bfe8d448cb7efbb7d3fce4472d6df2f8f62ed30d1fa5e5363fd2dc2aebfc5e4ea461386174a647e030f3747214d780099a464fe1663c4c7b491bca1192347f6ffe98a08e7552acc1873b7dfeaef97dfb4df16dc1ee65661262be834555a09335f9ad4add3272a0e2d7887dece2997c4207c9936926686c88d6a8da83f0468da482aab992157db8a65e603ae1b6f6cd233427a3132eec5cf04e96df3698a97167d5e0cea9729190e9dfdd1226de69c35cd4d6a47dc9c33f6379f8c762c97eac8ca34abbea862161c7dcdc4a03175111ce4108441b14edccdb4c9dd6e5cb9bf2a38a3b7f426148099aaf5a7276e259d703eab6f5476e232c6472c422cdbc52e27db9970f18dff1cba315b9940d7cfd4d6ef064ac5063386c60e287c4e1f76dc499cefff0f0c6631894b60993652ab6b2df145b39ab8894d49cd4b810231a7926a9574024105336d3026e2749a20a8d46bdf29a9062802431900000300184892344ce35a720014800727f068a4cc6c1c0e8c4391301446411403310c06311002000c053180e33c895575436464ab58f4dcb2c9dc7e6658f54a090f56dba56edba1b6e001b971b7ed01e6f89af44d80e1d1735491b11ac0667742ca1b3e11ab83348297a1451fb9faeeebb9fa9fe8ba6c9e9202ab39dc53d522816eeabc75562b38f6fea6aa17b48dc4137536c0fdd1b5374d00900daad7ee2afdeb066095f83e22f61d9e875f4563df19bb69171b0238dd2a8592954343c9d77f9cef50ba45c0d2ca44e8c4cd034861a4b711a5011751c73b165b1e4e13c7ab080a40507d9642b357701db6948bdd7688aa5aa26f89337022fe2264b946a01c5e5d3792c1cbd4992f7b7e03ee7b650016705a78080f03a516c4bc1a17472092e7caaa3b4680f3a0e94656503a67eb5c2abc1004108d378bf95e874881474e5e58f36747977191514f34011c2a40bb9c32db3888aa5e1a22164278c1021741b8b8a83e9812553cde21a5eb67ac64e9df79ea55a0462432e94aee2f40299f9da19418cb2f133ee2174b642185172c78bdce31c524d48b52599967c31e832e5de10c248b040b06d303e2a22c249b8eeddbce3644f11382315f4d78d3dcbd3fe3f2113f56f1f013ff620d7762b1cb6a9ec4e88cd2b6c7562a1f0cd2c0d7fab16edb7d3038d73b2932a9669e59f0f2e97e2ddaaa69721fb21529d1fb8d08e4d86d702016a0190c2912a198208008203a6db30953b4371f4178b4b9a896f3128a317147b7a7dbcd89cc896208686d2b70b5a8da1135d41564f2a82f1d6e522070dbac2acb04f45fbc9ebb94574d1e4f0ab2bfdcfc3cc907afc905b6ef1d144690a2ac10db85da1f3d806e4dbbc53a5990f9893644e76f7d95ca22932530619849e6942dd78ebd95205ff46f47d64b221f89ea505cfc79a10f506ed203966250ee3bb0235e504e931a8e5284e64185f491711436b73be01caf58353f7de7765fea97dee0ea775dc83b01f003618e4a1b97261008185f23375e26a7667559554e67b28d7596b0f2c1a79f50ec8bdaafe06194065d618cacc3ae0ff8610cca00b8fa0830ba1bafe92953b3b0021cb657bc989b00f2f3d4715c29f627d6ac00f2c55c629fe702e66c01ab64165072284f27b9c68114dd35d1552e41ebf08b3efa002946910489627f8cc7a32d25fd3f59ac73344ff4ea51a122a859808f1179db68a9483d8e4a4e0a3e774e20e5f401872d151f0ff27e7cea172dfc1be3866527e85ae134a1aa8b2d2d61d7aa5f42a92bc585ae07af278566d650542eafb9523075405f15bac49634746b1f44a460db5e5f296fb561040a12ee9a713b057a0fa889417a95ce9a43b0307e1122390a1df94e96c7681b4bfd2614ebcb6aff490ff9e59bf91891d76d69b94bd389aec96ac822f64537bcd86ee8d1d7522965ef708cf43185011317ea6edf8695c8a02e5724fea1cf04206412050e6544f583007ac11b0bc4a5248801c779929a951059e423e924e0cd011a38c6d3f3e0cbb4db2d95976a3a7284ef699159701420f6119e7d2ed93fe541df1d20fe788916e07d88ac14915cf2b51f4e2c5dfe53dc16d460092ccb242a72e975aa64ccb3deb1531e1746862c96a2d965f313b5643deb06cab9da6b99080ef1341f5c38a6dfe9130f2698b82cec01e9162d38e932ae23d141ef66bef442d80336aa485d721f08ccdb0dc2813089c5c4757aa0f7f43d3f1149568c369bb011904b4e918990fae1ef15b59a908bc4a8575cd291e4e7a1b846f6e867706322ee16b78d5bbfb6c9b1eb1cbd15501b2767d243de8901ff4cdd7a24658c8613df3fd35c426622c1342615718a5426b77a18fe765254258d9cc152e12b1ff74d3d0c1f202f9a4aaafcdd2b92575eaeec3593d8019e32c9d336a9ab3c2be80407a384ff33a9610d8c17e4f648b1fc538bd8c1670c433902154383c8657e76f454d913f503b06b0fc118e441d444167590b4f23a4238028878051012b9c5d024a25ee86b17da2693461c7456dc2e13a5287d5aeccb08d313d5495170f122624b2f05ef5320766b04d4274836addc885c8557b4be2c5c10e8c4448a1b547910233a528e29507595e389fa29e2274609dc5d31a0685650886d822239c919086aef06159d7514618a91c8212b9fd484a2961d574f8795f01ed2f5736e15f6c261d6cd2ecbec37aaa3db23ce80842286c1730775551eaca098d7a33fb461ecd19fd28d5e8103535314ac8f226441828b2a72f10417e0f8090746b3a780ea783becc4c11cfc22bda57270bd69f8caacc6d165f601d0c610df4d58685dc44c99b21a64ad888c05f745caf5abe38f9c31bc8a37eb0a002f55f32171bb1e8087adc152c47d67c45136886f7c83006d26097208410393c03c2eaa8a07b866494086f3b868a17a01e115c8c21f3b0c29e1f669b659c9579c38e09c4de25069c537878c0be927d3beeda7e7d032d4eb44a71f203b99b3321a1f8cbaa00ea103fc238cb256caa98454d701f2bd9937c5e56ba620fd553350d361bdbc6d5ab98ece381f66ad2ff6b259d5d387013607b81eb91a71cd5d81e27a95e1df9d7633cca6c6fabc1683160543fce077a87f52d2e6be902d395f3789ad0c018a0b2bc4b265ea9133b09c7638668ee4ef2c010cbd73bea08df79543737adbafde1dedc57e5dee69d60fbe6897da5ee6e707589553418f1c342406ff0b764e427a2e90235f8f5d71f2402958b6bf3b853471681f36f8a4860d9358e8fb54fe54e8759e0333620fb16d1ad477ed5a4e65a791c91043665489c32f4edbe28cd46e5ed2d28539319ecd3c87b22f0b6a941fe61a601083a9a1c503d0c2d14152285b2ba8a076fc08ba262ffb1168410aa8a632e1eda7732d0920ff97016d8d8fa5b3055e2d2da8dd20ed940097b829b0828a12ccce95720f78f96226a06f1d7fad303da19d415ee27ca0b3a432c6e05731b058bf7a8d753a8eb7fa5e733423872542da22d7cd7a0d3daa6d7ac655c8831014cb3012e038917f7729de4edb35f895ac38e1a74584514a4865b4ce02093f590be33f120b2c41e86be358ef83f6042f5c63b39136b0c5356106a31ab2224bcf693db0168c21a8c82548b4ac4f2175e89ebc5a4b9a152742e74faaea7672ae72c5da76eab5c87f346c70097f0977a3e9239ac0e26ceb1731bc23bee1d216fccf78d0d0ad441c3e981d382cd7dba60f42a7de8709a07510e515e54e5fff2b7ff804a128bfc0d5a3b36c1fd09f89285bed6465bdaf587f4bd58c1f1f34c3fc4ccda62b477950e2ca248fce3a16f09b316e37e13a6a5f6f9b062c5c7122bbd3deb4c1d7bc0ea21e0be163eaf625bbe5dd09ba6fdafa2b2d4cbfdb12407bf03107245b718a2b8bbb1f7e958f1c386aa9643deec4c1f6d83aa3dfc9c2277ba33f45f6f7d7670062b039b1d0556418d6e02884463cdbd3144b81547504517a2aefa4d62f7b0742464ec0902db1456fc8abc9026bb4663c28434ee7355bc8179fff38631b16105b961ef84f80cba9b21d364f521d41943dac90782aa0e26998dfa6e765051261b7fc6c9409760e704e691399b153a51175ec0a1d4c9446a9326cdc791d959f7c98427b3ff94840cd0b58903a8702ff8adcc8565bf6a96afc73b18ed70cd1ca2a921428c983558e2ca0ed3d8270a1885ac875e25fef748dd64b11639727b188a7ddc837396357a4268efe6d3cb797c3dbb309f50d91348c7711a5ca4e5e3a98bb8f03797a2328a26f8dbe633c04e9afb0eeb08cadb6133d434ca4896189d0eae3252d4adf1aa991c17d6d08a0972d0f848365428f86222901a7274f83fa63115e8cd00589583ce1cfc67297a3e071f66468f80c780957be3d09bbbd3b7c1ee433122aad094d7d776a257878f832bac02ea5dfba676e5c933f734b412380aa22480dbc2b0320a5ede15ef00da3dcdb36ca07582f8a71a464c729d8bf5950a97fc8e5282c5016a51e994494ca132a89adfd467ea04a7faa864f1e5e3a3259ffb9d7bdd33afb115f2881ca908c6e1e93afbba0f987433525ed0b31d3f63f770e16ff9e0a91aa348c4cc29fdc2fdea6d22561b7478426a7b2fc059e8f335d741351853be11d13761e39f50a1ddcc464585f62a1edf4fa1b02b3a26468e96bf345f56de9e8e6a93adfb717149ba61d9ccbc97270759c1448e000fddea56cd9caee467591affae074b98810cca5e37b5d67b58193f9a1a3ad84ffc4aa87f50134f57b42814116dd95fcef119126fda89c2a6a0412e1436054429caa521e630996a4486ab7a8c930c9a37cf3e277321252e5497791cbe7a991240564120a7693eedb9e2529b06042fe6de4250788cb4abe28d221adf5dfac086b88e882e48a0a4540ffd2d02628b2335d49f4162c890ab22bb127de32104168adfad02e9e0c47ea9793bdb2ef81b028c6b2362f51c15de586964344f70fd9b442491d2aded7c20c378d4fae3f4910e5660a57d6e0626681cf11792e5f3fc764078e6577c1afb89bc14607c66f9821cefc20d956580e0bfca92c94a0e872a49775bde3c86637a7830dec3da82ad457248169b0fa7ac3522e379f0139f5f9ba36615872dbbbffd583d0c062d429f1e4addc080041cc7b345399a0b440268e87f8cc2457d437684075b66e6515f8700b65c345494c3139dbf7f8c53cbd3db5556847a494b700e76794a183f3bac9e2b8c44bb9f6b742a6146e6de7d9933876387136d349ccff872914751af1615320a80ab2e8a7df0c52218bcec1bccbcd3edd06390c61e2ac65b894b16be71c6665ce19d18550c25bf6d48b4c6fc52c3a77ca1c0b849ede0bc1891134d0b2ccca058a237d9a3a9954fce6bfc45ecdb27446d77c2c663182fc938a2468b8140bfd24cb9a8b8e58e48b9f1915f29abe9395c900aed61bf18e6835ded2e295764634fc7fc272c2bd3d24df693d1d0be7ee78bb123f7096cc6e569b9b4fab3f4bb64cdd3feeb6a0b6c5306df06fe5249409b50725c5f4d252a1acdf742b6b7e98be8f2b3db7788e0963bb31f9907ef34e624d9adab0df9fa074e9ad2ea61984a71b46bd0547b961364221e4521084b978a667df5e8d9cf42ab72a35773a1d181a9ec3c1b1d268f77f71cb333d7bc0f7673375ea60b7f420579131b700cadec2779673edb4d67432acc730519c08b285aa05555728fddf074be3ed73c202def8d8f821f81490f29f57063694cc87455490d67019507b29de4ebf17bec2e3ae2cd49c4ab8bb78e624451757b1344b7cdd6e231da9a26663e1a88eb6849b4318bf46eacd49472f4d60ce1f985fe48f2ff60a665df495e02c348e8cd16ebe0a1f980e8056b9e3e378f8db39602581db1e08a393fa7380154e66a946f0950486e17429ca069700bc17a707e19a4c6a2d0f8ef3e1f17814bbed5a6dccaedab6299836688459ddc016eb53cefa442c8ef8628b5c08e105095e04e1f2e2b18f8c17c2e1639f8d1da7dd7b7ff5b583f16d502ff32b2c23da884dee4d998f38e6b76fe0e18864f5ddc55ddfd14a32fe714920febf268004bd49cd11b10e6e1151a1f792de9670dd9168ce20dc342f98a9b6519d78025fdd3043a7b6558d93614c476cb6ab760f4b254211c4a266a266d255fd49111756130b05ab0a62f27c917a0fe2ede0b527f9a075732c333476d0cd999dae2c4a1a5b761fa1cebcff06b803255ea845599acabb012cb765237a98b591082ca5ac27d8069195850e056fa49042daeefed376f3b334a6d8b663391a17dfa3dec228af96839f6361f903594f0cdd57767f5a1c9547d006623afda3f34b0ffc6e90de996db53702958d8af0eea5c3944b29e9e74cc28a12dfbfc40bf3b9b893bd3c59c81ab40f7ea427613487b58d8be0989cf9fa2620c75abd1d51c7d3088271d84e8632a0abb7249e7ad613848b585c53f7c1d925b39ba5ee500ff8debca1b67223417e89cdc1b5b92dd42c0024e4f8f24268cb8e8b419f2c5d7cc118fdd78aefe816c1b7b4e8ede44b35310bf2e6f795e3f7ad533711707197134d26183401482370a6468a2083a9107ead2b6fa987af6bf049b1689fab4de6e20562acf85a9f3b740fdaef7095358c11cc1779e8519ee9f3fd2e354f57b3b395a0c3a70f03f0f3dc10389255498c5f4dfe482be5db4604d5f2a0ae1d694e1358fc065aff8317c6048039ce44fb6719a8551521f6c91af668cec693da25fee4e78e0eda928ee0c41fd6c2a1caf71966208b3baef54e3ff89a0927feca51988371b9a5934a7c008e2dd738e9a9f434d591e5230facbe87275752a01fae54a83816aa6258e0d722731d84c1e9f220b027074c2f98e3f9749e1930c6fd7aec7d0de5cc834203d395df7ee6c637c4186dc618ec87f68312a98bc1cee2bff6b550449f66fe27f02874f5f4ccc0ac9e15b930068d791526d54307f6de43f8c330d098625d974102524736909bd6ad31e4d39cabdc5443959d9955d2012fc44016e08726406227da5029c6c76713a083d48a6a3176f24dbcd8f8e80ba7470c695115cbe0882d150be01a659b8d2f752c321d24973d91f8b389576e20b4c9648d8397ae1726e899263568bc04dab9c6b13393b9b751bc44aa2ddb72b1cea60800449ec4b360374f49c56d74faa1cebce0b3a730ae33f968dc3d3800ee478fd977a056250dd20a85cce795ebb9a95738accffb1b6866ee1bf1cdf1439ae05bb9fd4dea43b025da024f4d8127c8bd1b3e2dece7a48b97302c730dcf49e6861154daa860a4d82f90d2207b48890c5140a61a6fd2ccaec0a9753dc3227ca1518343f49b699fa970629490e2e4ecf07d670ddc702d5fbb0c7057255509076e38c0552efe56b2bd40f2f4b54f2225939f9bda3139dc0f2e38b58366125186eadc09c2c7be1509806207aefc6d46f266bc8123ce42b95b5125427c9f784ad6766cc815b3b9a3e96bd846a7ad77289627c11982d265154a87d74b118654f06a3847f2022528f043f6e89583de7654a94719b838dc3bc671e933cf37868346d1904d2ad7147370d824e7fa6532f80f460210c7f9ce1165788d915d84c38552f72593acb56c5e91d342cfdee6600540fe8a94c04bc70aeb19ee688660bc6279921d1c56618791a3720aecc7f42495d6e6516cd0e932a4c6377e85ede34d40e912e9809fb4837bb69939e3de1597c5b7821670e424b3c4c2027160369c49dd2e0f665d0138a6949418ba3c30426f492a5c7f21ecf7806196167de887fa961faca441a6ff0cf45a237665844bb4e090797417a46ed4f8cd4b50ad1fc7da14ff4d996150638f8cf3ee76c069770c0031465c0a7f798494676e209367670d47d2832b9ed5490c32d8253693d301e9648a59fb2859212460edefd062508b3aaf1d8342bb6708d128c5f49509a86802f42cfe3581f58bd1a510f775bceb29d1dbfc44dbc64105e9bf5ea43e41ba2962bf1e7f3f490f43d743eb5d651214425a3006619263bb1e47bfcfc25bf9a9dae2a1827cd712f50bd3a5e4bff00141a954422a72ebfd77f4f5e63d73098ad899ca0fee88145ffa16ea2fc15bb85231d205aecd41974b5b9e71ab579e2b85850a3906ce5da7951df71f5ce0f4600fd260a22c6e8233344804c900055f7a93e4e229a6a7d6c687f6b9af574746a6df2c2b5b7c774baaae711917529b70381542da3bde5edde4a2a6c9c8b7e922b74c0860a7d214c5cb3532498a8b00740192982bf2b1d5a52043c94c3f93dbf5d482bbb2dceed6164d963f7208d833123ac7be34a621a6efc19bb9dc4eadae91d8452d0b952fbd28a742adee8504930585fd1e589ee25a8577c9d0b3fe0411f6cfe6fb208ad6798bf90a5d905014224809a621e7d748bbd4ebac2b922e749925541901d8fc28af44fc5fef6f217464c1514159a82442fc66240f94a28ab87f1c15fa0fd65caf18bee86487b5bc598a97758083c3139a3d8200da6ca70ffe954e4362afd751bba642adef02de784d0591a9e9942ead2e49e8afcced52973d408f158d71994e3f953d46034110f7ff9f5c494516e9d5ef1b38bfaeeab024fe04e2e349bf125a592316a2d901f8db76e2319b7ec6a36c95310da980a7948b656611ee0252a159360226f02b556162e9454750de246ad2e107a367a4c2b3046105dd5600c89c55eea39d6f784118028768f46c31838da171e2dea8bf004a88b055cc001170d5d3bda1705f3aff4c8f0cf5d93121584c8e60ba7e8a8df75e08ca81be1f98dabca9012ec6055310836c149d02c52941a06d9b34ebe576866101a10441e7a1934b58d81f700e4d3fdc1d3d025b86298efd9fe1a16394a98828a089f2b6e1d96757f5808d04add095498bf8307ace5696e2f543877aeca7b0ccffa2794bd03632a133f592382f9405043cc0302797c15138dd7062325242c6c93e617fbf9ad8313bc586ce90101da8b4c4ea022fb60e90909d50101d6b121ec5ea620ce621682076d1ff88b0d241856e93a2a4294a1154732914411ad7fef4d35d682b2952f8a58d7dca21714334a966b317b50410f2f78438c402e7508a6d544c0d0f34f9aba8e364fca2d7f0c587c7b256a7e037db641324ea5d0255731e25cebe504b36dac2288caae239c17d638188f55332e6afc817b5342ce03f753d6eb4112c3dbbe1819105339d3fbfd349d876090beaded1c7097e1ef29bca4b3ff343d81c3bd0e2f1bf4ac43fccb6da9edfeaa3d71f3556ff972430a698ace493c9dfa51b72f31aa7029f80df353809341f3e081677f627c3c1ea885221fcfd488259b50cc14c5a89710cd83ac8bc5e0cff5a38343e2bfc20b66cbb536a95c8f4c758d860df1d3243baa5773d1606bd18633855e5759d05164cc8bf2d8165c03d5bebd63c513cc57e5d77946704f0a95aa114d280a3b9e31625ee6594e29ca11692fa442c8ef8628b5c08e105091e112fba926b6a4471f1a913a79db7297e8383efe367b905dcf0304f257e158130cb0d8fa045124f6e464d996180191d221e3d0e160120b7d7de0a818bbffa0a4967a8ab8ca76b437289345d1f5e5a02472898e35a813d98fb8dc7404a71627aa5d1b70f3f64e6bbf465fa7e49c1af5ac7d7b22b5bc2bb4d40bebf088b580fe962427e16a25223967a9c068f1653eb062fe3c4bd50667e4927054eb29eb8c282000ec1caa0894b5d7ebebe92b1c686bf11696e15e88b931ca0565e8d0c5ef0903d462731128ee366261840716171e5e66e032603bda4fdec79355aaf99a36fae561859bf19d39a16b540ba787c2d30d50978ce4c416e4a2343d6db2886a0d9d4c9e7dcd9cae85c3568335d3e137754a177330211d606e87a8bf7783f77f504a6fd5209cf0706034db0352d3d5196a638242d8302fb9184ef019b724a5570760261f61ca4c6a8ff3a25b963981fae70b38bcb469c686b799e90828ebdc6db31a3d16082403b90c2acb0055085fee74ba0feb1a39351e0a01be85e9dac7d3ec7ca975680c8b1899b74ebd549527c71280951141d32644bed336652f656b15a756c4827bdb1421cf9cc48357154b73075c78f41693990f3db59a89507554b1e2a88df95905e513a9e16d4918afec970960a63f570e62e034282d06e1559de22464d93a2a425e211091a24412eae71f6c7552ef42c0691c44c40d1adaa10db444576823311d4de0d283aeb2fa20541113b477973aea080138201055c5fd38740646c0e720049e5585eeb891ae6da37a238df8495826925a95d1a691f0b94cac009e395b449d90963c6de04d589058acf7c57403c4f0b215b0ff08bf195c44317f82dee28d361091aefcaba77b87b2056e47e2efe1fee80f8ce3f44b60c347808b2f502e2733bb0b37371f3325513f0ba5a6b7eae56c498950d5dad68fd8b2cd5bf6218a3d31c4507055ca7626fee04869a156c6428d6a96303047ad8fa765c0470381b23e626dacd4ebff60d2dd490cfb3a04dc8c4ad0f87fd84e54e02aa93ff691153697815e2b42e3fd1e1b38bd0fbacb757b6320b610f0ed790d5dc166c9dec47b4001c2f9da0fe8c9be189ebc45f08cdd5b598a845783dacc82048e9a5335cbd3e02943d875e857d8915ea2ffd3eccb20c39b9833bd6fc6f019419878486cfc31c6be926b9ddc362f5987871e441dffd204ed405026a8f972003f3712ec86c5d55cae152646639d4fe669723ffb10219ac9ba7794e64c4c38cdcb9355519b6655bef7a2906a5680b84048b2676382a84b7f9958ae4965ce9ccb7db2f3d2350f742ae9032873f1db72cf54b293b66499e3a663324b6520b4efaffd0d5047723c4e891bf5aeb5b7347cbfdd4c7d9bc23752daafeb3931a7cb7076a929d157bab6030ae425ee5e22b80fc214d5045d21de871a544253bbab28425bca2677f5027eff8f2bf9c647f281da0f7552972741c42683edc011e452c5dd4efebea9a36955d65069980f6f29f3f2255302125c88ea4cf26f477facac7337c3981518313a1ac9fc4f0d469909a1d5121d2940f9ce1b84eee1199919810759d988ac3fc163c3bdaee24c402d3cf122b3b7cc5175bb89a35973228de45f39fa7ec4b8249f616babfe09f25e7cd6e6495657de9f858fd1b1bbcbea9a06edfe8414682a965d4f9cefd7f54ce34a1bb1c0f2f61e7ee648325572d1b8e0a8a73f695d200498db42d57477edb1b9cfc5cb6c0d4b275150b003352a578f695af34374811d1101c020f1e9cc5154d2f3f6806b5b96e978853eb3ef86daa87823345f0e3fc20e138d74aeb1f493b7418c2a4fa6ec017a5f98f49771481a7d3ca6b1b47a8b2a9b83cfc526756093fbc2aa3e08ea07083f1d5115565c52a83a058d95fa72d10b906065aff8312a0857e257db9e82217f647eaedee4eec0c8ea230d4639f98b97d194e34bb695904131069fafebf9c849d5f5b6df27dd2d391837f394e9ad42fba069c33560f95061a7ce8f73fbaa67f7da1d1f75b994642337fdb3170b82c97f0d86cdc21b65315715e0c0e5baea029cf681d00f4f0b515f5fa9e84f7904210c2c3a83b20322ccd9f79ff91c61c0e8057e978753e8df5f7923feaa2e67c9eec3f511c1c4513987c68ec41ef8758261c9fc940e3d19d4258fdd9cfc95e07d1c60ce470d365433e831b30aa4702ae3d4d706647ed3c26dae174acd557920ed9c341ea328283a4e402e150b7a6abd55143de939a45911b66389654881664880ee3b461686b016e1e452a86ba630d5b00c061d824c9213dc7d0db832fe8cde0731740bfcd68b60d75bf872e3183cbc6118a79e9798c892b2ce65c6f9c86f2bf744b810803fae062dd4881e44d2e96237f2642e2bd98d41b0ffeeadbe94bddf935736abe8cc36ea7d653b4cb97b454316f173b27fd74c960ed06dbd5d57e3e987746615995b41ab26af409699e5a6d39bc1976a6dc91dd19651a19114e837b3104c90a8005e47aaf85a0cb63489eba3817a81b696f9e6cf67d65c1111158d7098ac416bbc9ac4e824086ca6e32a688eb6dcbe7682de5824e83b55b0be811209b0a1fd01b1368ff0849a010a44bc97f39f67d12deca4f6ac90d5d410449a55ca80859fb36478987da0fef629673151cce37f123b508565021f2c486758a762dd28b1f1eb39bcdcde14c5176711ed9bb737b227cec55a3a3cc5cb27c9dd2618929888390a47460a9a6e5a8c3c6ad271d0af85fbcc99700dc4d91b0fee78d2666aa972be288f2a3f6305d7815002f697b02f1baca9bf5242c57924c9124d650f9de894f5ead082cc4dac171f6aafa9f6ee8a7d102e36a0d426662207c9010294a023a88425bf46af0bfd98811b1f54337ce7a2b312d4e38f91a8950ccaedb049d9992007671db849da02c39f046a913ab93675ab82a531eaa018fa38b238f96ec6cf6fb3762f5f8db47c3e589b02308653d77e69bc728130861d512a672003d39a44b7287b51388a4ecd27a9500a7ef9f1c086d4382a7ccc9fa1aae9d082e98f062845890e8e2095f2411d64eb17aee226189154d99ffac32371ef8235fa853e6c51d287ab43f68a82a4db65b9932f5acbbc4c33bfed2a78fe3f84af7bbb22b5f1f3d948a900554d31a0942938cf17ca4111d65a88889130c81048be9eb71289443514371fe90177e07ce1e1388f353447418fc3be0af12e6c064eb79ae12110a453f5e5cf4be577d511a18fc6eddf45a700a1786fc3ee817ea5a980edeb766c59f29a68c6b271e984be466124b42d4eb0fbfe235c1826132e122eed115611484ab96c551752c4a0f036169068d18f080d05e8dd14cd025743c83d659e567d415c399593b534c635672ebc4f33bbd9bbd00facc53572295a0b330283081f85fe32c3de4c79bd3b2cd016426136128631064e6d077ae0d377a73d75ff255bb001ca821ebdb8c2bb00ecb19d7033c7ff6b452d3dcace8427e402b38d07d9fd6397ad2d2f2c9e775ac7b8974d1efbf9df95a15a74baf574050e0aaec26de0bb8ee040fd431ce9d2af1733915d4558a62f415d01213db0819f0e9b81b0ced361824a4ccddea411f0d692c3a0cf043ae08950625b90f9d9c50b65ec6f131a182dcf51c5b2d462806966386fee1f20eea83e3379ca968ce2919c49aafd1b95ea2b6d6f70aeaaf4cd18dda2f9295fe10065802de78effdaa2301b95f2d116f5ded22e3a5cfd0e01d5659d2d8be1a001ad4d2500004350008303f612a8e68cb1a0332cfcb0de41f824fb4268a844ee38bd25ae212b533542f7e0685e229c7a80f922a4c2591a5c76a85731f0ce88554e41103fcbf3ff8273d1e3399b2cd951f3bd24611f51f4d20ba86be13811469fde8718937eb5bf079428251c7a7848ef938c229dc764f416c89c37244ba8471de5b079e0f4527a3d4e169ea777e74c0837c1e35910b5bb24fb719d8b5f3417c1ee847b01ac2d386b9e8129921b14ed0bb9af87dd23a24c744d108ecd50645ce8891059a4b62d12cc3502fa0bc17a89d02d1aef815a104c93c2efd0f92fd34fc34a16c6129482d97e83077b1e481fa16515a684ded66fcf9a22d8935a337682b87a528ed19f3eb353fedac3b1ae73b65423ab04f315eed0e7c8f7ef198ffe3ba0e42256c81b057f14e6bfbb79aedb283804b358731384104be8f7fdb14af3648bbc52a782f2906291f53bd723fe9b6d9cdd4723db472fe36575479ff46f7f970bcf6edc7b14740700193de418016ea67927009207c0a12c7c18f7266a7b26a5eff000cde2c4154d21ce31372b7c97b16318449dfe92cfcf6fa29056c43034bd4325f1af9bb3b0cac94e50c9bd7ea3ee1315c3b813465f01754e7d769f4d24928dc1c5376be76e9c15455816115ea4fba0922947032a9e3cb4c17b7ffc3020851f5033a8bf65ec6384f19a04513c54f9cb4006d1ca01427d162dd8a0550b2228b83fd5ad5f12087fe5ac4282da3841c03ea4de45bb3efdceaf83804d84ae5af39d422d68fbc341e5c33b8da6daf926829928c47e9427266af59faa1b7259709813ee4ea34392e60cb48c93f5facde9f7be65630d03a5c6335bfdca7ba7ca52869376e633fb9b29e03f5798eaafe52e36823976e9173bc743796eed4ff6953bf2617480128cce5989ceb44f8b45110c364a822024c47ad64aa954b93cfe405632189d766b30555a0ece712805d65419dc33dd7bdc592197fc16c612421811a0571647aaf3ea94560d8b8eba4865be060ef0a0857543f098f796a08dcdf6ef750d0ca8c821ad935d09fe33437bc20cace549d2861fc2c57b53eb62e968836868334eff52ac14c50e218567175ca95255a57099c1dc33e679b64060d1371d38dad8c6c35e36a043d62e31cf667b4851806c7ec4cd3d02fcb66e2b9cab3943b11fc8729831cc0b72ed14bf9fd671d3b3c0139bb3e4c746c3bbe00661457c16ca5836bee53afa32069b3cdb2e634bd8e89dd1cd4c7d660b842274dabbf7acb1b164e33ec2ccea27a19bf768666e4740639a5199b0996f3528f846ebb070d6b09dafa425a2b3012e10149e5e40bf4b9f6cdbdcd368dd0e504e5689c4b65b66211888f5c4e557798f11c84074ec4d700d4f106ef7f5d593dcc92a86bc74af718b0510b09705be515015065e63b1c3db799005cc4ead26ddd5480427b8e159f9f99535e623b540e8f4132462aa7691a03a0b60c3222e35757bcfa6c0cf41a2809dbde6f0d96c95e077267113ddc1978f6f9213f242e468594bcd7632f27296dcb2b88a81cf0224125f0c0e6ec04ea4249ad598a475761f1dd9a4fcbfb0fa4a811addcf61258d5d901ea9eca256119ac0aeeec01d4bc985514a5e56e5e3a78f6d2b15f9dc33f6d89e04beae6dda5cbb00212b3c36ae7a0d8f52014ef058d05d4ae69126b7f548b5a34585ab12a4912f3c4b86819857701b360a4aa7cf6c1614d6d918ba4c01b098a81940bff339dc9973a37edbfce5370d1f54adacb94ef17fa60424da93c32a923794204c9e081061ffd3c9b3474b97badc54468ff78e316a6368693a4b4b813f48bcd195a2b359ac7cac34a1d19403ce57631b70bde237427555d893cafdda21d44d35b9c6365af69b7611a2972f05c5abd2677f3b09dac3b972ae865f0431a76881a527664ab8513ec5db187242327028e06916b03360fdd0e82b11ab4033290f0be70766b397ddf029e40da99814cccd246305ea97b682f8fa158841f070b234e42d58de069e0353637972622b694600d419d735aa55684303d428d3d34e2b5ef0042b1b9c0ce2fca6f7e575cbbf24c0e86da8b895689e0b37be15f0fd4f217563b0ba056233f7a928573171d0e3c0897b5e0ca17068a2a252fdc97947b40829748745b04d741f724ac17b19ec9ba3402e42079ddab21e182bc471fe7831cfb4907f0b5069d7a8fa3b563cc1333766b8ac01cdfb880053aa64a1642945c9a7ee41b3cfbd2ea0b7d4aa2c13a82fa035203c36ca52158ed566486aefea154dddd8ae4192e7dcf007113800e680b44e3eb64b7eb56e7085b74fc5230cfe7c74f5959b7ee9de66dea8d2e24032e2b3803b1aea2e6d0545964751e2e34740a5430773b893eae669953447415e06eb0dcfff468e90e13e04a9ec53a777b65846966b8ef52fe4d73182f36f31c77da07beed8c941678a0e8a6eaebdd693a685739a89769da04222506f6a0aa7cd1338d42f59cdfec0e8d128f52d4b9ff563391a813296351500a01b5813828318f5cc48ca0642ff04b2369d5802534488dd4d81a3a701349695a3f9328a78d8375cd4eefdeafc7434f44b2226899c9b3f85cd110e830f829a56551648b52629e8edeae1819bed1828f9b5004c1ff6aa5e025591c91a2fada8db6f906a259c96e7c27afa56bf62c372193b734c2805ee980e2d8c5f730935cd18bb8c408191529f345ae0ce5fb0f15d278db054e533ee824d61f3342bf4a6c9060b0e031ea40e0fb5a6ec65e7d7e43ac15354912c89047a45bcdbd701b97e5848881db2da72c6c39fa65d53160f3c85a7ccbe202734c66cbca34ab2f150543acc022c4c3cece3c6a4b39e74c342924e0d0d8945039fe80df2cf235c697eae67789760c9b8c2cfc1e8e2546464dca28e9f901688653d091ad91d64d56836fe556621a2b6751b036be6bb2b21936a58ac5958b51344f26704e054f27349291e28d8feaa0355a6ad7c959bbf520f4793b118b98b11630b7c8e803fad4a8f7f849889cf4137363c02db06a5344600f7d48782d607d43cbcdc7777d01f5146499b9b00514d8af60278b34d1f45d2f601b855bfa7b66db37fd4dd3faa813044db4d6a3ac6533b2d729f6882cb33332e80598b58095a12a2879f23c8166552c3aa2354c13a2e91a360d61b8432f6ae400bbe80b9b78d1567b15751b5ad424af1e0bb851b36438fe3a19e4cc9a48c063d8941b4c4267a04824fbe88b84fee4e82ab900e58282b295a29285153f7ca4ff31c02b1006cc95116f0912c09b42bf4c7df72cfc0c1fbf4b801c99aa8aabd1e10f56f89e89f27f5fc4288822b2da0c62348e5c98198bc19a4272477ac723a7b89299e5b7299fe080b76f886162e0289fc9eb96010206eb861f9e58b31053734cb58f1f86b8318454e56b97635968eadc0616bafc0c95f5ebfaa56f593fcfcb2e8b945a417339a6378f4b137fc0a65e15e8dd4f53d881d863fb651123e45660380dec6cb17566d0cedee9f461b3ee8c30c6b37529d3af1da9e690c1bba680b254b566e4dacd16956cdc92905896f35b9190e2c98d6dbbc9160822f1985cd4ab3fd3f5422d5069544b56bb200dc0352139ab11e2cfa8d4744e70cd0cf0cb6f15173e34031b51289d3de6fded679dd98ae02c3bf9f10080d88f30e9b58bec596677e9d27a1c9124c1dc85192066bbde645b18ac2bda8ea2b43f27a609d601a865307bb0e3b7b0b9865b285d241e84531fa52dc6e5139feabdbd736830ecaaf1af3de2bb13aaa1ac08f5601545113f7c2111ff01452fc7fafee03eb062b0fbb9eecad07c2ec593dbf712e7c9d6e0c1efc38ed99b9debfbf79ab2dc2f58405def6488b9e46e1e39ee6000df0fba88318b2b2e06194b3857d57baebf4bdf0e73da116056db3e9725c04fbd734c99b8ac5aaae401e29d553644de7a8887f969139582cd8c800c424b5cb9da17016aa92557be1753dc2f3966dff52a6d90d9fd4f2c135fcfc9bb2494288dd9884507913d119dfcb78fdafe00d94c45cd9fe19a04ab3c2c8682babad92800f4b0a4a31efa70a4ee010dda3d4c41560bf934dbc1d43db8f82f8d23506d8e9eaa1ace2a6922949478674591cf40032bb63ca474a8fe3dcba9f99d2cbabe770483c97ed6cf60959aa95e20a39d305935eac2cd3b51e1c29052b09930db8da592dc805b6ebcdd45127c2a60253e830d54953ccef815e637083036a807d91610951c3d78e31735d2772579ad6a8d2369e12cd63e7cac10c951785ac8c60444c17aebd796c920bc4d8bd9069b9215e1d0096612b4eb3039eef1861939b60ae915c22fc43d73d22f2a4ad01cccfc01ba7985554e2092d49b0336af8c18de0a55049106030632411495ac824315c1b32c7b810d18ef01e68000005455bbaaaaaaaaaaaaaaaab61ab28b4bbd782ef679b98eb2f192d43c252f42a5255ba694524a52ca4c053b053b05ef47eabdffdebbcc523bb24ac964bd325fb04cd9976953722c7bffe0fbc7587ce167d6f3eb59f66c7bd23df79e804f191b3036dced6ed7e59c0d4a136744db9115c9c96aca9c51351a37f73b870f8f2fdce469469b484da7a65573daac36d38a4f3e40deafaf7224f4fcae5a04b2e530fb20991d0a16946acf8af167be6eee7d938dab3d08bc7664ed2b0131d874d2404b70789ebdf7ee6e77bb2ef79b7318d7bf300a5d3bb2ee79c0174506acf24b526294cba9be7befdcefedeeaebe39df5f96a5766495cafa2a6129db5badd837df7cf7de7ddf7ef3edeeae38aecde1f7855dda5ce85cf65c005d6478cdf776a3f3bdf7de7b62dc1e9b1e126f0f10dc53f4bbb2078bc8b7555393a8f5f7ab978a70f202b8bd17fb06b7efce2418f7dc3d214ddc5dee6e77bbbdf3de7be7bbf7cee4b67fa7d98eac4e1a284fb46407497311b9bb77fd65ef05f04546098c0afc7befbef3adb515d39bdb6c4756278d5bb202fd2b5e8a4b87b2b8f480d3bfaff7f0eb617f22655a19e18227de60b835b5818b57b5064b84450b2fdc4403a71be9d02deec707442db0a006ad720285f0877684e334e70525798be619184dba546e7a33c72303a3c9d66b343170412461f5505630309af415cd8d15d317a4418d3b0dcf0546930d37d882b65d8adb806129a95e2295c582b70a5d01abc6a382b18d11bc15520a52f4a9130a5ccca22758b2f2a830956602a6f7b743d71da31bdb2b814d07089ae452f9bf128c870446933d1d1cc1d2890f10ac4111ac0d866089c7784ce104c1075c3b1a0f2cd5707c3c353b2067c501a34927ca3dc368361037a881a50eaf87f6ab4f31cd400a3314d237249a1e3bb226412b181de603f6f89a555e50511f36902c974b123828068c269da3215d000dcf027e6fc70b5f5580772705f42eb33289eb789f90260063430e5a0257c8c59865458e1776728a874aea5402ed7683a6f02a7111df0d26bc08184dbafeaa4280458b07052937be70498abe07184dfcce5e2a5e315e360e805b966ab5014a4b1cc52a2889b133c068d2dfb6b8050c7ffbbfb2ac59e654c09bb1ec8a2eccbbe806b70e9a00295b93b8e1932c9724256c5504184db6b186697700a3c9e6354c3ce85289e7487ce12803a8a515c02d8d004a351c432c9e13b87806d0a6eca90943ef2a7578dd23fcbe7055dc888a3ee51f7a97cfc32795ca273c570046933e0c730ac0cfe3bf4bad00e09254febffb4f1a4dba3b87a76185e7547d222d5a681b3d96bef726391b2cc168d2934be5ff4ab1e9379a6cb73a188236dcaba772cab0fbc7306d22bff40b5b0d5e68321bdc3863e93a462ad684caaece98bc094563282e4b092e2ea9fc7f979c5959309af4adaab10c8f842b625e7d09d314de49e5ff5fc9953682d1a48759b952aea80ddff049964b9230bc3cb26ece41b2788e502f0c85f7b18164b9dc60530fa349f771d33cb29aec97ddf049964b12b3be57b74539a45a51af35b8e1d38c7056451431e7ecea4bae90887e529f344fe77455200df772e7bab276646d2ba9a383c79a9e73eec2a57d49513e9c6a766956d525a9fc25599bc7f00d9f64b924f91acc23abef5b726f729cfc7bce4be86836e73c862fd79cd81c33fffad77b1fb2d681448a66a01a2f183531546d588822516c90b0faa1b80b7b3b1eb010928597b37b20566f1425612f6658a1280d1e3c82948096e019a1eee38857e1b7681b1fab278a23c203d1ebf278e1a0638a8f8e12a1c18f22e2ebe401e4394a92783a708c1b254048afccd05862828623b7587a2a6e91c33f563fd38d199d88543552a38a884314d5e28ba16b5425ee751f8d1bba71f4a65b35f292134e9ec596bb4b50443b945117414f188fa03dc521241fe41463fc19cc23ab0711529e20ae3978034d3e5c54384d2abe6a5858a4c488c23838c5001ce154565d72fa97aa538ca430342eb0271899c1ba1d3941270318355d88bdd5d13872fd4d099dddecbb6ea97d64550e28c4daa0d0979f2042ee740545f1f2d3a3a3f7f0bf31fb61387f3dc57c9d4a3d3de5dda0e4d531b9988d4ed445961c0a31c44bc8e3077a332c75423c6f2a4cee812c30ea168764d4b5d5f782c1a9b2f7f235e5a12a8183d4369315a8d432558cd72d3e1a2e1817158e89ac4a6204558fabedc912e71623e765020514524861fa33c52a12e4034113273402016f76d5ba2a0f0ed18f895677cd631473ae779b79df7bab2da763056b288b2aec459a4f0062708c0247a07869dbda8cd56a8be9f8b0f181aa089d68c63c9d4d1eb6087fc8da5eb4f42bdf6a2f527c7d56d1c5a1c74351184c891f21251d644db93449094960e5dde6368bad2ae62e90ba7022c3ebfa504d496a12449991b31382df78a5b21d2d3591dd4397fb13ad1eeaad57d7e7813872136d7ebd779352aa63de17d01a9ad409a11a47364230670baf352366e7dbac45fa162bee794447961382510f587e21a01c823ce855a95437625f577930c4a4928d57ea573426c62bf50b8a57233e492e3be1320fb5eb1110735704d92a7fab3c81e1ecbbe6e17beae98db44dc87131c4c28f886821d5827fcfd49545f8f51e67afba275514b1d183f3e3df4f87bf86451fdf183104e93f0ccfb068f6b9b9198af0eb7d4cde0717fc7bbf5e01e93f0cbbe005ed7580b02c77488125fc10e93f0cb32876d216278cba08bfded7a813fb8372f387e14c836ece05ff9e6f2de568952c9fc107c8bfe75baf7c457a91ceb7e654f438df1ace83894f92cbcfd75bdd6a7dbdecbdf75575b3e0184feeb2f44ab4c900a985d5789209ab3e51ec9d2459e6ef3fe255cb4b70b39dcd5a5bc5fac33bb1d6f22ac2d0f5216e87a7ed4be70553abb5148105a640c1d0eaa2110fe355ab0bd0480f7795fe3eb7c0f9506da3ab5b629ebc55719e3b530ad429ece0d4a081ea59c3a90688182b552a1410bab05b9146c35524c4d79deb62fd9149a7b8e4ab35d5492dd9c41777c42bd569aa527c616492ddae6e87dcd764770fa73c2f869879bbdb7be79b01ab55916da7c268c045a353134c5e513967726f856dcc93e551080f71fe3effb62b8433843366bfee435d27d15ac8f6f77d75c191670c8f5f745f9cbab7f6ee191af14caea8a92164576749318fd7abb3954225be979efc46a9a8bf7712a92054b43ec62bf5a6afed5dd79da5e2ad6fbc008007258332f82ca2d16a0015152867088e4a5475a3a4b5065eb5a6e5bcf68db15535a99d9c2bf52ae3556a47147bc4abd40e1a9f68340d3d5ea9373af146bc526f6892f4f74971961a334aa9301e8ff0d30ba9232f7dd7bff6b66db219ea6ca4e03ce79ccf56788a3398385f99e9c53b5eafccec028b39efd66c9b7394161a60574a60dc7b5fc1ec80b09844988511323b09514f33bc9c24a477cccedb12bcd65ae7228678ed4a298c3be2f5ca6c2ba374e8efdf8e387f5fe4f0e0d59b9b9991daa00acfd9ab419e7818af57652e58b685a212d5c8832a30a2b6f8a0bcaaab5fdc0729b9243f36a9ab41c0b416c620352efa9890136fc4eb95d9d3095bf11baf57656f7a65596d8078bd227c117bc4eb95194d6ed939cd3647f0974a25c9ff6e911341b61f02ad7344d71d76a1f56dd7ddee765bbe416c1c93aff2ff1d98a2535970d7142b1d1f221f7edfe570bb7e73ad5f7878245be85af65a005b641c87dcefbdef8a1d2f3c03345334b334233593f5d177ef9dadf8265fe5ff4a1512edc80a5454a2b2e4a15fbfb9d65d1d1cc7171e41d78eac7b8025236474f8cdab881b5f59586d47d6341ff7320bf1e045198459527f72424c2eb7123435d67626c386d6ced9f968590df18dc55cb5731a809262cc10b17011c5c22ac4015ff3cdb085f8216fbedf42a35c170bb3fd4265fc3e219ac262b0750011821eb22533bb9f102af80b1da7c1b22b7c8dcc1b169c53e05f04988cd6a62a2b5c74d82c9d2c9decb6c651f13a879f2a2ebdb0a73f1924789045c8728555c89276d3420423075d6badf5d118af5a285afe5ef4b4817e39806b5bba15354d2c48516a562d5c9c8b2053a289222da926ee086b88d7ae9696d8c72b965c5c7c72d4807cbbedadd65a6bad8f5a7fe4d75aebef7b35ce5fc359450b967dab41ea3fbd07d6f4549d08fcfafac9125cf183a6a00576dedb9a141b5935259c52d07b18af5dcfadcdb5f60076d4108049725bbdb5d6ad9f7a388a3fe2750ab393f613f6d282cf8ac5d9ba71204a73799971b696b487d7e73610dee30ab37b7fb18075a80f4cd6cf7dd082a107629113765d0990d7aa0b588839ecac70f12a72923f1038c4f967110bb44a0ef1048adef69fac594378db78fd722311134f7e16c927f9af1dd0222743b8408155c7415041e9d08cd755b9fe16031ce2753d81436b34c1c1faf31a67a98f572f1a5121bbbab6b5388d371a52ec028b9e6522c8f6983e606e36857c472c320b210749cbb29759e038bb3babb5d65aebdff9e69bb3bd916dbcad7073e4a27335343b23f2367ea63231045fa251786fadb5d65a6bbdb58f57ad379a2962fe9c732ec2ffdec575fd16b4fffc9f681cf022ffdc6f20d7a851a346ccf2b66dd93666599dd90adc855cfdeedb95ebb65d73c137fce56db170a25bb961a615fef89c50c19a9601b2cabc989bbdbfdf9b6ffdb22227ca657e9b917dd73a3c18f8f71f864700aa20fd48117ebd87d685f0ef99ba12e93f0cdf6c65e0df4b15458af0ebfd879f24ff7e8c1882f41f86655eb85870733314e1d7fb97b811febd5faf80f41f865bb032f0ef3b408af0ebbdca5f8811fe7dd3af30869fd7a0df91df8f05268e0037fbae770031206d133e0c934843f2effd48117ebd0ff114c2bf67ea4aa4ff300c020844aa2852845fef9f82cd24ff7e3afc48ff6198e64d847f3f460c29c2aff71eb22cf0efdddc0c48ff611866c408ffdeaf5728c2aff7ce401610fe7d0708d27f185e711be1df3f3d8845f8f53e45c984127e88fc309cd90e5b306c62cef17aef5719807506e0dfaf84805c48c0a90c61c43599ff090a6b2ec1b7236c8fd7adc19aa1b498c8516c17b00338c601eb56a7564e48511e96867506e1fd25f4c0e80195159ea543b733c564f6c6dc222b10203a11a337349745c4d4226bdc1a4ba0657fe213e5c96a75b2a69dac7d42f109eb8cae26b2482b72c23663f69fb0d098fdec7f59f67bf6c3ec5fb2df02ec7701fb83c07e9686fd6c94fdac15cba6c512688bd072c85030bb457e1886d596deb8900d4a66a4a746b4b499065105a96f59502a9d2dbede7b9fad9f66f4358d0a4513f39a231079641df6a8e698fc86049451918c9fbea7ba6ed5b647aedf687dfe841754347e6a4680b5313b5f8d3e943a9da09728b3f3203717e1d77b9de4653fb9457e18d61a7ae73baf6db367c50f7f627060516d53400098f180d37c5a42eb295ce1a47b020c177bdc0c6538eb155920198b2221dbc3aeb127d6d6fc995906b3e896a0115d528914990e28404a80722fcc669827cbcd573869adf5bb21c3030b5ed065571f65bab74887a074fe5e3899da46adca274caa89c4d7009a121f43b0039c1cd3181c0f6d8cd7294c0c2f43d6bf1ff3a1090cc123c22e068fac4d12acf25006f7fc19ebdf8b8d114104544a1cd21a0994a4a638206a07a39523bea826b81f0f8433622100926311535557f6550511011a0f2e025e18a4c8a80b4a164c90f0038f18e15961040cc88894638c8f171e7a1d732f1b07d98697515a8c222af961915bc2eb741c4eb35df409f052a914819291ca0fc3ca18bd3d61a9546add0276b6d8b66d2bb26e58b62d090a1bdab60db117b7edffed276945da0d7a67ac8942478ca629d5224a636608699ac6f5d65cd3415ffe7eed6359f6c337d07d23433c12facdb0897a0500151909cd1db9713137d78a8722353423a42e1b1a1ba709bea4bf0f11e264c93af3f79eccab2a079fef968e9d395f5f27cad647f87d0787f97de1231c44446cfdbaeb36b921f1dad532d652f5b7f4f9ecab4300f9e52300db75c5083b005cb13b8fa45e51d03a7365ce878623d586c8bb342ce1f8e1f7fdc59f7c95ffbf673b21c70fbfef1f6648becaffdfb3198ee1f8e1f7fd671d3690c957f9ff6b1648bc70fcf0fb9ac91d2db8ae59d80709ae99b408bbae19d3c608ae6b56e2ade0ba9d67b1390df2ee0868adf5f799a3158f5c1f4faf87898d8e9de23b35e41003e16e88352b18f58a6a1ec42e8e2113885b268464f4d5208eee72331a906da652d46e981a0979f31d7605bbee9df1873d69bdf599db5f2adf64a8a10c77dc46f894b9cc08f422804c54204e0d3ebcbbb4bd31c156233f0cc35d73de761773ce798cb6340e27b6152f1a96aa2fb8e7d90d0c9a16e0955bcb1672716068fbf001eb0ea9461d9353f588f840e41775a1d306c5c3b35dcf7bfec8acb54e12a2c56148e9ebeac00394633480aab3a60b94f0886bf3b49a387f64ce7f308d28001c82faa4a8a25aa015c58c8089c05bb17445458ae919e93f0c871c6dcce0688bf0ebbd20d988e30ac2a8e78ab30957c4cc3e414e2a9f200d2f66e69b0bf29ce1dc73ce3939f6422b933e3e492e8b6e7af3404f0d43ca5754a8b483cd99b38d995fd0d662a6058e10081accbc37cd39e7b5b9d8ca4ce7be7bce5defac838645f6e59c738e8461add439e9d99e9c53d764c43d3e492e7d0ca2a7ee2177ff0a8dd9e5fce51d22701639c27f781e47e25f5b9148fc2bf3d43f127f53eef2f91663bd3e7614b8cf482ad9d45de13340b22923a22be4cb5ca22f0fe65b88344be19e7fd2f9a7580a9bc3f8701a31a190a77a824cae30a60430b524a40f34bc3a001276d0bc5c7023b29b73eb7a4b910279990dd16c4d916d20eb98a103f1ed0166b238ae46d0df83c4eb74e685c592bb6c4e912416a37c86c4a810eed0e71e1563745fdff70ba83b8f268b0cbc40e9d91a11531b539e726ac498d86a983a430dbd5ef95f1318870d8f0e0d1263342ca632a84baaae60528919cb7b33ed7ee47e4d59c31da5c01a3bad20ba18bd2940b335a94b8b8c5de2e5224d2288fbc3effb5d3af6de5d6b6a530ad75aebdcf6c54c7bc4ecd3a170ca060af563eab50f2750c8ccd050cbbc9ae32a3f9360716fada950155a6f6d8c572a93964760cbc762e6599efa67a8c4b32c08b0ee6e2de38a01957192e187e7b5d65a7bcd75313e79f51b07749262a4c030cb116b5d8845c19145d95cb890e520033ba6e3593fb7be3e39efbaa146a500b31e34cf9c64678e78edc291115bfd0ae043f6de9b1a731363d48263c970428daa523e3c4dd07507e676331433b5c2f52dad189dd57006ee31a19c35282c8e70a85abd0bbcc85105a17af2866ab0047627a716f424924bebb1113c0a03ac6a5908608698f40d87691089657726bec410375eb3be6088f26f17f08190a7bf7701e01a89a666918aaabc385387a858a89c29f23ec84e002d0525e965e10da5a9504a61a2a6472585fdabbfe33018a2116342a96fa54f0a58e7330d6a72f1c5ae30363b59722d602aa25a3b12ac44dfe43c3c8bb26a84b0acb4a7c9db5c7cbdf775801c787af99b8a61820a87f9636248056187aabcadcd5a3abc9637a864ac9df90a0d4c6e50003319000003301c077224489228846d0014800a23e85484a460482218c8827140180ac318068128868128866120904228ca83ce491eb753ffbdedaf0dfdbeb44f68ef77a4fd227bdf959a4f680b9f97f40becfd9e645f6ceff3a57da2addf93f40bd8fbbe3449481bdafaa56efbeeb67f65d0ef49fa425b9faf52c91084b47139569b08dba7ffbf06a3fa1ed75e9c445b5e550f4c821c062fe0209fa7beb99fcf23da83eb379eb5486ef667b010bf3268d4cc1082c65701362824b8c06e1632a3f16f8339ccf9b28f1b9a058f16943626a07da7ae0ac127be325b2f15a12f974f4c9df6a2e63be6b2089642d8697f9864364abf00a3d6ffe191d9d822aec92b9a6ff2c4c2758bf72d780d585a9b81837c6c47c77d87924391c131031be19a6e4a0e5c09a0a6b72ba3f831ad12f38b666b41f406cb88f5ba85d2690c58d54133ce8e4a88aaac56f4e7b6860e7f27744b08999a03c7981fb7ef0e36aa27b80df298f0504d2c50240d63521f00a3e343bff3ea2bdffb148f1919926240ee94bda7a07d6f3c825235845a220a20ab1cd88807982006597d887a881ec7497dee857bd01d608512448cf97d64e8194c7030785b7717f4f4c84c63b4aff136cfa68d26b698ca3850b1cfe65808dde5342799c7e1db7d4ef5a0d046fb627dec34c657d941fd3b8631ae4b6ed9c1af5c24156a38fd81484ddc9cb9e758c57abd1c7cc2e9360761f9c036014091fcc7fbdcd3118b96609ebe84843b6d82ae8c94dd1ab58e67c9f6449de5c26103e0adef6a482a5298ca6186eaf79345e1ef93cea811a147fee101c0b6e855a743a32151b19bca504eeeb3972ec33c68a546ea10f8ea2e8eb0933eef99f8257cb1cb42e80732f0672f79e1d74b141c6c6378e3175f1068c48404adaca11c297eac92812bcce42dbaff48e64a255b7b57ee45e84e8b9dfe86c0ea5d0e86dfb0b6d9a007182cca333990065146950453d171c26a0156d8163d73c2c484f59c8287a1098fae6a8f8e8762e953aff87e4baa0331d640555b9824897b2f9a7b93ff5b79805aba3b60f7734dfb5c75cd7d8b7facbc83e0d758a9b5b1b02da4ce98db4e543ec2710fa931669bb12e376ccaf103bc3fb0e97c386e6168fa7ee5e24593c6256162b1842787a4735c0954c7e0de3b44969f55d4c5382bfcbd9d9768feff952ebeab17b8eefd984625cb38f17615ba74ff4c9db215d2acc3d1685a688577d6773451ed34911eb6494e47d7a9055529c57a1b8da68d56d864ddc072800cd7bb5168c3741640808f417a0cd3f16415f6eaf834f71a3dacd63869e5610143d2dd72a4c866082ad7b5542262c0814856cb05249e5824f5806c9abcbe8e67ded5dbcd07464b81c079637d642e1a17823ed2c1dfd7e8705d5f6ac79a20da087616ddfbe9ca9af774a7f6e937d2d48624ee73602f8b0d582101cbc286154207a5bb5b564bf2c12863421c9f5cb25ceeb6ac176521b9a58274860d8a4d28fcd3ac433f2d58a2928c4c85fa45817ccf3e1af3addb1b5c83e16b7655e75c73cfaa8da1864f19efc5e500bf96b8829d5d5e809b2dc838ca126ab8fb671ec1633e2a071642afe1b348bb373ac33098d48a060d01b3c5955fda375b04314e01d7d3dd2b00df1ba4390e96c1c8ba2d63a5fc9846c001f43f6021e3b9446c7f18c9d1e4eeb0f2f7aa1a11ba446e63b617f786ded0030161d06b8269d337103aee063443efd6737ccbbfd3b803e6171598c5b2d7ca73a0a26aa998b9cd354e58560bf2786941e9b9aeb5d2dc051ea87800aa22f05c4937b590f6a621a11cd2680ea1e9bb1aa13ff748cdf5421ebbb20bf0de35f8c213937232d902197d7ee4ad4e4bc5ddcc3cd6638d389f345b48ff447118f08de7bd9786cb394ae148c7a7f0d830c874d013d568b7ee69332998ef7d814722a2afeb46bf3ec331e074b29fd4eeb75b57ce0d52633b1ee3e2b2b0c44687a0143697f1563290e4d4dffb9a807223f7bea6c4e6328fc31bd4d56ce52a527c024ef45e280c4fe698668ac89a6a7099c5e4ec8f4665b06df2325f7b7a3b0a81c48a2b249e86ea9768689f1a64ebef247873a2f1b9393db96a7e0b4b251ae39b16e56bd42733db6a23605594313a7fb3cce386ca087acab7b4b59d01a34b2811261f6da1bf1b95468c9dc864d029ce3c50d545948e455a1b601a4d89057d9be18659659d1ded2086ba31b9c9fab60d48869ba57d34b9876a97fef29c7eea38a1a7aaf6ca69ec53d67d388fe927a327d95750a7241fea86f6c2b1ba3389b8c58ee1ad4d7fb979ad0149bba27781ad85811ca02228d29545f6edb4162bdc80a34c5b9f022d64d95567a9f7cecb058b68d208a015465ac179c854a90642b805640a27281516eeda1b523b6ce7e70201b4d407a868ec9446a877b564e3f2cd04577fd5f5125fb46c0210a5ab66d0130f15659e11adc8f4779c53f720e224899c1e49b8959b3a0271c21b11b95b4a7cad7c5e44bdbca002bcf8021a9ae14f493f529341a54fd74f9910690f851fbb33c245d3a9d6d9d21a42aec3ddc8d7f43b7d3a0991efb78200e99665e776ebf66c28f4fa3af7f1c6db7494db95a1286e62887e6b919d216fdab6bda82e653be3daf15c55ca2ad1753149b4e361357736873cb5f533d5942a934046b91dcc7c7666a1c0ad5edb17994cdbc7b624ccbc0d477f9eaf4be5ba32191c166888c626a243bdc1f190226874eefa25291b63ebe517274d8fe904f3b36ed07accf89b320ce7b0b2027e36515fcce3beb9057eb177f1a76f4e657794ace520e1b507c142b6bf100c7684cef073115548b474d03006e6affb6e61691c33f5c2c5377867a29530cfb07fd811b136af9d2ebaafa5be3e850f4c89615fcb830a7399bae26e0ba7bc004380b80f235623a343f8f0c221147ee8d4cf9f890ebabbe1459b09e28c1b57e5787ad98b08c4a681f88b765de6d334cdaef9ce6d4aa91a1db28a572ec84a1c71976592fefa03499fc24fa44bc7fb87f44d85fee08a49884f98b1f08d8363abc24141301d0a035e4f7918895cc5fbb5e942a92c85f9b7c9bb9432988c56b1454742d5569336653426b7b04c5a06134eff477c1a27662d3fb5c73cefcadc1f8a8d059978313b62280f5dbddbbf2f312852af895989a8b222d32f151cc9d6010ea24c43a1f960d4e46b1ee792e4af7cb183893988c6dee337e1bb8083e5131c304439ea7c2fe6772680be117fe28859244c8753bedae481f40a76be71241885f12b022154540f232559bcc0d811714635b0983b468269fc296f570b871b6ed5c1edaa7f9bd560e871918f612cdc9c866680b735f5c31ee6732821567fa233c44b172964ac9ae25742e1ab90d08276c4949b29a0e2db6d2c3f88a2684c2fd42407cf883cef91f2e0de921c5c1a46f33ae88260c389c4c4bdc0e33b2bf97150ca1b574548681a721d6af4a3dbc07cad61102b3a408432113d1b4eb864d2aa511062cc575ab462af4e1d89317441acd8fa02be790944a134a533c5808a20deef82d65d4ab75415cbeba541460b22135b1ce2e1485e952aca5f48b76122f28806f2222f1ec80a551d4777386d9940edc39547ad5c4141ac253bd85307069955334510cd8acbcc4770443da1d32f0b9afdd75a636cb4a8c373f82e171d7cefa3a37bc335d05c9a004408eaeca347f72a198c86bf7e1099beac64223a4efb68a2b0c17aa192f10f917cae095d553ee6c42d6ac11dda156c79afeba4232a822dd6dd61c17b7639ae946565278fa4c1be428705b7544711d4cc19e6ac76c8204f1ee696915d398f08576bcdaec257a81f8b75bc0bbd2c574721f7fcd8b70cf29a0941c1897f92921bce4d3b4e72451fd5ac1a84e2ad77cc494504419480df0a3fbc99f06ac9fc4a2f2fb1f947b5941f8b62a40c911ba435b79ae7391b962a13d1130c3f1718bf1efadc7edeb45d24f6d9b24c26ec176b07ae9c29ea2a959a16d6f4841b04f2f2f4ac71bf0e40c2ae307b13e20387b24ddc53a7eafa478b1556c0c0520abe8819f66c066484e688aab1be9baedc05476e83a7cec9dd2e1ff1aa32a6f327b5404838b1fc1fecbdd7a49481a184d45bcbea1fc01f19bb6fa329b8e8701ee63eac22a3bf53f7b9a7bc867fd528349350d0f540ccdf2277a99593250b199c352e7edf52677a6ab0085060e7fb8e9234e8af938cec00c2b684cff955e2ecfe049f180b64560ecb97fb15f4709607399371eaf61f7d149e519361e4237525472ae71cc510937ece9f0fb4ee1b2eb9f33e403a4ea3a940db48da307e4e8e6b189ffcc50add28689ea62f3697966d82402eed47bca26e71bf88779f6f28ba3f43b6e8ade2d7dcc3d0d2311c3b2b487e6f2748c1ee16cfdae4c80f3f28d34fdc09aee4bc277cfc7c8d3b59313a8959e8886a589c78f2706ebc77b77ed5c27be9d0b0150300c9a26372d6c401bbdd701048989af139844be5531fca9da2ed72021897395b9b7a45bbd1ee52af3e5f44d5947b115dd31b7dc5f858c726eb52a07f980e76b14290399cd3d12f87d22a14381db3e1f2eaa51b8a2d2f50b12958a84823658c88308bce71e09483756857b48b00d9e791ae491d7bac632236064e348b1bec516b0b5b4f5759293bd3704609c575793947b97f405d765601150e458084b152a12cdccc4af995a8069d5260830de1b56566d35803fee06df4d06d4617f2a6c244e8e82aa68bd0ad11ec4eccd55b18773c7be802d5b3302b7408396d70931be2fb59e3e84e8090c4554432ceca1e7744f40fcab06a1a796b495ec8f51602bda5bc19d5cdaebe9c8e40ef774b1f40f58fabc9fe639b33a149444125193b94916409e4c9c11b4c0d736bcf2e140a2a7df657fdea490888b934403cdf965cba074856fe85815eaf097f7d4a334f4d1a1ccfeccf4551c6c8d48dd15f28880dd754db78fe5a3f61a7792378a382079b3babfaa1cf70a3127e52c67645ddf201365568d6f120c864d91dd67b9c72895f3eef119fe5a4570cc5f107b8c878c17983ede3801f42331b87115a5f05da021ae5b1c964746e944579a9e81b3f8339f537a5f4d16846f652e8ce41469a76ec0e70a7a2cbd38230c924aafd8c827774ae778d8e44e358efebd9b2097b173dfd3c71cfb88e78652d1d8e1fe96607082d703e3de65ce31f3ed1fcb287d1891942aebde1597a3e5217e89dbab8ebb9eb5f6e81ef0ad1770a34ba57aa072d628b202cf267f9513f77101d3edfa69bab2aa42a3175cd012a8c76bb85eeb42269a139b8910bf3ff8a3ca4744435a4da8224d79b148ed343978389e21619964643d2bea4f361be9e927a81dff0882fd8878aee1eeb6cde8a75c9f04a3fc86a6dac16667741fd86fb091050f56c4cdce6139514fa724b89c9af3ea62d9f00bd0ffc2be3d598a5c1108eab8a021a78140faa5665acf1a9c600546864e0687b000f3b03b93a5ded06f2ea936079b5427085e1e7836171e806392ff0cf0eea089e17f89ed415f7ea3ef44c9c81ab35c5c35e8984c33c4d6f2dcdef1fb8641699e5360fa96379f17da6b0b62a3563c2135677becb814cbcd1e3a262c3d1a12d78c8dac277832b8509e724027e8bcbc9125d9ba35aaa74a677507749961300719943420b907f90f3920c68a8cec04b028513eb1fa24c3eaa0d7f6d3f8e89127e85aa1b3f103b61895a8672a336242b52e11c445be3a0abc4317e59509676a65d400e9724c06c8a0a6737283c63b036f7d5b121acab27f9890abf2c5b0ee58a1c0f8c548bd6c6af227a081dd1b5bf40c14e542fef1b35a20d87a2c97e47e8213f74224235c50bce70a8c5126aad823e35f82bff4050e3505ff48e447f9a6ffd8f0ae7820a9f01138a11e6169f819646e863aeb9dee7fc4869436635747e0382fcc9209486cf89445d13d99a09e20efcad4c9edf419483c4b8f904c3cd8cb0980a2c8c721de5c5b60380837fa274d8b4b08576645fe8af30b8088f6ae55b93eda5f25b1f03859571799832311e8fcc56591d66e0dcd6886f4b21b4b7b9755bef935f0fca76fccd5e74bcd1aaa41ae1db9c5645dbaf5abbdd5d8daf0e1ad676858175ba8c8452b2c38806581fa6fe3e34f58d0b92d3471db206b2a60f8568f565b865ab5fb548a7105ca8e02f8722b387602f9bf541f859bf40d78113097a788f2448a5c120b40b8bebf34cae1fc879a00a32e2b2f7c0394e554274f5fcfa48eaaf3f8279f00f1fcf9c75114512c2895018ce7d1d219efbd2bd0eb81c248989b79c2aa90e71fd5ebb4fe1edbe81ac20dd2845077e48826aad3a44bbbdc98fc29dfc03ba7595a323eb420d7648020a6cab116dc8b5fce8bfe54f5f963bc816d6fe8753aefd40c3a2cea34ceb1fd1d0d65f6656d40373d5cebafee5b46e4daa0bd528c6faccaa15bcc7168538e7e1550c1cd3e7f1e4bd3eaf768e03e90e5e064b3a062921609905f4233da0378375787fdd58325926e84d0491b986205e7ad0431884be98d7411318597f8f2448a5c120b477869e677a0dbdfcce8336e992fe1c41a0425215e2dadd44cff8277af3330f4de290f43f2201b5b42ea25dfb458fe730fac11d076ad048d7bd078e030609a10b66464f021a7d08d6c13fcc7106569b28b9d5fa506c03d263b120fdd8f7811030bf76dd85e3480b2142076656cf0db4fa10cd03b6d78fb5b358a1de4590d29608e2f45f3d8406d6b7342b430171acea696ec81abd55100dabd73333ca5eef5f9724d8bfa83d504b65206bf70ae2723aae663ad1960a49953de1eb65a20f5d5fbedeca51d4c53a461bacb24baba932a59d657f49f8f688d3b7dfee0575f98ab010da5dd34d807b195a54dae3b67787a73f654cb31785215992f71c6f63e733f450294e4333074bed599b9ea9bdd5e2855326ce4c4fe375432a11c82cfbf0c5ce20d6a2ddbf36bdb4ffc5e9658da7e4883e8b4753c5e3187cec231223eaf8787dcdb15e882262e5b195afae6539d508def12fdbc7574af91046db1c1d98731dda307e39c739be99a728df1b41366a3cd3fbf93e42babcf1c7a1e3263e7f8f45c1936135375bde79d057cf73f7741fd43c3d33320bb84ee91567651d47b5e89159e52b42067ab8dc992c3bface234113376fe49179d8c09cacd15a1ebf619a3b5fa8073f41d41ba511f56838d4f45233223d28ad875ed2c4cf0380249db7035fcd2307589817d5571ed6b1794aee0fb06ac4a4f4a6a0c5b5091422a1a3a0a2f95bae7a1fa44b10a8956bf3151811912ca0d8e049a458f957bce4135175cc0befd3d753f64c2dd2a424c6d0ebea817222a8627c1766afa5e9524851a53b0bfba83731c40abf07c1a727079393999e1a820ec8a8879b100086c66cd569f9f01b462e7908da42538fc448c6fd9ff615c86a1e720b92e06fd9d0a8023f506ce01c886204102cb38b6e4195ca51dfa940270cea3ef62a36b5985a20d950456baf319cc0d5b1b8fc5da2acbd662ffae3010ae51a786251cd4f1906b7b10058865922ebcd06f020132308d6415673fa479a2066f2571b5fc875dc37af76d1d36c9e02158646743e2046e07b0a45cdd950dedd98c3570915f75e01afac99637b3ac5f9bf3a3a378aed14a2bbf5e1a554f70c8c32b2ba4585c1261a3ca1885ed63484b8e8581ea863b2441598d72340d65e0d6e717aaf8eaa30b211aca4438c28fe186715655d7097f2394ba2b055a3387ac9093ef5e882c3e50b2bf60eacf7ed86ef67d9aa9a61ec183d55f01ae2a26c29b515c65a8f2805788f2c7c4c4b52db0f445b5d99e55187a73cb966523347aea3b40b4edb9a2db911042d4a3d99489b270dac4ac19090d457ccc4ad5a7236158b49d3332f354ad8bdbf62602e7a276b8edf529b5d6eb01f10e88dc6cda052ae37129085aab6408d9f02133799557fc4478a19fec23c2ea3e24150a793bfe717feb06ff2af9b5493c1042ef90ece614821490184fd74a6bd238a8d5d46c75d012da7e3088b2471663d96ae65563281f581c6d2d04d67e09df80e8740b115c66ee111aedbfc168e1da1779d62abf1c85a66695c8453a047284e96868456b0df340f00b5faa97ef56f3d2c2ae9cd3e9bb752bfe96e412f32d97e976132f657f82e86143ad4d3944720199ecd9b8dde2c4833c2fd97c230d3fba2ccde13242963ff6e3e95bfba52f7df1bb8e30ba7ba1199d177cd49febd8b6bf1462ff1e3b1b179c70a3bc26662531ce33879a82ba1544f640bd50e3e0f72a2828cff5974d75be7f2d818953d97c07ada9d44cac80763f873e9adbeb9c9b28029e7c90caf005324a3b975d64f67da6849c655f172b1784bef80e5ae1c44650323a56bfa32237f1ca58db6b14f6f40291116434601d81013f2ccb738e90d277f2e82bc3138f0d82c3b3b4fbb4d80f1f1bac663f042905b92c13013c65fcfb9fe04e2e7a606a15775c88b543950af428e13e3c9392356ae9caa97bf77fc15122773de0ef66f492e5ca15c50ed8c3d06cc6055018bc2193362d6fbdd595e5e77768a4e012f63d9797561687931130695ad4ad75215fef4c6076921641624591a6d742ad328da281ee49baef3f046962328a40c7d86c7fe65af2c49f9a9561906de66bb24247442a87d774c9147c77efe11ea9bf3d5b2512a4923026e766d438ec317d29b9d0c45222440214dcfe9c20225aaad50801aa8701877fd9072bb440b5f28f0e06473b27b6eb6a073a7dd6e00e881c22585a01c95908cae051428c44c4cc91dfac6696de9982d4c56757b4c419995953098e55f6034d585895c0c86bc583303629b66c6f4430c64d100629beac6bcd658de0b56fa372d47d26b5d7ffd829e3b0d44ce68f70daef4ba4160580ec5062894522e7a3b19cbce29cd702db65e906bb287072321348f088291253444e0a4b25f5c011bcf4a1b194d24b324033d94fe0836b24a944786b0f1c09eaec21af4268a1c33fb11188c420e92dd0eb491d81f52e3dbf620c959c79a2df0f6a074cc376392733610fe21c798b05e448967542f228b510027b9dcaf074de244dd19874065c44a494ceb0da3925bb30fe7981d8151b7fbca099b8e9898d8c4461947241395911e267a205811b8009f94816920b40127e9fc9804ebd3538512fd1b118d968b681f9cc3c333b32aa29063823702b35a906b591c66016f21c242828333e06bc3c3e858cab00cebbac1114a3a4132245e5a6e42504f77c101f0928990854c105672062ae70befe0dbd77b505c4613245a0d7e2e6971d0ddff62ed3cf293e3197e48c8fe2ccbb3ecc9bf1d906d92012825cedf5cb52c9bedab099a4a2eb9bb4926e9e6c619008ffb242de727f8d46c3198593ebdd1c03a69d94bc0907843d97fd6b5421f00b36cc3e51eb540d8df4288b9cd2ac991e3486fe61560fbe148d28f59ff1e71dc1d37fee069e8a453ca42e7bf0f5a55fd4c95887e0f3102c898c8357a3781f81aa143aeacfb1ec2386e6a036647c0594193ac326018951f8861361886f2d27044f22762898b8fd46a799190155e097bfd0cac81a288ffff94d949761033b30995068ee705849d5256f9bf0c00fe3c131e376a66863a64e383cf782c4330cc3af4bedfe0c9ec5f14d0eba1f66d64243840534befbac00b805f7fff1b3edd52e26c99b506949697c2827a02acd55eefe33be88e2e4485ea0b12bf84a0ae2e7c8983aecdc443cfecf62f4c0a4ac3dcf22b52a0525a28ee1bfd06033a14b2fd830a5f8858ab5961f16e0eb1c75b5b55d28fd9082ea2ed2a3311c3aa7ad51d042595c14bf006343f909c1dc9f92735ff92eb44090014d184c554473f33b17aacaa482c6152d80bf451586ab0fcf075223f505f796930dea8319d19e46fafee4c85d4964e5f7dbbc440b015243ab93d3f0e6274842446d2ab0095a4680a8e0a7881166b3f6e53c3183d34e94935c4d31254cb0c12cb022d5a44f9e7c881eed92184ff858a0e170c52dc9f877dcf8bf0c667ed87ea82413920d2993cd02a88b7520950e1d5d193328ab3b2fe6c560e579a3c3933338de880a566a290e43c0eb7352e071ee5df447a38f05fc1e025059916696b2c0ce5cc8287669776ce486cd74d5e333cc1692e7072a35a4a7ef8d92db80eec6c59b28e55a3ff9bb029f235789a58eab6e078764ad06d0a254e863f761120a7684c3e74a1d84796e0c0d68ab36f32dcc1580424a07a4156ab686c95eee2a02bd3d4c50c307cd2e489992661b4666329f6035b03241f93a942b7ecc626bf6e9623d56b76ae99061c80cf006c051e637c2b0736891a51d2ec279ec5e5b92b440deab189a17084c841744961994cb8855777e8364a622b1b5ffac8b555ddccb7643ca0961bc7709554b258522d2d346f826e61e8980af9f489f920c8e106b50918e04d51de0aaac0b0e99189b6d336336f408c4d99a9e96d3621c1981e21b54d0ba01a6b7ba14c6606cc20b4c59c1d9817c8d98a23145fc9583483f9e714b755858c91c86746973ca7f811c95d11e1569a18ac14dcfc5f07119bb2a939918fbbca92404ee98e65946ac593d30748cefaece480b7cf3c1331c1ac87d691d66a6d65946d6e1290ae8c0a604d26597bd45734b905d333ad58935b0626d3924493e18c6c7ae947510da9d2897d268ba6b607cfdbd1346464ef24b39de32b32b1cb3f76c69ed25fa1e2751f105e355be5a6a2a8193e0eca9e9f9af24b186f5d0e1a1424d32198f8b19caa986e800ac59d7e6200deaed146a22ce2ea171079a24e543c793678eba5fdab5fb15c7f65417c60ef05b59d1c38fea9719624980ea3d321438caa18f3572c70a9753142ace4eb4a665faa8c3fd9d7f516727954f1577094000e5040a10c008afe1ebe147a5172c7ed129aa1334e6caa3dd822c6c3a2b22949552c05b28bf72b6133e88a5d1d4b140aecd8ac652dc24b2b5beb757a69596b556ae58c02ce5a6586afbbd044d3858a9516af303b0f34185e83a5abefbfe5f46f19b27f2fc7c763e4e4c5779e9356ee5c01ca033d490c8ac4cd1a2a74728d57d26e0ac30c07bbd8dcd45f4510243361cd7c0074731eab40a6971eb62df6fac81806d1ccb20a44b4493d790bcd9a3096f204614156813e86eee0f14e6e7752de3c96ee7676aefe1dacd535cf26923f451c63ab6b54bc9149689a3b4650d7dedb95f30b87799336754ba243f3ed590eead02597618b8a1ce5649e3849f1f28726936ea9ddbc0f3dadea95698a0b161f9ebda069a69614569ca7840eb4bca0ab54d1cf8f2a3ff461e8f5f3b9d87237623d8317ea8e0136b45cc03125f405265180caaf2d51568688b7500d72bd85382986205b0110ea61d5a4060464c188988ee57871c8e1e340a27e1776baf2f38a304b42c7fad071663bb91b1b77339400f7c247d938b8c58b94a600ac9b0258b1f49b9c83e71831df93f9b8dc03172e4635ab3aef8269f73f370a2f9dbd41c8af81f48befcdb0b955e6f8024505fa076480692280ea2bc93822247d72d8db1062580e4571320fe083197d1c7f716fff070638e83489b36456be9f6f5069c243a680fb282386032b6e25e756a9377a5d1bc903e497bec8e5befad52ffaad3c959213b490bc3f63ad2e4f2d1368b7905ab40dbfe52906c19e28328103e6e81e3775ab4b56ce80a197a4a2905ff98c518577865262d42a2dd6323bce2c1df615dee0064c0d4eb9dc156fa64ea80e179374cb8e0281883076952bf63239d64638d90800ffb491ec95c2fb06f209d69664bbf10f0ab4bf04d4a6ce887bf19092ea06565b18b48e814ff13470957dd9617c50cf02997438596c52a15579c0916557e9257074cfeac8882ff41616b5b6e3d3ee810ab1903eaf53bbb3c059a7303a55e5e3dc2b6f75f8b5d572af63eed9dfee976cfce3a1586eff5b7cf6a58eddc210802d72899dac984a101789247eaa77df4483b473c0db707991771023a64df2a7d8cbed454a2d7eb0acfdac84d6696d0564b313984c69b4d569789a0fd7a74a34158440e6db33806150afe5e28171aec000e8c2d3feafa08ced04fdb96b29222cfde67833c85f6f4340a407fb4a12d4ccc8117b59622a21ae123571b43a3897533edd8f53ea3bd483177c2d4f216b076fa6d2a02197a52a53165ec1ad169858dd710261958e72a8b5a0116ac5237e88f8fc957ed2da1cdbf4d408de6938e84046fb79433b6c38eece5d20996fa9753a0a571d21aec22f2de2b5058c2b77585cac9110169f12175259681bb90755e2486fc343f839798e170b1f6e8405d87ee254b476e47a24701b3d1a2829823e646fd383122dbdace71b26776bca7040e6f3d3bea45dd427d004a0729cc98b1623c66d1e5841bc52411d7ac205ed29a2ff0d21b1e2fe3148a4d5f437c0348bb9f7497fb6e86f54cf8da789919d46a8e3fad4ac54f3c954c0097abff02f222404c2bc7dce5949a8dd44f287cc371606536a2c78b5215201c00bac18f0cff5adf8835287f39c6b8fd69937f18df13adb43d0d004d9f22bcd526df1b9680a9cf2e4dc10d1cad07e6b383d95becec2dc790a36f2281ad47a5df64827e67e96b1131628a2c9d74ec1f6abc7e5ee569eacbf49359f2e9697ee86fb4fd096403e84c9122f497196237e32e22399d3bbbc03887d47e7408cd2dec39bb1e48e5fb50aa0069ab11e1b52c1607192ca48ebcc06d9f1dc33f6c093389d5f2424fb9464061ae7351e6058dd9b7e3ad3512a94c25f4b8a61494817fc510e9be5d5942551cc56e5a38070ff14fa4ac27dece612dd1c293e0e447cffdaf18d47f0696434204f990e8144b653513b110be33c633bd676062d10d9512865313bb71d6f2c424698707f1ca2aea956b177774829fe909e8237d1c12c97889e48559f98295133a3c647f96a050dcca9100fb32e920efa603c310e7013c72810e51c9b8c34c865039226afa8ad6e10a2d518ea2e64dbdee70f1697345cc233cd68887f47ba1cc46547d408878a2eabd01a64b7419096d25cac652d33226446ed5daff738d8a0b63919d257310619afe81cbe148c9fdc4991b0c41c26691b800633f251ce8a6767efc3eaac9c8f115fc17cb83f9a99e2cc836cd864386cec4423252e5976f70762677d38b936f8a0840110f3b49eaa625f26ad725df6ffbd58bb2d67fdcbdb88fa0e2592fadc4964295cc82aad0654de8a38d41654ac51f6c6752842a77328aab54c926f14a9247d7f61ea0d99084888253f570912436321714664bf874c2a764200c0c9fb4303815bd2044a9512e604b8137420274e1a7d6501afeba0fe58d8048b1a1ea6c258124226278e457a60d266ef13a14bd0cfa142cf481612c78a3d9197b0c394efc9c4845b5c892e01072a73eaabb3c3c99895dd23af27d07104e2b54e5e6d48fb51d3f35580b0aa8bf12ffd3a639ff75989512048d747dcade05efaf24c7ca9b43c6e8e9d59cd0fa2db57a8f92d730a37ceca0ec253db1125475fa38930f9a2de718ec2e0f1a9b6db7fb0d307ee92215ed9b1fe7aee2e8a1af43e629e02a9bcd4cab427c66e432a0f10d599e9404b0f770fad11691b280a77e616621002ac8250f93e6cd80bf0e818e8b098aa3edbc5b1845360566082d52f12267c30e654f6edbd84a4a7fb83e939a3d6b7b5c0b23bb51535ced0a44f5314b17d926a5d2e750553050b6bf4481ff0781117ccc6036b508dedc5f52bbb0aa43d87b33e8648a9c83bd87bf4c51d59ee4b9a891da84629dcc6d9a42c43ac2ff01f55da9df990126a6a115a3f520d875f03abdf653958327ee111f2a657c02a292ff02b75be40b3d1c4660aba3a4f93aa626b207b079db769de72e783c148e2aa81d340de82839e53cc852bdd4485d9055449038f2e2a824cfa48d742b4e605206ddf82f6b74cd2f3029bc30112f21da2e89c36951a8daedad9d0dd0b0b64784ad0666c166a207b2f1ced6af598ac93ffc4c9aa54967468bdea157ceb069cdb546e714148c1d7b3d002f446cdbd5bb38ad833d3a196fe4e6c2b5366f7d0e3d81f1d409cf9b9fafae0ba86d670ea85c6a08afaf6b3190e22a3e0db590abc2dff02d84224f2704dc22e81ce900affef86ec842d101ab9e348ca434d0dab5d5567e71f11d21fa8e7160544bd7f9e32d53774eb6880fb5716247e1baec052909e487749d3e639c2f9c7311640ef6d2eaab4e3aadedcfdc4c0d3581fc1ecb5c2bf7934ba57e58c866c2ed31bc8727252b6371ac2f0024f5e69a25b4fac3ec9c3a4b6bacde6b6a77575ef4fcacb133a57856aefca5ed4a447039a593b59855871635f1e6d6e57acf6d8cdd12375ee4710cc0c6a1968a3633c449110125957d0dbee88054aba19b61cc727d36b3753a94fb63b978f16231843629c6d3d8daff2a723968cc2580745348ac45d9d1927e9692ac4c5d8e8efc86a7c56a5cc122a67a7b3ac5f8ef485a228d4cc46736dc9770ad7f68ce8dc3957394fab94b6cf294dacb0773ab34ec2b4befb91085b5268b6a89fe991d8dc975c5076c104a7ceec1f3e541e43690f868f28be19a4eb249ff2159c90ead4f3a7c3689a9f0c430388a63638009735007f3321b76a308ce378667794d6552e4f1464a7c2b3fbd8f0155a5c49f04aded81b407c57da002f9d74a9849cfc0fa7542c77a3c0b90cf694e5159965806a27d7f0d486f7f4cff7ae420b8558405dbd494a219cf077dadf337c559ec828fc47d02663be54c18368e04333b63c4383b01441492ce7c8081d6439b2138381a74bbae4f40c6353487af8879da9d15e02b053ba9193c33c31067790850362521f26fef936b4a1c25e6278b44db390761befec2769775b809dc54d598fdb59de117d02586f0423b0f3d45093d0b69052dbdd143bca4897a4c6d4af65936f95097532d9b1513fbda5a1fbc24719ea75c6bc4181d395dac5258b0851f47cf5eb93aa9a54183965488dc0c6a9d45bd8246f37a582fa252945a579542141875121891b7239b8c5e1c7f667d9605e6d6f007a86bd33d22dd818aa9d5e7579b9e7cf734dfd62d5e5af12f4096f8a384824f3f608ea1e7c0a2bc302cc488ae1b35e183994dfcd4b62147bebe5d7404fa0566d62084750a85da3a6a8f222155dfa09531457487054706e8b565194ab84d1b8aa87298a2bd26726442a510322bcd2ca0a013b98732faae656dea021639629a92e2e6824c8c09851a8900331c21cd70f82990785bb2c9ec219573298f510b895c15b05f4b681c2261b9ad94e90e37fd1a737e18ad22a92971e6cc93a1084cf2155914fcbdec0ec8a5a5986f5a70cc3dcf4ec4d621fb9c4f6cd71bea04798f0f3fc6b17bafdbecc4e1ec3b7c24618420f3409017ac7e02d505010b1ea1256a9f27c23cc1713d4859d2c95a6a18228b60ffa1875915125245422078f1a3188810300000e59882c3bd9410c201818001b80d92e6e4811fdc7d9843e33c3ffdd04edd62ada96494ad6ee0203c702b7024ebfad9bf29f31294a245b2bd2f04b894d2fbfde905ac652daa8b6f43287a0137f7e4b3f2950e90a2174d8f3c81917ab2a119723e858950e79a196a868482dfc624efed563c2e99270ca184ba7343a1b7df39e926c246dfb6061125427c630ac855d6b10a43e0d913b22a5ba3422a2ba31ad142829dabca624a5bfa3338d0e2eb8792d255c30962fd81c94d9b2170643779c5f6a92b389697eb7caae6609e677ab4c66068200473c705803658e0f202cff3b03391eee1c34043b5ee2e6c001870a6e0876f0ae6007b7ce413b1a64601184602c147f0065e22c000d37e010aca0770e38168a4930e104165c88a10c66de6a3039592333f09edf2c113ca190e382a00240d983293ece2f35ba9cf5fce13177fe787f7350847c3ceafe7b73e8d64f66eb2b8bf1d5c5e27bda43d24b91e7836773c7b483a2d4414360e8d171e75e70cfe4ac5b2d6d54761a370407f486e316f3e2a9d2f61461e35ba373f1d6224d4d2c8d8a3675b6645654f624d3c1ce8c05b15c8348c46284d559e3c05a7c8d797175557135558da8d12da7d6892c2013584025577e5677555aaab5293229afa82b28a7a7232721163d4d4da697a59f13a51f90695e921c52db91d90a30a32c29545112918aa11f15372960847c5250f82ca37bd0b9a75b101a50d8cf968f548f120f91336827474726a76705a767e4a66b03a30957a376e24a539da192618a91c240993b2f342e3c2d26784ab4f4b1d8ad64555ca46051ba4eac4c5894ac2881828467a446c4e9b2749a0043fe8020787f5b3fb6beb2185f5d2cbea73d24bd14793e7836774cbb1d94ba9d10187a74dcb917dc3339eb564b1b959dc60dc101bde1b8c5bc5069d31111b275d7ea5c645baca995a581a1699d49992d9549c9a2589eb1e69529969312962362ed82d57d65bdd6bacab8c0aa5a54a9ad252d6956148be7aa69c5acc249a1c21199ea4ad54565a16ed69eca3c00eb408b03a90d9634906610c580e782a605cc0a6e5228b811f1e84e5027413682b5641904600f683920d5c01203d205a20af07434136022609372808d08b24ba26e4496634d2e3300ac00391dde9f6531241a8f71ac63c6ba3ea4908dab81b9fa915b53f62bca90d254ec2f6e6fa2e210d9935d71dfbd677f861ef24e3e9c85d11026b910fe3ffa0b6620dfde50d9900df8851b7e0bd13949964812638f74df5c0d7e0d33f74dd7b421800f4f9990d10a924f9af4e5a5d9d6e4a2f744d90b8466ce850d659cf0dce9109d60c148e1c7c6cb8cebd2f6dc7ccbd8bbee9fa4ee7fa762efbdf7dd7bcfe6bf8b8a74327d13e54decbdf7be39e7ccde3b726f627f1b104164872c6f1e570200d028bc00ae88ecbd77993b7f24812a5d1e64d38248f6a66d692f6c373d59ac3aa8971f0c49634c3532cf2bd7e59ae7471d9979be04e10003456af982bae29928a145a56e2b0356e50216b79125cc6b6f19de9befbdbd366c3c86985f286bd2fc61dc435a6bad753145a8438933e0d396e1bdb5b5a5cbb066e932ac5b56302b4a4aa36273539bc1f935d524d386d4e501c9a39aaad9896996a1109ba9d79489f6a457e69c5130cb25d00af065a3c1cab37bb2911a5595107d027a2b31dbd306b58a0a4b51bf69d71494910c56e3860e0958e99fc3793cbf54375afc2844314415084cb9c9deec4160c35b5f3509c51dd50c0a7a23475db967d44ca3e0648576c714949e5331aa8b1d2d0b7aba5b46512a58bd7e8213d4e1288344d414c618cb528b90013b3f4e45324a2f725d9b94c55438024861c15935ff60a46e4a6ef647bd69c144e131c270eb4f75fe30bf54384f4fa54b76b4283b7e756d2a1358e159e55c3c99a02a0102388513a7b4b7e536a7cc8be3d22eb1285a9210a02346f7a56442c7454df96e2e756e2a7778336d313ef2f313b718f8bf8dff5c84f1eac4053f1a8d47213026acf9d79b7e75e02f897f7eb374f8923280bb73a0a1cc871d76f9bd9fc7b0f0680caeccffc6dfc2f98d5a1b993f62b98ef7cf182aeef76603b9615c11a268fdb19bf919941e018136c662c7d2051663a67175c6dd8959228bda5a04eeeb71101133190bd117a8a4164f4a8dc482450cbb1c961378c31a4e69d65a6b3ebf54b8a1302e96ad9d5815e10c58327935cd12698cf28fa27156512dac4f9ff2355709f8fffffff93e0ee70f6412b8f42fdf7befbdf7de7befbdf7de7bac2b28086fb8f31dafe8105776b9a2ca646be1c44e864a49ec79c49f0bd3759c3cba675347cd18147c45e673ac3bb583d5c705f91c2107cefcf0acb2b621511188d9ddb4700a2223d272f978d438644d0d1ae7089b8ab4aa2923a5bef253790103098331b6f50dd989c671c9c595d796e692a7d44ccca31f67cbb2476e2aa845c0f2f1a86db0791a5d33412972d7b213680e51a612dbe89dd19bde5090788c61b8d9a627b8a2e3f6a14c7547949ac78a9975c327d226d70cea1a2c9886062a35356279ce209b1c962d7326542fa86748ec7416ce1fc8e486091048024bf30f7c639b8bf30bf576dd6a9d26e48abfb03e63fe88f7dd735838e40102e331163a7de03ee7337c4067ae99e1403cc7841b301ee3786ffe9bcf990c1940c61db20c131139ffcded161780b71d147861ce7531ec888fdf3b172b4c60c72f7c03f3deb94504f0f48929b3fe8213845944476436a5ec2a28f698712b2fa24d1e0ed03449b0520ba4aa62cec7aaf4d6d138abdec4b478cab8d00da42ce4cf95319240267b72f3773fe7a210476c187e61350fbf375dca90efa4cb70aff07ffefb6fc861bcbaf4be9359a3dc79f2bff7de7b5733a0f7de7bb7120b78ac210146f918c7724f4211da3f8d0164ce8d0570dbe87b874076fdf06ecb38f62cb9bd877f6bcb3202886361d710845108effd5a280f00cc3909b54645e3cb1fd606113776f285e1de7b9fd970437c861aee75fa35356916ad967948d51e69989cb4a8618c8f317357bd6b55e20f2fbcff2e80d8adffbb9f67df8a1e36fcc223bdf7fe7b67e1adecaca9b89d0ddbe890c52216b5cc63638633543d4f27b36bdab5e7616db037644d673d7a6cefa44787e63cc82f482687c8d75cf3cd390f0639e71d5afe90f550cbf0731ee49c73be7be79c73ce797fce39f7e07232e9616d46c0a5b5a65a5f5463acacf4500856bf13b71c9e99124acde7567d0a698f56b8bdd65a6b9da662e6d5280f589ea2e50a59080c1907266a5f6a735d203b25a75a575491ac9e3fcc7bdf2a22f04c61261cf3e182c25449d1f69428aa9c5c9914286a84a42fd32daa90c896b213395d5567965821506201415843028cf2318e85227e77c88f620b6584981ad9614a3bab96297b090acd1bcdd8092668fc61c32fbcff2e80b8ef1db6d18158dc7ec3d751e9e3c55c2392b9a5d55349083080c4ae7835d41328707ba207547d6952a167ad623bcadae1e316871db2ccf573bb03c0338617141765446c84951e6bcb3364684aaa5b0dab31c5fe9c7775101780114ebe27ad6a9559a4ec4d13542cd2de98eada3a8860ef90e57d8c6361af22f52e17f4496e2150f3ad549cc1b74943e3566808ff6f6fe0dfffff7aeb036fde019cf9d7235cf816a5ef98ba1dd1c6adb5d6da18f0d623323234ef6a0f945fb8c330ea5877a07d41d48fbcbec6ef7cc75b66f1ce90c88350defdc4ffffe6da1e210ca415dad41ee61b1c5b5ab44984164f31402d6a6270863f549ce7eb32cdc459c550bed3848b8a1855654bf9bf5f6b9b1f0f87237887b525199f845f18de5dfbffcfb7eef2631c6f2df3adb7c80f829c23f901d40bfe6bcd77168df8bfafd9b8ff7fad0ec11dc32fbcfdffffffffffffff7f64872c0f7de6ef56940f3574cdb65d2824cdedb6dbb2f71563e9c7e2911d1222b1ffffcfe5a407566786e797ba953400d2f5ab4370c39df3e725573912522792902f39229cbb144a569a8ab854dcad5cb53af2aac6a47b3696a5c4c4f645cc48f911f7cc5a08ab0648850e4dc5ee2f7fcd336b38a459579d4311a2ee028bd4a8548bf86846004a77531a0c2010876120c791467c2d1400091cd24468a848242858c024108a0281502800060241a1202008832014072118c3642886911300c93dbe43781e1e053d0e85d1b25e4320e9ce459f94650dd4f841267ac7151c6b7e9785dd455fd8e572c62d03260f3a97a8789ee8a29a4e8d6276dde80b2a83090644eb711aa69a553c32dfbacee705e4529bf99af360a2910fe420f4bb0cc8b5293a685efefaded1e37131d566f69a9d815e8ee8c15247138d46a641527afe90143b636b80e2abfc0eaad649e3697514d306061991c133216489488b6cf3e306527bb1ec4eae59c0b7dedc2064bf5c4a0689fc4402553ae4c3c2265767ad8520f99f5e107096d49effd8d8e70d129ee3e6979636326d126ee3e68643a630e12e77dd7d6bd300579f426e4528364407c8b00a406cc5f3ff2c422acd45771d006bde5d1ad7464cb291ae7ae99bb0efd1a7ce03ed654a44e1cf45e0a066b036da4b7c8f5bc49f7f63608217ccd05506a02b5a27872d27c7bfc96cbabd1e23f30ba41384cb781d0c69a91b8e82db6407848ba0875270822ce3967aa0a445a7c8e3c4b5ceb0861ee2e26d358b1204b514b1b7a3da8e35b52b0b374fc91319f5340f5d42e0123362bd577afeae9ce5070a7ad3603a9b391b4cf9fd0de4cd457819c794fde36d55681b63e0cef12eac85b4c7185bed8d37e5ea22a88f70baf186ee721941f4d686123a86a51e7df0044bd2ca377a5283a9656a89ba784b424c8028fbdbeb54bc9b0c234b132a3a8b8ce3423a69cf3a7610c2dbdb9200772f99787bbe9ee4e858f3d00e05cf1a73bba3d3f94f9787f89f912348253abcdf185f7b209835aa76b69a47c937372abc8ba4d90acae2e5993d024af5e3420cc814ec0ae0d77a326b0ad10d88cce3e00ab917ac96d7385d06ef2312145a4674ef80e75e6aee37229d1cf02fa9470940f036ea8c490ff82ef0b703acc05baac221e4e28c4087bb363aae7a6ac0db71828350d0c0fa70488e5bfdce00de82b292843491a1df2d12a71a13f99014420f2ab36e199d90b59be39a352de680be9b52a83af0c0eae73353943e4b967fe40aa2ec71fd335250ae9fdc4b9323be71427db7811267aa0b1ffa657bad4e1849b845700d198fb2120479607f82941020c4fbf753a2be5b35be60363665f8b743db239725556a57ba6baf8daeb7a877534b1535126da195733dd1b3bac08251f36358cf11efb6929931049eab5b786ba38fd316d0627a3d766208bcfbfb6fb1fc6328e30765a7c74412026dd8ddf48aaf7fc0508282e4a576d1dd7069fca472c6c190f37cff85bc549e8382dc4d3af9471523ab207f6e7b514d3e51f41f868e197d5564bdd00424020945670eb94d215f03d49eb54067abc53330f5c768d49c8265ec9be4b60ba84d53202ea1a501fa6ea03ac0a057b5a8373b7e7efa58ef02aa0d99ed802867c8b643ab8470638ccbd9d3cfb076a99fa18cda75c5aa0d9266718e60efe9459d2e00527911175c14deac969108f5c8dfe1f3833cc3c413efd4571c1a890af80ab7a8e3e3eb6dadc731f1af18b9e97bfb6966877d71bf20d5ce6e5f2e209e1a2aebe179f3ef24778e086d1ab85d0d0f86123d02b3f4a6ea3308b7e1402c4a59523f33c878f29da77926a7b0ce60e7c63ecf4238039977b7a44cc9c7416ec219d8d6844e01ce8fa01ec250be3fcffe8a70e033dc28ef3642c6083e0da7a64948f6d513c1e516fa2cdc13638c0b582e11e0a52c02c2f4db8841a5390ee073678521097d0035a9660dc7a86762768bc3610fe03669e7ceda2fdf97de0223f5f80491634132c92e4b0c5424c301d8d812ace758c11b58d1dda9fc89a4856e60b10001e1989628b9c5499feeeccb2c1668ff5cc4077ccec82abf18cd21f14de662c291fd147f17acfa0453d1b878fe01ed8b65483ec5cb03e8953cdfc6744fb81e027e87189ab38cb1311f11c29adc206b40d55f8292b7618dfae2d82f2f412bf717b1196502f345066ca1cc7ed3643a477e20ff8437c629bb1ae82963d221855de47da5209ba27d07707af73d1b94991d39a088913e43522b8d91d5ab476d68ca8ff355d74e8ba399ba5ff9bade98f5e00194918bfda47740ce97cba785dc42c0513d540df89131740d2c231f969091c386b9c51f1b8cb6299f23e02dbf2037942e983e224d2aaa2ccf29dc546603a9fe39336f834fa384d291ac287830b36a0a4641c372151cb9f5f02db3aa03db0cec5d1fddffb93f93ffed193ce810170884461be134d44092791e0382fa2bc105f27816aabadf600266e77f7134006dc53d428a9835f291f0fce706d8571eeb7c5b8c8fe1378d47de1a17d59fffdec08565468c0870930da09ce0b73356dbfd679eb288f0b98aa823c123f44d5ce51716e28195b4e625b08a81491310ff82f99a6ea60753e1cf87733bba9b4162221f35d0e071f5cc5f8c4a258a332c53f8d8e7cbb8723e3cd309b09949e309878c10360a0e4631bc913b3c875338f5ae3fc1447baddd736f442226b82802f953911f7ff9751588dbe71eac5a8e333c02b9697e917cc76bec42ba7050a76a592637d615d537886c046c594c77f94cf313acb2a0a25f25b6e850f04a21de8b35285b2b1bc6f3b6ec61607b957a5d09970c9e3f57892720793a85bbfce84b036449e80044218deb007a828f460807a5ac29963cec9f300047bba1caca4c4fb854aef34de4e0e60cda9c1dde63f52b891bdc72cd08f4d21d234e8f84cbc3d7249784f8f5e8ffd0a2e9ca986d81da00473e5d528a2f6070cd502a91769a9423294d6ff2fdbc6d8e20a8dd997acb299dc7787d3016b4b0288dbae585d5a2c5c6987dc54a51813cefa188dae14134ff9426306d4782cb6c3ae9b6235a031d2f9ee377948184fac01a0f6c56cd6a738779d0ca675834c16075995d5c1367431527a931a226b8a5ab86deb844bb74b19974a34085c370dc5437e43b9c0f257b7048e0ba2b78a78b81be953befe49ce245f5b0d473d993c2707202af3b763b5e3334f050ed9c4f2f59f7cac726c4083110c2d89d08a83ecf35b69dc228e94e019656e5295baa4848e43df2e2fe889a602b91f2d2885c339e72b910f0bf1801550b7091260ea7579b19898fc09fd179ee493a4e202d35fe9ae4a1e6f11d2b5a9a8d53b026a0d654256fbec6d8c0784a7f4074ec853aad788711c2d0f64cb85d1d67487016fcdea605051462b88d33798e07eb4f87ef82ca30ee810954fe16e4fde9be2e1867b90deb9516e4f07befb0c0dabbfbd0ce453340c7dc2c18cff534680f98efe73fb8bb8fd3dce88076b5de6ec0dcd86e80fd899e0d1a5e0527b79110dace64b924b3654f52e361194b142fbc4f24f958316cc3aac13feb68a24a03f4a90a0dc01ad60f1ed7037b90ec87146480ce4db7e3e25f18cc35285eb2c1f8ac8e6182e552bd19cd4bde64806220ee188ae4f9d1380d023f0f83cccd179394c61281bd04339eccb343d712bef4e65b48504b92885748b38739145dadf50886f6da5ccf6b8818b48fc685025d49df31b027ad9a7b1e8b601620bb782a01eca1f2aa63ffc49cc2dc00c53f6222a00a711836271448c12afcd0cc331edab35c74c46e5a58658aebd231e8c573800eb83705feb3e3fd1877f0297f42cac96c650abe7f257d805db3267229705c4bb3bd45d43e6f1a5c222c4fa402bb3cd7e26c59271df129f903c1150cad9b419cf1a4e011e1e9982748f709411cf3dfb221b24f3e2131fbb84873d27fd9338e8a04479787332117891d45f5f0dc12a72c5e417d235af4f6a52671ba06a81dd145d725295804f919c06fcb515500b396c9b8313813dce945ec1b2d130153775dce6a3b4a88321e3d6101f12a4d1d54521b9454fe7e2e02c08f159bcdc77a20c67a8284b3c8c201ce4702d991d9957c4bcaec0237fdef0b97777d112bdb2e186bc1437b17d2e5b1101183641f1287f2aa517416aa08f8bc596fc3b4f61b93409c5da7e9a184b98290d0b6c247a5e2edb6ca4e402ce5353b46df270601d1110edfaa32e6a26e60725193550ae36a2404245abdfe3ccde5c69049a0307e272cc5a085fbdbc48642d8eec7826c8e29a39ed3d255f1f667d3aede7fa14d3d2665cd2d7fcf12bdf9c1c654baa59b72d584d2e72d29208ed43c92ed8aaa9b82fbc2d009e2de9a944c779022084abf17c084266b6f552933804088c82944ff93c3a8954352e4a3718ea17c642bdf6e76cc0e243469fef1de01a4bf6da0c8fcfa5c9a92aa52df882fa856c1dec1e5ebcb1878e92724923c01ff23514cecd41e5ad592cd344ab569e6cf65356e2a9dc7eee35485a0940ff688672202bf79cb3abca67ce1a56e2681920b8b414f0872adc25b967dd606140873447485860e52291c04ea7fe851e28997d2b6889c9a20d608fea304e2e265c3db36794d1eaad8384aa1a770c3401c205c0019ab9e14edb6cb3f2171f6fff0e8d080f2cd82b95a66606b982caa386dde09e37f8910c58cfc159cf795ac55de7106bb3445569c71201bb9ee85840569879dc87a67e04b17e809f8b467f811d9f624ade8aa929eaf27a5cf714d983d0a6c192008c32dbcb3a1217c1740842de3ef9622adcf1e24af8865713681a4277d6d24b37bddb217674dceb1a76579962511168a5a6a4d32308b2e5b8fe55991c7f3ece9344d98c484fef01b5052dafd7e67d9fdd8c08c02fcbe9320fc3128b517e7cc00ba52fd96c995ed3ecb9e82d225ae4311bdca6cbb93f7de932cbcf654dbf6c06473d6b26cc0be0b90696ad013eaf8250b707389ec7fb9222b13a0d8a15e0ee65c6ba9f2b463d0931832c2eaed36820c98841a402cb74258119bab38b690c4646567ecc34834850e0c369106d20ec16e6db0fa040ed44f9b34d555a42431983f2cee6c9a8254b9601902774dc37fa5a622f5dbdf0e57278a9487c884ee63fc8b8d292a92125b81f8c092ea46395f3ee56e7c94201bc8d426a4aca2a4c90a5cdd39748a627dbb8a453008c75583224fa51b7f2b0ca638611076ce0633646636b6d7cd058075331b64c2d675b7e2c298ff754c0417361b89f0e84ef83d7c3225a362f9afee0e2aa697853d53f89876b842fa6021446430e8d79046fcffd8321c4aa41a920db85e31104ef8143848f640d282838bb6f6f3e2268c18d7ec9e298c5ed54bd9d1735cb3b81e446775aa9ccaaf2bf1e680fc77b74429c224f23d6e4c22c325a52a4dd9591f44c7a77944df6e91f61dbdb9a2217c6e6d6ab1dc6cec96e809b1c5100a230e2a31f56939403a377ceaf3551c75ac6e515a79ba15f11fffe737691299e8ba6f76b97fc3d390b862aa4651a4c0592ec5a5a99515c400714b6c9169b8798c4cc5730859da55e91a99506d9d830a3d3101b999c329500f818aa41cf25c253d1f1d0e33b7cec3a9245eec106bc99a14ef7c72eeb1a441a0dbda2be47b768ec28e4fc8aff03ca7157235cd1181121542aba49bd4a7e25b7f1ab510e8284c91ace23164972889a1267021214c43e0251b8d3d9120486fb37d7e59e1cfacc88de7cb5d60ebbd9696bf80093c39755c6dce39cdfb201de0fa1b22999593bedfc4b4351a2f6d9ccc053f9b001325ee25b261225f1714aff534d10519758d905d009807a22301bb39f5b2a95a3e94474799705cce5b5463e4b2343762f6477993c847b3c5f196d1dd1b3aae09826d2cace847afe8cbb11f03ce78609ce10a1905f28db48cd539cd2139da932d03fde565ecbc4921c8e9fddfa6deedd94a20afcd437b82633f54cc802d43c4eff00728a202c99193913492e9e3cbb193d43a2407a56ae5cc6f362f0c578ffc6ef696012c307344edafdffee8516dce99a14973d26fff7d9808d7f35ea21ba13bbf3dcb0009b6589527f0b305feea82976d5d958b319ee00a8f00b7ae422480ed677c5e7ab5139fdb6ce83db1290a802b523b0f9f4f14d15bb592f56ed5339e8ca71f8bce2ba08ba125a11ee88c34855c7746b54acbcb27458d744ed0f49d42df8ee3e2855d6bd33e449cced0d19adf254edc7cf058526206f161de777881197be47e997d211412945750a6bbdf08fea6ebe5a65177d1bd77994790853e97686843147a29982583e66915c9072ee62077442271d71d8d098cffd7297c47ae8690cdd55cacfa99b4596a866469aa213cde0ba255ee23cfb1eccf617923549dc980d7461c206d1907a517d7a14d02bb711861df5e535ddd1de652de1681c0eb34009673e67916a84bc23ae9efbaf996c5b4ee45b167a9e1d00c38b90d48a1816fa683e327f7c280ac301c2ee38cc1cb7c876438c6aa98d17a27e20cc30f5d2e0e156c45d62ffcbc61b983736c20481b535cad94f5f101d34cba8beac52f8f9136755d501dfde6214580ae647d8e2aa48d1579e80b08fbb92951f8cf8c8f4713f040ddfa277435c4cc0fd8bd17f390dd8af0b600452b8100e01376c38b14dc6de3eb78462bf991150c4126488d86def9150e57c030346496256026bcac99630641582f5d0c13560d201ae545d7116c4e576e0e76105ffa47b823d8a01bdc399eda18215c50fda673ead7a0bd378225f1c24c397d08451ec39c81bbfcdf4b9c14565217c29b988b71a9c225f66dac7e81c18308a0e9d6fa38d14cfa27563eb8810781f1b8d28f0ad3530991ca247d5bc9bca3cbc7dfc7f2418ed6d7c716c00b6beab6bbf8bc635d1fb8bfbea335f671ae45995ece2e6119c82edd8059bf6dc3531787b50b5dcb49d6e5e3c367b3e035c5658b92c443ba842d34f84b1760bb07974eea03ab53cb323fb864c7775d55cd4f547080ed70923b1762d7235d3efe84e23f5b735e6a7a3e5824554888f90327313a03eabd904bcd80365f52a92845e6d4f8fcd654ee477377dc4891483a91c86e11b5ac141b2cd40643775bf4b3311fd7950206fcc16e83d2da2ec2a5bb5abb14f6812d8a03f7b06f5cad0e33b8bbb13d511dac360f6ebd5dcb57ea8d24c14bab71fc82d69f9a7922b52e58183fa95914c4dea9f92b31130c4728c6a0a3579cd819e9c7e4522661ea737de03bc410f8c86f24300d9536139b81842886494103d9efd611c31bdd2f6dc0c74b80306842b221b820c0cbf7f7ed27dcd755bf64ff6d1fe2febcfa5af7c7041f03f929ec9bd437db67a83fa47eaabed27d6bfb69e813d037d9e7db6ff2f811879f559feefe69fa14fb0cf459eaf7d3b7a81fb12f55bfdefd65fa24848f60f96deabbf50dea8bd49f565febfeb23e8efd14f44dee9bed33dc1f29f8a9c857ba6f4c3f8d7d02fa26f579fa4dee47dc4faa4f55ffb43e8b7d02fb2c8fdfeff906f703eacbea57bbbf6c9f843e02fd36fb6efb06f705ee4fbbaf35f9c3888fa19f83becb7d337d86fa63f6b3ee6bd5b7b69fc63e83be4b7d3ee73729fc88fa59f7a9eedfd667a1cf609fa57ebf7d83fb81fb72f7abdd5fe67c16c047b0df67df6ddfa0bec0fd69f795ee8fe9a3d8cf61dfa4be993ec3e28f34fce4d40786fad0f4a7dcd7babf7cdf9e7ec6fda6fb45f6fdf4e3dc4fd5577a7c63cecf439f80be497db6fd96fb11f793eed3d5bf6d9f629fc03ecbfd7e93567834e27c02b3ff521faebeccfbf4f7cfed23ea2bba5f45ff6d1fe6bed47dbdfac7f471381f83e3fbd46fdb67d417513faf7e5d7d6bfb28f629ecf7d467db67513f667fee3e5de75b367c1ec2e4883c9b270849a206df4037b159f2254b4973e28075d3808eecef50f5d9d35f6d7fe3fa44eb67db4f729f8afa22ee5fb63f1ef341447c40fad1d47f63df03fa0deaf7d76f6f5f65fa9def27bd9f6d1f89fa2aea17b97f5df345b67c88f483ea4752df15fa0fe8efb8dfdfd88afdfec4ea010a8865505a304cd24a0733f40c74ddd593650fccd63ab3b337cb2f2000e204346424ba93262b02c6f1739395b5dfc7517284332657c030101c1d6e5785fb4f4dbe731454f443b58df5e7988c71a9987bf3e83cfa9290bde5de7b6f29654a326505a204ea04596a3f3844929f0734738e43afcadbd65dd66f77b23ae6602bfd1bc046a82662118a89abdcdf357e2d3e107a0a44989005649a2488540113fa40e8d6382f905731e54abddb4bb783087db80d6180c11dba42374a183e61679717d3d97dfcbceb24f7b32afbd84843bae43cf03bfdabb2ef68fa9d46ee40df5d16105567d0c6ab5c3275f73b929d636fd317c85f9234bcef1d1cbefea7e77d70b8e5da4931bb78b44ef219356a405dcf4ba0ef9ed33e6e5b94366fcf83ed1b49abd6c79d8ff27bde3fed230e5faf1f1c5aeb83718c9a8d6a256944dc837770682d6e59e57ebd2cf7d9a8efdfd6bd79f3595ed82e54dc4a1df319fe1d8b3cb7e90c62f6aa2c85cdfeb7cdda304750e69ed6eee58d45b972b262aaeaa5a9a9a72a3252576882be06f81424a79c73ce9bbbab8347ceb6cf3f3fd9679fa42f6f8ea09cd9ef0e9a9f43fa3982b27f9c77e8ce8d7b1e3ca48030f4429887d00f611e431d0e4db04272fa0086911cba11e63766211d3cb2d721f3d08d30a10cd329a4690c18342b603993e4003564deb0206508141a58ff1075aa28b1a932051b1608c0a58632376031f3c60dac7fde0c38183dc731679ac86de74f2307a3905855f9628314501410858f1e439e3451c10f9d1bc90b1c2256aa18e9a124842522cd9a224ebad059d225b161c7f20c0624b9ca37ccb4992607630eb7cfbd24697c604a152a1b9444fd5801d6df4c9246047a706287ab237494b6c0fa3bd64a31073bce1d10bc30c9970db7304b7c880244098cac100636a74462630e3b26a7721480091429c8db091318cf178dd7ce2c21fdcb24f224b51ccd106192b4ede43866054d0c8862bafb5f6dfa63ffd6ffd2344df33c4df3bc269e17f4b296db26d7e379e0ff7c59cb45d1d6da4c11f39b5d8e61cc04f186db6846a7e5ac4091a205c70b1c2929a5947213001ccde8c43500521c008ca68680b20394f5f87977b77f6fb9a57c7233ddb3dbb6d19919318b1431413345ba64568e668a50c9e18ba026122a6a054a8c80c18a9438b07e33059226394cfd908187beead6b6984daa1e35da0e41685e8e627628c349d59fa097b5dc3675b02c92a270701204082a37725c3e518131424e1534fc60f2d1769b2e4d4a29a978a9451c2126c80d505f6e900293545091041a226400628aeca25959e1041a904cbd81c9353b0c31c5143d748822881c585448a0208a2e3d40612487144e40e3e6489928b22ccbb28c0b97258430c35402364fcc4cf951c6ca11262e4cb1022cb287219afcd76b6e1a6db9dd36b9254be9aa000d2660a3841a27264e60320e1d2c6ace006992260a29a5dcbe68f14018345637045124cacc07ac6c802ac114a5364aa2a266d7347b415423a85a1635b65ab35a9570c1d62a71a82facb0e40b9126599cc0b42544307981489a372ac852022e48f8485a63822c53609a10578458e2480c8a0a4e93c733edcedce236b32c9b940e90a5b4fed72a628ca66974f6b0e69c934e3aa3a6f7515153c045d334adde609134c920c40714aa24e1a049851eb4bc3901489a2cab5e8f9c74ce1ae49aa6cdafbec0e253020d74e048191264cd189816c48618ca34e1c5479536b5d65a6badb52ee18305a5934efa53eb1525c4744e8c8071c3062acd932e300d4e0b6b9c50938316261db45ac57c9a18add62834adca27a9a6695aa5d26a05ffe7cb5a4ed3b48c62c0c7171fad879681e2450a94116aa8ec00a18304941927374328a1820ae650252186ea882f564a507a12841346e4103135b5a06d4ec9b22c6365599665d4860c89cb9b17ced0a044136a58806552ce9c6044082a58d2a040ce962a754b0a4fbc308133c40b9d28b54492113f0831a3a507184d9e9122843cc19929d6922398f4a8820a26638e80654f58a84365882545a4c8b22ccbb2d7192772f8643629a5526e9a1694c1a1370438ace1020a413e755b39d83ebf6ddf069f3b0c45c8e738e8569286947218fd383a0c2965955b9209a554ee977229cb259f1b7a9d707b20faae4b3b296a2f71381f68bbf49bfb6da3ef52339523941aa97c418e506a9424c90ffb2aa94e2f7eee12e873ccedc89fe776de3027638ec6a59c8c3f7577c73e04836a6430a806d0cfab7194ea90997ea42f796ecc9a4dfaa0d458e5f8dde0c11deb62b071ea87952f73c7a28c3256fad7720a1bc6a910e4eb652d6e34afadc62b08e4b60de8b91b6e61c772c4a9f81d510c74a38351cafa523e91e4441da8279ae408f544111a394239112477ac577c47a70e9d39759bf7683e5125aa7528a82f98ef434495a8d63ae377a47d47f33bfbea6fdfd1f4ef3a84ed3b3c1ddc41f38ab9c2ca67acf8ee9fdf91afe076e882c41050386122888e092846baac0152c6cd6665bf94c2ae884764f9fa3a70e5151cb7f9e69b7c16ef3894af75dc5df1fe1d693173dd8ae7567c7f3756fc8fa464261899dd17902526a23bcfbd87f41d87449bf63e40dfbd7feb4607c356b8833b9fee67d6308e1dad07fbd0a759fe107ded391cddd3ace1a1be806be11d9e43a4cc752191d7f73b591fa9bcab0793e3b8797d3e7e9cdc88f2cb70870f75061bfe1844c4452aa25be3f4f199eff3f43bf2f1b9533e77b9a15a5f7bbfb4bb317bf746d23bbfe1a437809efe6703e8e977afdd2ebbb3bec61c87808872d7643bbb5ecf04d3126d49e0f559f61c4f7d1bfabcf73bbfc3fdbc9bab074e941368baeeb91bf2c8dcf69303a4ec2e458094353c820064c7f2257743ed5b7ccdbc7937a09f38d431f30816f4e7817cc7ae610e9032c5dc77db77d72ff74037d49ef5f48b80cf774f3f0d3afbbcbca13f873fe7dce73bfc7dfc3cf0790ee3f0f90ee3d0ee0ecf126f98f5f2ebc0f5f2fddba0074f072dfed64b1c7ad3c19d29819f9faf3f373421d7f9fed56faddbbb94797bffa9dfaa2fe76fad2a792ae6f916cfcbcb83e3c4616db57e5a302de89a1c7fe3161bb8dedff58ec32e7338b8cf3a787df659c8b9d721f96fde7d7dff8e74789e9f859ebbdf0949c81b100d4520bbf7f99f8ff135bf756bf13b3eef9f8dce3edfdd987f2e8e1d9e7df008fd74669fe190e300c9aff6db959a8b9fd773f7d3f174b072effa30b3e0daece2b660fdce77d4fa5caefc7add9facf71b6eeffaceafffd70db7fcfa79438fe5399fb638e5c5ac93eb294b17fe2c16efb28a2aaa98922c7ebb3ab4ec58c7eb22e5f81db176785ec122f3fa5bd18fa38533499a5853278d970cc449228a1b27351861c4d005258cf8425776d66ee72b20420879c2440569061de6a0b1a20414393d602c928833e48d8fa6285e6c52163368944e000411b0f9724aced7aa36df3656d5fc67d579a5b7f9eb54dea62f98bfc9779657c579bdcac1f952babcac4d6e4f6f1894a95c72b0adae7ace73df412cd0876dd559248d8841fdd066296377acc3924916f1be23df01a1cb9c4e7210880f84dec8852ed7dfe119fb61e9d3c7e0d21fba34eca770dbe6d7ba5da775e6959ce3569d372cb7772aec550e4aa0dfb60f81f086e9c6b3f56cad972efbf3316e8fc105faa11b230ca10b04f4da77ccc5836d0f0e6db883ee609aa5cf6befdccf2c310e1f1ceea0b93ecd0e8471d4ff36a81807f7345b7e6497508cda72c3961ab62cd902c39c73729d95ec5f8e515b826459963535c1a265dbb6a62258ae5827515890b03c2c2ab4bceffbbe966d4a8ac2e2c37af2ba763c79af3cb1b3b3c393640e538d1fabf65059fe459afab3f6c4865bf62f43ae88e1a1082184b002f3236756e0429c16e220b982f98be04f02ccdf48d2d8acad91b7fc2198c31a317ec43c620b10e6f737e6d13f3b2afe430859db41b3d386c1fd0591770d0d881cf45f80cfa81de7e1bfdb87444446e1e631868394a886cfd026de417367da78049a331ce4a0672c3865da6e6c1863fa077def0ec3df3fafaf64ee98be9893e2707e36e76ff8fdca9fe18e2cbb2f40079d66d9a30e506849d3e6c89724531c9314b2f0104515228cc0fae30603a57f8452f6dfa08e8865ef9cd8f9928a871ca1a8b8647f05e40845e5e3e3f705c318924edc9f07190b33e4961fe860c4fed6ffe51ffd379f53b2cc764d2c0f2121301e26b8688049916f42cf10b0d6f38874ac9e96603c4cc820aaca13acf5266429882201ac85a5b4e8406aed7d903b77ef3efb2e647d76c30ff2f7ddcd11c4ba2fa2b0dd7b479d3ff78e3570f0bf7f01d6eaff7dd79f0dfff94632bbdefbf4be591e8680bcb2b52d757158cab1d13d29c7f69304264b297f56d6c86ff8dfdd3084a38a3b1686c00e9a3570b0e36dd141e69fbd06bc7e6dcf3be6e06f5e15a125428e0ec838d6e849be671b7fcc5a0771b1840d5dd18d0dff45772c3e7d70817e230d58deed30e7d49ac7e4e1cd837fa40f3a38710c07713806a96cfc90088c11e46fe42e25f63a5b4903e904513ef810640c1418a08091f0020c47201142030e4eb0feb839129bf477d01514a9cbb5f4850737534a51e918981d62b8f2c4253a91692a3fd09e8fb1e8c5ef7c8c4503f8d6c7584480a202bccfc758047efdf9401f63d1018a62bc8cef9efb188b2e087a0fba000132621cc00030c0021060002f04a086e7fbfb8458e4ead13e62112b167d3f62110ab1e895422c5a118b5a7c7fab108be2f7f70ab1c882ef6f20b188c5b78bef6f16621100bebf5b884501f8fe16128bfe3e76a187f4fc0e0e9bc84e91d6ffe0b08de4fe791f1cf6119faf38ec1772ff04c261c3909be2b09124e9a6ee391c760cb93fe2b065c8ed386c25b937aed3289000aacf4f6ba7c76219dfcaf20300001751860537461937cb6f71b72ccbcfa40cf9ac2bdf73dd186558b5795222462b39ee4b3c5cb50ad19d8874cc899494b8a4c5134024f46da5946d95fbbf338879e889be7fe2041009e124d7814d9cdc763e06778b30e6c4de3f7cd8fefe8ab425cc10273a4b6c9418d11f99d858774cc66e89f550f0440ace0c4941d65880029b14c440f5a64e0f37666a00b2848a26ac86e8ef9458346037837575f69b8793cfb574e7a494dacb8d7292d229bb00503090a08c0c52e0a024ad5c6989e3a66acb126590705343ac4a0990334e57983001153fe8505501ebaff646f7daf67c95dfd1fc4e8995dff3f3caa5a51b1a71b763f3473255a00f5f9c2ce264fd43b9e41f22e59d8a430bf4598648b1bc23cbb6fb8ec9ee7770b859e5aefb1facc476ee61f219ed33e6ccb288cfe8f9f69e9e9e9e9e8f30baf7963ddf15cb705ed1c1dc8a3bb26cbfab92c1daeeb76077ac63355a3f3eedee719e77a3fefccdca1287a59483ddc34920240df9f4c445667972ab7eb76a61b7b2b773cf0d659adcefd59fdf545de533e0d8dbb9d5752f1994bbb08655f49895dc36fb8343ebb381396cab200f1b38d2c4264e1040be046103aa870f609a9c7c597ae2129dc80ca579e2248e1460a04002e204993628cc6026494953f901a44b6afa41ab364f4a80518eb70a0f80aa21093a4f7078220858af19c3c313535461642a4c24d231275252be8fd5b2b6c5fa6acb1620d4fffec33cbeaf8f04f36e0b10bc673d0bf360bdf748b02827892338d449f2420a563008a8206502272790382209587f65ddcf637dfeb15ad6b6585f6d59e95875decd20aaca53ee0c62aedfde7fcffabcec6b56e577b47d5bed6dd6bcca41a7ea577ab052cfbb115635ed8aa0f3118f93ddc9dbc7316474c8f1a3802ff910c1c8350624b9caf5b37f073b8c91bbefbe3a9aee63ce32ec4ae360ff77e4e2b5e1171a8652bfffc938199f72327ee5535e45c6ca1b83e6c8d9cb5bc129cda71c46bf86c31cae0cbe00c2fa87ffdd182b7f66cfe1ca3eb374bad47903d8701162453e65fbaa9af7c56c7ac7c98a51464842f60f73b8b2d328156be1aa66bfd9a419d36c524f21c9f13b0a0a12522787b249c81c19450c5304c30210b9898ce23422ec5629e7a434cb6ecc974e4ea37dd26cb34952322cb338d88fc2c324174f40bf36c486b2c98af462ccbeba74f2194d7c463635db3571f2196167b6dbb8387da3acacfbda79d995997adf0d99b39f73d6ef86e7653c4e36fbe8a0772346308261df68efd84d1ca30c4ff2a7281aa224492b3ec3712b49269f11f1cbc130168cedbed84e075b3b281bbfeec91a3995289d172bad388d4d765dac9228a35fc6102fe827eb5f35011038532ca12a67382ca1aa95655394d1dfe560c38ec9a6eefb59681390b06e1a492a95c312aa42c71443517a1887457018f159945095e787ce941dcc529d1c764c36e506b3344736f98c2cfb0874745688945be1083946191eaf72c7c5864831b6d8289f5c606a8445d84bfb71f00f8bb0f8f625ff05d838042ddff3a20c7f0d88976f481c8da82e5e64eb606c3b9164f79438678c33e6f0f28c1a55966919159ba96643d554316e485acf26aafc5ea3237cda0226c767e53886492a1f2d4d71bf5becc8f43486a986216a74679240b4e0833365d5bdfee86d1eb12c6f38a3dc91394611c921772c471cdb479e788a117295639490aa8a00970f91ca9e5d4cf627f21971f3aa7f1140f292b718237ed490aabce598e9c7efcfc696a9fc4c3469f45e514b68b2ff01723ce343294fa711df77de83b2f3b55eedbbfece9d0ec3aff63b574adc808df5be6291f711a9be1769c4a1d7ce5e28021372fcee3fef632cb2dfba31cab038ac51a3114747df52ee697d49a38628e5da8102b287c3f85a485fcbc0c6a8a5ab2c99fbda5f8743241a6fd839d2cf06addfb75adc53eeed57efe30645393ead1fd2d7b6cf067dae05f7f46bad3874efbdb07e0d69ad971531088a72fc8843107fc7966394a1bd76351a5b77b26ed8fd7c59bdaf61fc4ac4461cb3e7ddb0e2d7b08bf83187afec613742bfe4d778832e783d7bfc0d830e76ce6e0d079d4a251bdfb1c718290ec2313ec7683bb7dcdb12666e61e6c7cfb48fff92c6c67d7c0963b3afcf11cc3176378112250c0bbe8491dd1af46a00c548c2e87fba8084d1df1dc56748dc4dad34a744e827a13f840e50912ea9a021b00081956076d02c29cdf18689eff48391cb2f399cd1cc0f31640e58a9430eb71c89f0724c2205f20a1c5c8596225cea68517a2f6bb945c4314a0b14394605901ba824cdf594414c32e70c01000080004315000028140a0844028158300b72204cfd14000b74a63c62582691c622811408311005210c40310c03200c020c32c0200529a2dd00b5141f2dea4b26862c5d72b1f1b48ea30f815acf1a6ccc567002db0443d31b35b685ab8adaaa94aa5229c3345961a58f65e94a5c02019403f48b5dc32c5b9697e134ce1117515cb370790bd7d971edff108d19282fd573b4bcb25072d94e02bb8c12398223c5d2f5e75de0cad0bdc5ffca020c57d0731ab802d0661ff69a906fd9aa7156a1a5b7b50a7cc0f8aeb22c163a7c129ea9f824c5c58d6f71311e94b2df3f288c0817ad79e1b1f110f655554fee15dc38dd0f9c547ee2df8fe57a3bed1bec0b207875f17a84a1a44e7de4c136a917780c742960366526be435e9b100cfcc955448322301cf457ac71d5dc88667d861085095e2ffc5c29fe97b8aa1ac7c25660898e5255c8b6de97c843f2e51356accc18feaa980a7b05109eb6717d58c6c90c54b9d10996eb69b7b88dc1907d04d21d7b8e77278316ff942151e709196074241179825bb2e8f834a3c0897d7efc158489af3fa7620851e780173e2bab7c73e480275b44188064272a5f422e0e8976551d842e521a18a3ccb196fe77ef723b446d13b14e76214f626b7de9570004206332ffeb1f82ecfc8a55425b2414cef45b4e2efa92670944430d1578bed4d9ef0b165b5c450d4b06d2ec0dc0081223f40d32ff8ea51f7e12bf703858df286cdd93ee61a86bd5d90511b85e7abaefd2f299790bf1a11fca4c252688df64d01f6686846873836cb49f8970e28d9b072169975b16ca6d2d5e8a1ed0bc15e766bbd53c70502e0414b58f35dc3a79c164bc796faa1e59201ae76c705fed0e57b91399c44ae98f6b8acbd3804d238c8ce37510b2c4529901bb856587e2ebd1e9b6583c2527bde6e1380adaed2aff5e84c2a216bff93d9e54478d1b3783fa02f77cc919796b88e64ba354b3439a07b7934eef88f27b7f9aff9dd8b549b153ceba393f1489ee70b1db3fd844aaa19d802847b7c0710882ee9b5790509b269e3152c556718bc15ac9f64845ad0ef15618dd1c49445051798ce585444fbcd9da9925085800b5b2404f91a15578f93c59868d5b1d6b12aa3cdba228f7c4bc73134db156408a5bf96c84729a0aa9e938e2e4f823a9cea07539d4459cde4c09d5af96edadcbcb65664bf8d7456c307359802e97c7096c9616440a59fdf2b157667ae7b614276ea2d5d09e1b8811ef51f9117279693a112e8443b1dfe8d66495486edc03212b81a184ab15fe94ec353b0ca2511173263eb294efa15504a8420a775edbf3b10652289486b6c421bbb607b822fd3524412b07a315addd101502bd7661fb125066e13942054dfc707d4a51014f816cb9c904e7bc3ca1f5269b57b16f07418d6c669943abc9a05a41594dd9e66f2c3a7dc45277cd2c5a8b6b79506c81e1a35fe1079433ad1eeae6ed1d8b2719e84c73601485c77e1285a5e5becba775cd95459e900ee1204bb02417c4d5ca7a2d0b7d3810a77d64793c74e25abdd6f12914e291d8b29376ac95064bcd97a8b2b37bc42453ca123501640bbf6ccba996816ef8edf4ba7770c1f14a55575888161b3a3a944ddde55a036b56190ee3cfde9ad6c6166905e100ba00b4cc047fcba653f03d9e75b787f577a499b98e09bfd7d9db41b3bb71c390a000da38ffe054e608c8be7485c8ccbeeb51134422a1796ede01f8ddbb9791345324d3433c4f815d88cfbfac1c771a75d1b166f1eb0cf2600c9267eeb7c86b095709b22536cd78991ff5fc5257af7770283be99f414c0616e946ef1a64e3ac89580228760e1000c73513018c8c05f146030bc5ec1760ed15fc6d81d9a9928cf28e5a4c1ab7e0c805618ca81641074b8d9dc5d5910753b43a1bea6cedf75109edfa2900dd133c2bed05e7aeac38921f9d899152a61be443c2f34810d0942f7ed873d89642d0e9407ab8ec54d41fbd47e69f2ea92e462b23132af2813309f739f36e77006b35b0604d63e0a9393df05f1a15dc0857eeb591c6b969fbc3f901b9558698110cfc92a3d877373045104d665a488c85943233b4209a4162cdbfcfa4c721c4a00166d8e9026e65f00d7f4796fd69f998dd232fd5ad1b1cd7b27c6806d164e065ae3d37898f07e25364d6043f21b5f0a6043b3886b2a1d8141e30254393dae94cd6299daf5c6c4ccb97f3d1e242649fbaba33827c226652704d6dbf6a105880966432c003844b3299c29b5afe401694759d1d838b9785b927bd1029005bea8bb22f646a2451666c060a7399cdac1f9620bb33d4089f2cc127e28c7654cc66e6b0b968071bd527d751c4f7a1f4c69098a48d44eb86c50a9cf1a2c6ae2e3fce1ab8b8c65c275d40c0ef41729eae4167c6a7465038d834c6051c0e24b4977a4081f9a1cf362e86ef91149510870799347e76d3ffd0d5674c2ae7b3480da255c70b33e78671dc80549ef478c399f4fb0a0618040351e0087c7639dcb6b9760023147f088e70fe27e86d7d9b403777143912659774303a96a4cd0671d87fda3a126321d6b0c73f16f24b3a978c5508d2d6b6ee7620ad693082c8340752f16df52ebedb37ac26cdfde4c5306fcc039f6cafd6bf066dfb61543f8b1504d92f2ba980a260385a4091aed1e753c1e9b905dde98c99855e69b6b06285a9ad3f90ba3f3cd289b2eafc68bd8164fb7a7f9a8b0c16f8567590362b69b62eb02d5f4b28aaff7598de06bf2ea97f1d103b62fae6cce800ea2fa5b793ced560655482c325c5bc3388b2ce029321c4260b7246ec4bb34b58a77ed15ea64974fbd25e877bd33fda223eb4f7c368beb4152e26cfc895bd37a72f58191a8fda21aba68388a6f88d7c1b4c29f8a285a47feb8a51c8b584c68d587cfa47dbcdc50071312413964d0872b28bbedda55d652d9bd1e88b0e63a217de65403dd20297a2bad5ed81fa1495c6d58477465655307279d169c2731e7ac6b610778d1bb96246347bb9b9791403c41758b145d84c7b352baea8df7d9e346013472662392820fc32051f69849f217ed021c016f23f41dde4894e5bbe92a2fd4232e5a356d21f77a1a8c087f54fd4dd50c631184b5e0a0649acc568379ed8a3496876c367ed73cfba5f5670d0a68bce2d10faaec1dc2248cf7219992dd1185ded886526fdd020edb41727e3ed986bd27b1a4d25da6e6b6c341e6e924731e1bbeb5893fa6c12e6ed385a5bef31990119e11ceb2fcbf0f033a43ad5ef4e55818909c82c106a64709e0d778c2335a9d0e7e6b4e30202ea25f18353c45d919ff78a5bebf3be6e162eb3ac30a9fbda84320dd8b4b3d86384ce5877028d4dc8a3bd8cc6865df69413fbbd532f4e915678fab18545a0fe9b3ed26194a4fff7b507d7e1789735a3ac318d78326bf567d74d2fcab0930186ff61f16a840dc6657f290f85dcbb58c62359c92210456c1bd0d556a24c7c0632301e638c938264f778c03803d3648ab4c0e30b4cb9d832631bf3b38402926d9468f20f0cbd5a1aa345bed8db6e8ad0ad53161df7011c8d054781816f915360a69e44437ef6bed573c2b6979d5de2fdd8c54765aa36b84e76d9895882a7cf5d0957f0bf646499a0aefb32fb2853676cf4a9e5b36b74b89c323122e410b5160a4c296a711b1f7a77d11e29433d5e32ebb106bc29d16421903a63fed159a288d6905ec0b1477d0a9bb008475da2291a11f96bbfa81a6a5d548d012729423160eb9283c8ea06061af04e999d78b1456200051ebea41c46009db795341a250b1f76144870f65f252ec662f768bc085a23eba6178a7dde2d66eb764648e6647e84897b39b132b9171b98789ce6dd5c9386ceb33dd1918774f9392cfeeaeb3947d3fd8a084a2817f67f4df65a981d3fce81d4d1d650332e4bf7530d842a8589d2236f824d4445cc9c35f0445314e29b95a3ae52666f54209e9151fcd100a606c0fa8b24230708e7a6c645ac77a9abfaf564d2334b4183308c884a5d87aec913acfbf84a213bd2f144f04aa3e2797b1a57c55c4df9722e2a1d69463d1333abee8008654b2881155aa0090d1a25a9a9359684010a31253b421d22d430055e3b6f5d414db28f06557eb9bc5b48d7a8c0adae2249dd48aa0da4c33c74c4ccf579d1793399ec9035ec800ebcb7e6e9a6509d539ef4a47a7d54a9d1d0f217ff39a0ddacd767e8377da2272caa779a9ec76cf3e50329d82753efd9f81c65e39304a322cd7b64743e93064a6db0089d1550244117a731de52b4b23512360d9d53b1ca9658039ae7b9fe067b9a1c4c573d4cdf526614c16d44f0360907bbf4db38b129601d66f4c30932b5752b1b0d6cb78f1258cdca1551d14855da84eb587c88447ef9b496f03026dc9e5adae49e22322a0d9425d60dd11cad1a34c9f9828dcd6459a23a2064b159fc221cab589683f98aaec20288e5c7dcf38c480238ea7d11480e8f62d39000c41d60749598e7b96a71d6216cbb7e009ba61c62672162b3f4fec17a88631af3c4a283d7a0a54b71ae353c46c0ccba22838c4028d54b9e434c00a6ea631a6a273d36389d9d3335bea9c139dc931ecabab373e8e8d845b2b2cd14caa4801c72ddfcb985c4d82273f3254c7930bb80e81d6885465a2c0cdba13e06f2e99f7957655ea681843eb13353d3d39abbe2867a844ca9a76e563d50593dfc80d81fcc844f46996168e4e4e02d159342d9db6018aa83e8d36a79630c7fa647db8b335ef544be6cedc17095eeb9fc9431273c1136698c4ed77e131ac564be15ced703dcdb9b258311cc093091154425f323c778609b7dae9b849b4ac911e225bf06002b2991be18b7404640c544bf7981f9641c67df839c6c720153b7ab528ce2fab911238d63397819aca31aeb9f5fb2fe9f0654d23e6c335d00b39e34a49dd87b8ece8a1d6003fb0f04455bd09fc80f98e97484a0440e9f12c968afa9ba7eec1c174e9b69d01d0e4e0962e75ec6f1ac53aad914fa7fd2a70de8b5989fb0c01e4fc3e813c1dbe8f6e052c652921f15f2dc20a5c7e4187e112fe2ac414aea3b4024091ff4a56bcbdec22afff3544aa3c0c52ccca22ea1c0d422786a1b5022c51377eaac9b4f9366a1d111c401b6e71e9d451b9531a599c89362ee48ccf29e6967148e641f39e1c04a061036d51f88e60bd410bf228ed58ba7c55ad63d7e8a282d04aaea60a503cfca2b18cf5c8b28284355c34a8d267f0f64a76811123de8882a87dffef07d301380a9a43836e2d335bc04a8feaaa41bf72ed75610223f46fe3be19894fc85167209d55d67534ecac0c205a67233b2d3665e684be0458dcbe9a6ff1a17089a405f47b5dc3990436e5c86ebd00f6820206482e7f9015bdda91002e825cd42e4e7183108ec0bc950028adcc8b2a8c9bd1cbbe1bd466129d59a3ac619e27347508c371479118a01be50cb0b9c4fcb077e5f94b7ddf95c4c13300a6fc68d1dbc06a6c29a4c808cc094c941680f42b08b5ac2f2f37252659b8cf76c39a561c5007fbf5f8ea6096135224dddacda08587ac73c7487a3cc1d90bf3168c9962003930ef7b4584296ed6774405c1100bf6d0a1480cc738ff7e967cd3934701f0f11c45f09d81892d71c82ea12875639d7ecbc02286328b4192ee1a6bd8913b1f0ef86aa17ce1968731f9bace248ba8ff81b6dbddbb8afd7f4a4d0f261fbc1c146683f64f0888d8fe0c0f124ff87aa2c9f63fe17f242d7f6b97084e99bef56c6a10fdca72bfec0e5851580b8e7a493107f4d0319ce468d9ae590d958eca211408498dcdb646c5bfa884e03cfc8476b229328aef6abce82a213ae5f421633106094a6fe7c246ce095c307eea0311f8792e0ebcf90590846844ba0dc9711c3d3810f4591a898b7e53d05a266fa3fefedafb24ceeb2cb1d27efa513104e877511b984260102375eb348f2ae399e266f6cf421f751e779e89fe761120c7c2dc226ee079fca01d93ad62782a4d1b898d8711acf6e6a0da7bb9144f7b731d8fcf0aa40ed7e55348962c6245dd51e85fb222a631c37355c0a14f7ee77a16dfdf9ead05c5a64b1e1515cf4a3ed465234b9d4d675eaae904001f4ed4c387ce88353d429cd7976a37ea8a6e3dc0907b5e3d1a1ba3336d13c30812cd41a5a677a66e1ad722f7585ce8434566bc9140a5e785b6d116e140b2e686c3c2d4ad028d817c1802e59659556370e01a6f62773dbac67dbbec24d46513b5b8e3020e94952d78b404b196277445ab367ed49305a2b837d3cc8c1b912c8a4651f8760cdd1fe00b7e4e418be8a4dd94d848bf58a1ab6080349001ab2fd2c023803a9083668eee92949ac7f770d5ab0a1c6522b3b38f3e8899fd31712bb0e90853496a584502c0465c0ddb898d1c5055cc95c2af76f52c7c974dc135749251420d89acd89df5a26d8a8a590951ffafab0a9e16147ee72e0cc20ed49b649be2248113cbe27616f914549c035d549f4b21f249f76223ee6c7402bccbe2922d9fd785c28c736906d62e50f1f4d499325ba3a82761422f7ab002f5519cd8d93dc184f959d9d49c3244a90709a31700e1e95c7a5ba99fd51c76e6138dc14b37d697243b2f02e758ae9f22a0bedea596d7850f6773cce34c7ba109863c4b1c873b20d40f7da64999b89dbdf73d29bafb11acb590c48cc573ad49db4480eac1202df2acc03f92e61cb8c3eeeed5371097b548f126f6b384cf6c277e9ba46138883d9b18741de5084b811e1b79248cf0ff73c30b12dc22cfac511cc3bab24434ad42a2aa30c408081a7204380a12d904b83caf2966482f12618a94dea3b847198acb9877429596bcb33a449066139cb5d1d5ba8761f859dc943d6a72907f4890501fcbb398d9c03208eef9d90c06a1f64a271feebd41177184de371c8b35a860f8d2d24a90a75d96a84a017ccad5a6a23649d9ab3c3a324371918e03b808eff47306ff5623554c86d5db1cfd22fb6d0d6bfe4334ac9c4018d9c1c09dc42f89493bb8eddac69f8101753f98240179155783a78f6c8c2171f07c634f2d576e89e3b46591cfb6303fd0d8fd4a5e13446fb806ce78d98a564c992cb07bd1bf7034dac2793a5aac67375d7f73aab53ffe45ec997c21eca60ebf1b2cba31950cc761e0f55153a4a8cf039adb956db303301647ed849921902a8cf1b58e25575bb01d28b13af462fccbcccee3b0e511173b44583c2771e8b4cf390732cea8895ba77ed671f2ce6a0c7e80ef9728de0440caf1cddd0e5b3f3b40f07081599143f1eb59ac063709db8243704477b0ce25086c4fc45d08d67ecc042fb697feae34614efc211ce4a69a4d1257a4ccda7255c186dea5126db517d6b43ca74c6b7e0021c408f984fa48c24ba6d0521249dcae7a354457a8625edf03328a85abf489a1748e8588e72e819a5f78051691caefe8723b9a0d068d1fe2c6b4346e471b530b3d02a3966df881d0ad6dba266dd7d069aa47e475d14a8054a9926b7cd1cb4082e9026c94a70466824473c260bae799832b9aa289609c50339859658f023899a806bafa9b51c8d0e29dfcb3f857e76ba1f578a4dd2c4f19e870c54ecbf8abb5e4469f8d09b2d09349ed3b68324d7c1bd97c8f227b7ab4516ec5be0ba4d07cc76cdae16ea74b63deb91f5ca6c857e9fe2dfe2f6010f1843055ad10610388e8b6e4334fcd1aa6942755094fe9fdcaec0c2bfa56c4516a054415095037ea0adc8802e85c493e33325382654fadd817bd1e7e814988c129729ff619e7bd8bf1f146941aa2216af7972b665408f6160957ea8adbfe439f5eff78566f7794e7fdfa5b45aae103ec447f6f1c1973f8abe1adf06b54f84e000aeb7f61f86afde584882ec5513e388c78897a63278b3a9003702126e5c560d15299d923534028da668b9331c190d7fd148b857901faba289af701ed7152f3b9a03961ef778b080a1502261495aca6121ce7a54c5c232166cb6bbceb448bdf4ccf66d0be4690a825b424a76638934153b8fe00fb3200a42d41fdac1100b4b469e3b663f9ba81380111963dc2a78b3c4301dd4bef4f7d3faf9d4e7e5fbbf4f552eea9f6fb042e7f085b34f29fe09bb7f4b6e7c57e64495381bd87968b71c34f219d4274ea9c621f2fcb31a3ffb23b86a7333551404080b8505e40327562959c56e366bbac2275139b32d34619b7456e009374841845299a6400ab222d8b79a0328d4e09e5f5be19088e4846b7d3473bbd4230ddb70055c46d3c13dbc9aecca9eb8da1140b34a2eeedeee86ef916551446063d49f243bc5cb3cc8a1118bee0d0c9ae11a2ec0eabe470ca6901ffddb8e6d2ecdb7234d86040bcdb0e3485e4036b574a164ce8d0194cae96e72ce5a982ee2b24a5d7866d3d876463759cad58125d2bdd40588a91cc0599d7df391c84617bc7897037902f49af1f2aa28c36a09adba57b40d8ab3eadfc4adbd4974a78af19f3f0cfc50cec978982b48fc90b4f06cfc8471ba6f5186c3461f2f73912d9f208324707dd26c3add3e0c5dba40f2033b082af8ccdf321f2ec8321b8935b5100c08607182cfd2c62c49e6bb004a66b930739ffdaf4a523a1f529537b1033df340d2c9589151bb04755fd06d48616336654bbf28c7264d391a41c20f01bb6fb59a3782cd0d7882e456570042d32fd0391b64e1a0b608cf22f409d03d9ffbbfb664f5a1628de684da0449c0fb2aecd4f3f1e01c9c5ea0cff00fe27dbb73ca46709b536803d51e18c4531ed878b1e0b3d36bc650620ce54d37375093145158a52cd797e6c991fcaf57c954ea4d830b21ff735fcf0cff1206a8bc23d22fb8069c06c172b3dc047c76a761ecfaa644f899de24760f227f37390c3742292b06109b12e35d6a6fb8beb5ef402929187617e8843f08cfc8bf02c1258d0cd3b59f8a4626ae699d01732b527bb1b1defdc52ee0b14bea08b099d34bfb0dfd4aecc086068ad638668143a19bd4b08e6317f0c09c638175cb6cd59911b4176c64494c20b57ac8585e74299c0d417f36dbaf0cfc5ca2e428f59a49b074933c6fc9d4e2aeac3865ea286221d954e996fa165bca0030f01d4ae9949232977bc7bb9295a145049402474bc6fd9c880cc661efe8b96db6e58e77b94b62f81f7a8fcd0031007264a63b930f8c08c4a51f221d4a195f59df36d0e2bb5bd70ea52485ca419790d6c028ad6671f24c18a52813e94d04a9422200e8275c0264960ebbc5f06e55facac12dcaeb34ee339a06fbfb9dc20c59f2a69261ddbe1052e8efaf11f762fcfd4292b67992adb1f164f4a50f1666288bbd1940e112dc88a83980cadf5d9a1358a0bc6503436f52ac2442d5ce85c54fb09eb349d1e96ce3494c4532e1bbb51b25d2fc7e95a23a09d0fca1321dca5ed1a4ac6dc769d2f18af40ac9cbaf375c166534db56b6e92b90d7c720d552f3656e705cb1956427aacac5a386d73f8a7334e32175eff4284c35915ac3117fcd82b47f70825d0c04ab44ea091499ec94efef532130b718264652baece4c45890b15aef8b971fdecdee38f18dd79637f4dd11f6b9269a2a61f83d03944a2e3aa152d8156d8b8f685776b6bdc60d18c0a91e204f80d4958ddfe389952062150bd8c662ff2d15a5820b5881806ee8cc7c5df5f0cfe8054c10f562f4d83f2c7092afc696d3241a9000657dd49b7bdab05035a8208cdf411360530990f21cf5f4b74808d5ab623848783f3c392768e2e0b18ba45b8c46c7ed424e52e51458f4c77ab1c64f647e3911906d5a285e75731818183ecd2c8c84de408ca656d1a3eadc2d568e0b443325b8e0ce53d3d8f1ffe701f1d836b15cc41d98088fb0141c103d2aed0289db6632e8848b9ce1371af96e06ccc242d35bc28018430aac6d9a95f61892f00d143fbb1606014762a5a521e11a40ba8a98784c42c55c4cc4b5c344b269198cf58539c24dfb75c13837697cab2070478c5474887001b5b996383d76170d2fb340774c3f803195508b7d2625212dff8f2d47c35c122919b51e778291a9dc1237ae7a39be185dda57fbcc7f10ae56db1786c063c023f9431f879e778f1a4a91875d73d1a462b3cf74ec44d3290e074d93a19a60c10410fa572b48830a9e0aeabc711fea19db8283628560944424414e5f7d354d3fb19213b350c3005e6f6cb835c9ac6c014d85b355580f252170c077994a22a15fb587fa75fd550bbf810b1ecb875b6d3dab6305b42ab95832e396ecdc5604db305a5603cdc978d19fbaac1a66ab1b9485fd1e5b38900b2d7986dd74024cf44c831739e3ad5f74b8ffa8d80695920487a3430cec2918f3778b05f07c876d8056133b3ee0dd70922435968353fb2bf74860416056c8d08c563c227b23119324b3efe732fbb61cc06ad99c5f6aa5ee4cb9e42a6d1994f12127701be244b7593112f15e84a2a74d2b61e362b87b920d3700f2806123d2f5dd4a2d6bb5d81375b8a19ff47b84297caf77218aaf6173cc75f808799b8f113106f5b94968f229fea74088d9138c8231466e7add9e94a2488d5f66ae7df18b79697af911090a263370288218cdfa00404bc17d498a1246b83172a161790075e9eb2f29a7e2592fca2cadef1157a909bd20cf53d85dce7714084f8b967317a8f60393205d9548c01dddbe93a0a67d8aa79f6af8dd265b86f19f97da1e51d36ac7638d27841200be394e91722ae411afc74302af774365dea321f7cc58c0aaf00b70fe7c66f6af3db2394c733501c7d949c9b07f6b8f4ccef6d1c46815c45ce394dc072ba881db880e898bb16415e89d14763c9090576458697c658ef809c690b333ed1ef9cfaa52460b7cfa02c9e7e4e6ac30f016f86a3bbc6d7d58637dda04a83c1c8e494be13d2c2d3d2be3dac563a621c1a504013ac2e02548486caa5cd9c9627d1f1a87e4a840744bed5e45a40d85c296e2a9de972e5e388a3156304ef8743204ccf1e038a5359bbe42e903ccffdc1e7adf5fa3c7c406b48a159e94018745279a2e38374eb990aa411baa17081888666c0d1317969791a307d088f83af3b3ee51cc0a4dd678cf5fdef27f2091eb8db84c3ec23356bfa0984f3a808133cc223f55fe83dcad6ab57c5b3327ae457e1e31eaf0c75be6d226fa84d3c98b8b16a5ab565d44953bc88c5629e01c5e2a3852928e198e2fa28553ec6cc8dcf7eb1e67984938fd0926d3a7fa0f34e23aa03c32b3c13b6d8acb1da65224c0849a844c04305c6265d0bfab7b06dcc594a2cacc4331d393314c61589d260375e9684d203b1b9e52d5223f2610f4501e8c231aebaccf01f957debcf2168f3b75461e4270b3208e948df3333020f970163836ed83110ed1ccc77ec168b914dcb7710bba2be00c3708f33423ea171a0025fb147684db4f6b9aac4d8cf9c9b37ff5e2b62e8672aee098c3b5684a4c205de0882fabf299665a17d306bd3c32e6e387d5e9651faacc671c60d161487d7f11a6c00e942d63c99b50f37de4a1f1e375cab42b92466b002e094ca12c12c29585cb8a18a656dc50d954c1d8c08007f92c0e40514df5175b155ec4dab33f365bd2bb51ef239f5bfbc0f52dd283959dfa82fa7e137f9288c112a08dd94e09493a58a071b742ba498579b2f7b9a85ce30887c66e68644e80633bea583ff7768a388bcca66bae59b2571f11f18188a408476ddb4a84f13c594e6f6394ace46caf3a593a81d946a5dd0b536fc7da6c34020bb664808830ff43d43c5aacd6a93aa3f505185e355270fb78642e2c4f086b18709bc9c38e39fec8934aa6d007f68c4c45492b8dc88c4edfaaa057d885fb9f37d217410cc2bd413f32828e5c65508cd9bea2d93d70d82cdb0b8a2f3152652dc368465dc8c6a18cef66ecfe06b53d483c908ba3ee0975eeda0a504755a5327bc0cddebd423cb8ae2a35f2897280d8a383231e1a4279f4bc8a884dbaa9230836f83e3fc74d0b76538490a241daa2583303f4766b40161add0d45405cdedd8d9bf0f035f16dd6dafb33f11c670df9779966cdc5b7b4119e15c5a3ef6d06fccae0153f3f8d60b4daf6b36bca6d22cc72dd24bf9862bc6c1d2289c5fecf1f3d33ea21a8fecca67400bf72865fdd18855158edae46044d12ced4a27f46fe80da805c18f060c7785d86c5dd9ceb76bc5ce9c74a0b5a40d72da3ae90fbd63b706e4eb729154000718b49d775df3f7b0118206d81061d56cb080e02a1316dc9368db909092b0f13dc59791781d9718192ec5e6166d0266eb31175133f9e49df046f829cd149944583295f4492455217bbde8422860b9fe3b373df97bf003bd931a32e786b0ff97e5e0096d1aa19b8268d4124245f50b1eb2d4a7cf8c571b66acf7dc571b0829f36a78b6da2e89cbe020227a3388071805b8b26340a26cb96b5c51120b54a5816dfa01a303f8eb1734573ae4e7525ec0a607ee674a5ab162668d8355ed6029c7d3830280fc3c988a11d13deb5eabd183164b2abf9cb1048f9e6e45bf823871e785e2d064ec9aca8a342f8b4c25f9d5da8a0902f65d557fd8d0c332160105301f894fd76721518c8819c7c5608291f9f53deab02e5f3ff8660549dbb9d982eaaf1e1ef8cf2ca37dcc3f35cb844ccebe37c637feaade36c45641a89533761bb2b4feb2eeb403dd84396ef190b30599550eef8c44336e8d67b7ec42a1dc4bfec1413c725a6f47933c23f4b99817a1f210fbe18b18ddb8997f90f173972b60bccb49a29bf9868bcab55e8cbb946a27cc7760c89b30a76086bb95eef3db50963cc16ce8ef4c21ec507decb3bbe1c6ec1d55561af8302383ad875cd79d469d7befbeda24c031712cc1f743165a5270abdfd1565357d2d5d02a9f8df96690173190fbef66db72e3bb22140255f557c9587e6d8b096b843e84c08a95edf13422ae0a6d8893b6cccbd352ddabbbe2084b2660f34921ae2cf0d8e5c5d1d334495afaae764c8370092ec233ea0d276a80bb1ceef915dc50d23f3aa2407c8d176ab5863b8a7cb7daf115e95196952d41a19c9013c972411f7cb6de5740d1705cd2fce82a74dd7792ac0d2b11645e67401fd99a39735a847012d04b1eefbe821e59c187cedaac56e0ed225eb2da559dd89519593ee763ca1e6e7457af54ed978e306fe35dc90127653ea921d7be86b31ce674f484d777f372e504692c916c32293fae7bc78abf1abd9b52841950a76eaec7f88618bb9487e848355298172babb8b2d46e4a7c49db7feffc0ffd44a29451e6db124432e2e315dfd051107a0710cded87cbe7df3bc1dd379bb6bdce30bc0a02548f95ca5a320a9e40a62168c1cac1bdfec337ed977a37bc00aa76609fd365de9e0915e19414c035e1e834036cfb75fd33d7a010f5a428af7453a0c48314710b1c1e862df8cdc4316989a25f8bcbba4434f8ab3822805fc720c01dd86257e676648476fa95c11c4bb008d25ecbcb5a4c39d145b05914f79473278d3330bb16e8a6ae76ee0556e29a265cd33fc1982f57c28a67ed45c099f0839c10e1ddabfd2b17db4f225ad62540d58acdcbd5adbf5583d629116d65c272ec14dfa26ad9579f752d96a91d503204290377513c4ee4d5649506d41328edfcdce7885a54f4736c459b9e11ccce7818c46eb3293c63ed57842b2c5b75b1b6b92adc65c166be558b72438539febc9dc7b8a1fed53b60c19928a5c08e0568f637630161afcc789a42266a4bfd328ca01734f29b5684925a0029c81a2be5f06b9889a64b4da2734ea47d4744097c39b8ad0e8fe25a51c10aac6d1e8968e07a8c3782cff64e852134924fff571ee2c34b81b08707ed43520199546866dcaebad30edd80b389d017dde306fe9263f4dbc432100b122165949438a3a43c9dda056cee124f616add386098c66e38f1d0611a1c958f898887f8177fa15db3fafd0ee987199b0867d1ccb2beb8b9a8aa44f72a4ede4bb2875e94786ab202cdc046305394fbfc1880ea539880bffac7654c6fdb4f5b46d1f5aac6bfb02d5fe0a90fe64f03faae03af8d3f7431a51cbc86a9188ddae6b5e626b21dc469c98064d286ba14c9dcd8cc62dbab9c21fadec41114a025eb7f8b77f6724e2a58b06b79fb960c7ddb4bf9bcef1e8843b8d69597564b98ae16ad5531810e4bd164c48b56d78ae4f6c8420676254148f7fe62bb96471cf1474891fcc70ae5f943d8a184da8f3df9a19db84e2745df12284e2090717fbcf85468ff250071cf2841e0cb5a8d281037a59c4ccda1d463a87d2c01301ac5fa0118fc348f2e64d0dc795177ca6cb0d23a7ca2037e36b7f0439df55fd08ea1ce65714a99f89b7e61a8e39692c360c2aae4e6433d8f543739bd4b7459b76e2552bc9773912e8b97da04e8a802d9179193944dbe109efd4d5851674553608c43f42c4c69ef46f04bb3620a34bf26ed988fe137abb6f74a547ca974e67c367f2b17a02629e1e836ba7e304023e37649e898c913071da975e3af0eaabb15a9f79a2848f20f1afead8391398cd0cbb8d447cb32079ba37c498f2478d9c1bc0dc5fb7868c0b0802967103da317354671c102348eb0614554d960acff501684945c9e41adf6d772748bb62c4d3e77e56b2a5ca69cc0078a3015887f7e73ebf05b3df2351a40dd90c1eb82eaf359460b0401c7755f87624de0ec316af001c892cd124df742e163090e7057f4fddde019fdfdc7c4518b49446d66e6e2458fa325a3f2896ff40fd844bed76f91d52798d4415d84f0cf3c030db2055f88711b27cbc3c08f7a4e59bf2697985f2b303610aaa1e185dcdd4bf3cdae78c872edc59cfd4f5f44530a2c9f5f228b151fc865717da45d8a0e55154bf0e3eb06d70a7fda6194c12a5d15bebda32293f5452ae9ccf2fb0f810535b1ea9d5e8f7b24c7cdd7ac994c95231f54fb23c50ed592388e58125a67654ceb71970bc26c468bf00a0cce4405d66c57af7b1e0a610ef560bbcfa7cf280769bd3a6a3f4c311b1938fd7e6ea0644af79aec46077231685ee032533dd7443137a47f301bcc3058e5585c61057205c914d3ac9b0611b5fb6f3e4694d4632024212c09152eec2cff10c394c5c8506e0d55475135a83070e2c06821514200065e46fc5d981f0ce10e6f551e0ac28a3929b81f6924165de8c5a3bd41a216a8d10b2f7967b0742112f1047102e4ff1ee623ac245bf5ede306f1393975c22ea4b0e5e22c0abbad225627eb91281caf5da2d482f1152ca8c24b9be9c62a024f328c930ef66c995aa3b613010cdcb056a9de676f84a975bb3cb9d42bc3b7db0a5a1b9318d01bc1ffb5ddfba5348bbfaf2c351b7a0616bc462a0d65bde3e4f00ef59bf57e6df96cb431665d6b7ef66055bd994c1fa008624f113da20e63ab15a295759235ff6455fcadab546ac11ebd65a0b84e230033f68a7ff416640032d38cc1ab6dd94558ad5725627735c174dac552c80ac8f53767542100c8901196418901073bd2e01b4b229a38fa3b12e3f0450d1d32f16ee634016e59d12162dce21b93fb9008e9dac93f58bc3534827a45fda9475db6b87b4383df1eaf4089080341413efba3a334fc45cef5628343fca3485c6fb1649077b0590e63437a47f5dc65a37a4384440eb3b3c089abff011344ef31df0114572689ce647e8f01608c0d777787d071bb9773897c41dc92f4c77b8d688ca2cb30e58521a5d447b48afc3776221c544e4d0b80e5887064e808cad7e19a3f938939b062e422f63ad8f606e1dae8cd1d8c9363d9b90311a34b02d02eba93532d2228ad10c4a40c76c3502dba88db458af00ebc76e9de6396ac518c81ecdadd255dff2720e541e75329d9debaa3c1113afce9c95810c3a887de0452c04b48b2e6245ea6d91ac2959316d5439c5b4512fbf1b33fba05d7421a50c7bf97118a7b386815aad2334ece088046088411384b045ac6fad7dc1b43ed3467dcfc01c4c1a5828dad5afe2bbd1b9de64c574f5ad60b9306f606dba52a1a85fb69fe81795c95cf7e4859878e519b25082a158cf286cca4ef2d8b3af8b1aa98b2a13c0d1dafcc005f062eb2eccf38179033400b55a6fe18a248774e47085100338ca222fd75fbee86b3d9ac2148e18d0ba78f921a07511b71175e9688d0cd5a66b4c6c4d591be9775318c9c28811a32a72df1ae9d7c422f70d1114986000c72e3a1aa7ac8df0e8cd93b9720949a8014f4c7e434c998cab4016626042055cd022074a30a0750d09bc4c8c2bb58ee3388ed3ba895bb34646306e45ba5d846493ec11cd47fbd1863422ad4833d28e34242d493b4261723bb269413c3e455a4fdb22a421a15950ee9665d4bf9f98504ade512bbcb8d7735ee972f7367053c1149080448675511be5fe16542aedb3b45a0b9e089b4d6ef39c53bb262a13d35752fa6ec0cb6f5e75b512c8402bc14fee3af9dd68f5a5db3bce362ab921bdbdcc35a4b88b8c64130daccf086affe91758a20df901ed71589f598247109c59bbcfb6a846bf6acd7648bfb87ecd9927c77dd6da227d45b9f4a58bf2da2f14140e757a1180a3edd74ad60860ca9ad4bb289b4c9369327349d36ad49899794b74c59ce251fbac3123fe31d775c5dbbaf6bba1bd7472ee9adc966ed3d67e39505d74d14536b9324f93eea745534ae9d5c4c4528d72745215056909a5263f7977723b2231004d3e763fb94d4a4071e2f11cd6f02d0e0ff6ac5379cf54de55af3f1c25957b7a0ac7e92aefcf3ee563769153ef1937bb864fb1d6c94af0b8b29c15e26e8fc25928ac1085591e219ecea713c223938158a6c223283319ad14b91fbebefbe988f4eba45f73668e03c113eec49ed09379223be9d96c36a3585683c3e35317c71de590ca45a95c5411085a9e16db161581452a5bd4451303b58458a11e0e5b2042b9673d9bcda8ea8e3d4433e1e852ae2dbad688354ab92537ddaea70b72a303d262bf039257b83f41a0d6e96dbdda5b174891da599f5c6fcdf3ab250e0fd1f00f9fa43632a45fb48828a8c0041da4c18a27ac701e30a1441349f880800420c775518bad696d64ade59a446b236dd4a0ac083c78e590769257ed2899bb8eeb39e7e4e991f9d42223d4eb25e12cc9fc4eb37514066aa1bcdbf4973a89c715c0c3dd3efdb2b854c1bfcee45a648f3abd3e2d240ca0fdd8325917d11a356c51178907f1b85261a969da3528fa557adf0ae5121e21704ea8c56e212deb97f746599496bd97be1c2854888375b4cc610b851156c80af58b04db21168a7ea53050eb7766f0cad39df9bb337bb29ffe0c9a3eddf57163661c3203c9c00a57bcc0067098819008b880075b1c99410a28a84167ca8dc5115558c1c80c892d6881060b04a1054832981da10d5cf438f5e4791857c662ae8c752622e0cc13b1c986341015c5ba09d69e59ebd35124b073cf8cb50e448901c45aef9931d747eb23d3d989e914c911ef7aeb32e774cc8575746a20440b9e9878f9853a5a38ac6f5dc6441c56acc375f132e6bab6e85a23eaeab7ee2401d862cf108045d6c767825c57ba921e810b481900591ea10b3c946a9a566b1db9b17467b63eab15087691cf94b5d842e657a4699a01340c048222f5ad5b6b42aab847eb3385f42bb4b9e219a4c5f619d2ba52baea0d99eb47efc823f310e2ba673e143665d4fa589fd56aca7c2c9122ca712048ed10eb43613deb99f5e959bf8bfab5e190ca34143983148c925a90c40522ac60820b1aa440073038c1b54536092422c5134150b8c1139870c54e1129465064094b241121c1490d808480032daee0794217b0d053062bb4e831d28521ae20528321505841c408028e202951451bce1044e68026dca008478650e1c80c35ea6d68d5ced04dafa8439281b146b93faed822e9ea97fc9047fb2bc0366ab18d98011cfbc5ad44b29ea1d84e109688c31b9c48ea811096380242145318431247104211fac5ad3e42530cc5baa8b1c83866965286be3565ad2993d9238b648bac5137cbd2e503243b2f3d7d205bc9a45c75d40b616db45b6baf0f2dd63aeee46edc551dda6f098f3c7249876aafe118b6da2ab65823051d051a369b7a43342edd182d42408c71d3d18e4749a6d7512fc4110474d40b41613b4d1cc10a21e0c00a25184921566f53ff43c7518ec3150452a464035aaddbbaaec3a309ffe0438b75c559ebc30f518040902e99f31b76205d325b1b5315607d67f952f10e99d465a1d16dabb7f5da476beb6dfd0edb739cb65f1ac7713d358fd29ab977e6f0b8d98de28437ec7128b55c31976dbac6e172455d92db4872a238d2a2cc3b79e6d35fdaf93ac190b969c4853648a1096c58c36c89981ca283236b0881133b08c21790051648e8d18112238062a69bb260d25c39dab3a9adb427adbdd9be53b3b4ca1a49393b658df6b6dfde6976da4b69ded1a62592c204435a68c145902c878820451ea711f27c8c2c8ff0862cb46bbe481d123653832b015959ce48980e1ac393b02a5df35287c41b96454690e41841884cead1a2113f5e008e0fe80204b9b9f72d75db0c8571dc2c81320329d2f54c8ffbc3648de857e3f11dfe6144bf62235a9cefa103b071d276ee86fba603907b8f1f466c5f01766af7bea3f7ee33a8c7fd614415c0399b4d6c1f67d066a2a50e9a4fe4b95d7e1ac6a9d9d6e9e34d9f2023d0740538d66c02f0086f9822a7005941891c3861055708926690a2062e00021741d2b082d87c0f237828ecc40a232924110532e8a00b151f00e1054418b2041154109bef61c49077c5145308cad547beca7eb27c1522eb902cb5a22c6faa015865143691384e5669f02387aa1086206fd8421256e8c102b4804513a690042de4a006f29a263f9328acbedbc9a9d7de38c77ec33aa08ea4ae9d9d1cee1ad629d2d7e972744c2f7d27ec6ff5da83b0dff011dbed357c44919c0d4bea92b73f425257bd8c4d2dc20e1b8708e0ae3d08ee1af7204ad7aee1234cf263c0c673431df5610264ac3e0cc28928f020092d82a616a0e9dd7f7e78a68c9399a800ca5878748526b658c3cf9cfdc81889125970a10c473cdd9d43d425b5004be75ec2617f2726a96b16c5a6d1bc82fb8c7197b1234cda7118c4f650c7f604c8d88683d08eb0600c308001110f4fecd425afd3d3b1ee1fd8aee1b05fbae9e3740128632916803246a120c10f9ef0842c78786232d6e1233eb0611e1c36c619b2a12ef930d49143b11883109e0cc4610850979cd13e639a8e21130e73340e571fea92f2291f50c6c220666cfb8cf5a02e294bb8caa84b7e9449dbc7f6910f1b374fffc8d32d8035a8ceaa5035aa4796c7f65899f5b13f36c8ceac90d6a3c93421cde7a7220d59a3a14a247df258651acf9124247bd4b59415ccb363f6cadc18696e71a7162d4a6da85df2520eb5285f65597ed486b27cfda1b00df4e0064e0cf9a9410d881093af41f255265f7de44d5300b5226ea7768a083f796ed71ee6681c75cded86391bf89d98fc48c21bf4f46110dc76ae7484f6d2b9f14777bfef0e8a431c55eee3dce2223aefa0974b5081d155657ae5125410a4872acb976e188456ba7619e36e18c411dcb7cba13047e71b16a254838639dbb9735847a42eea9ada3ced91f9080d1d21254d990f5292cf114a4461f247a87f286cce9e5c8f8c6679aeec8f6d0d06f2a62a40f9697790c8c11744d08524d880441162f22285a582ba18c111194c503484983cb8eabeca92526c9cda9a94334440199331f909018e358410c39ca9b3a22ef98939ea9245d4b50570ac91a594524a29a594524a29a594524a294f7d7ea494524a79d30c9c2600c71a59fef2353aa98ff451235959a50205844ea0040d78d006232b6881133438c113c2b04419a2a8020a82e429a7f7511a218216b9a5a813fb9879fa40519435b0ca3fbe332701bca1dc53da00793623e4de6614072d6404da4e089db2a90a705ee66dd6aff9392906f26ed3d30340ee6f4e4898ed91ae7e3d0f8c82a2b03121f711720b21f7654abefae824b2c69efede45b12490b0b62a7809e5eeba65ef86f3bba1f6ef3bb1ce5e1c32afa44c38768f2cd7eec9e9ee619deedacdf1fe619d1fa86bded3842efd16c0719b6ddb4cda8ef05098fc099a51580ff10871434414268b8a38a3a059c9a85f12ea97867a8eb8a30d89c264ac88e8c747d693bbc4933bc908d82098628fadc477a333eda1b0eefd964d1bd3543465f45b051246a5205dfd991918305c5cc23c52a2308fdda3ca63f7e49636e6c1f6ea77633b9202588fba15e0587f260e11d0b10e531a0538d69f2a45bf68307f66206bec5743007b90fb72e620f7a6699ba6514a5f850f0c044df60885a1febd4f7de84f2e62bedf5d90b05a5790fb601a78c660c223c7e1115ce171953deb1dc96dbd3cb19559564b279d74d2ea699ebdd70304045d695a6bc7ed666e332d486ead277797794c53f6898e45647b44ca74ac933740e90417609153b274420b92f277e316c1651f5b1771e09977de53aa03599190325ed515102177e5c9d407262119db89f5b7a18d682ba29b91ac99390752a63f2f0d9a32fa1deaced8140188246b6ce4a641421476fa3693359c51c69bd0362b01d85966f0a78f346876a3030888c3799d93609d0dfc4e0cbce13c8951384ff29dd85138718880d3c1e79c0e7e1ec43a4526ce014f02629dfe090fe5ee368f863932cd1df54e57c6c02b63fddde8d866d4153345016c1e2923d3f7fe7d1e493ac5a622b016b5d8591c7d9bcd5b8d54a31693fa35ce6d8bedc8c645bfecebcf764461f5679b6db36da69d087b5ab168b18fe48d8bdc3c990392fb28a4a4dcaf3f14b6813568057e75542557bbbb7b9b5157a3ae8c4d1280350b5c8f5aec0904ac582040c64e979f8eee36ad324d00769a37af29534a6f0bcb0a2b544959a19c9894a8522420eaf4799da9c46d9a94913254ca48192933815030143e75f5e9da1975f53714846910a5415d94c7cf6d1604dc84b6274c5000e93b296aefe636dba0c8dd6d4fb4d8b6c7ce68c90a5c657af93e9a3628b63db3b9206b26b64b1c019c9f69089c1f3d8d01d847d4d5ef3eaec43cda1eb18fb8b18f2c8d75d8065197e909e0b8cd7a72b7cf2c77d7d9576693134abc6da7d4304840ab949c04e54afef3630cfb69717b2f98e91674f265b4324bb74b29a594cbd5540528b3890828a96bec5998d3ddbb87754eef3a88bae68f8e501eda4383e88c1651237a4491681285d12818b1420a7a84c26451e5e931aa4714262b524da242b21a446192caa80f85c998e539524434447ff2fce97e573601a402ecd7d3772626400fefc45a8b3e6aa43c6f620238f691cb09212534d18534f0a08a1e3c3bc8810da4e88115d8705a343b15610811520cb048818f4b14c6008621f044e102354c898532d8400838e0020a90b680035218a688821186f004f9c10cd8881f5700516eaf84370465d62996f9069c8d32293f71729b4f4e5ec72ac4c78653c6c3c3938b9879fa00c1c2ab2e80dd44bf5cf038b3cb65b7aacb53ef5c6e37713ba84579ca03badce5e6051be0e4ded0fe5ee6d50d2dbed1e99079c5e766a5b2742b57e63a5bef2afdb1827b802f2dcabf45d93221d46bd55e39eb26ef566eeb3417489195d6bb940ac0d6655e396ddda4b5d26ae1182dcae7b4286fc41559c4c23446fcb032b05970ce17c0cf00f41dbefc76983c97fbbbbcb4de6ab5d8fa3873eb2bf62777476579bdc5618e3ecbca9d99a57582c7fa22663e39cbde93d7953bf30bebce9f137c3929d3f2d52d499996d35ba78c96dfab4d192db7a12ea30773471e5e0ff96e4cbf4c5dca2c535ccd3ff7e5f8bbe9a56e3a2d9dbe7413ee9f2cc0b17db27c577bfeb4b4f45b3ef6cc7bc98206594852a208464bccb0084a83142818720530c87e07863a68681e06719ad3b834f7c404161481831344787862344ed3519a4b83d2d0a81888264673e5670e9657af8e2be7e1e51396133cd67c7296d33bce8ce3581b90e91099de874e8cbcf26e56d44f7cd01b2b972c1db5b22ebf1b14eb2d2d1ced732db7667a79783fd98102a990c90a20eaac1797a2de75a85b4f6fb39ca55b70b8721b8afa917978f98485c2a1acb7ace010c7f4ce2f725ab4b5da6e91b9b6dc9c175766890690e784e29c16e5af0e8d0b70944542e8b87c7788d3afaf8d439cc63c78c8f53b99070fb9bf93eb9d3e93488bf22c770a6971c8c99d3f1690421b9c4086239820073129ee40082160a2044928c117a68cc8884924937868e1712753faee20b4565abb57ac2dd55aab6d7b57deea5a70fa54b6b83ac563496ea13b280e739cbc4f6f9ce091070f99f5febdbd7775c79ddc6261396179b73db9e307b25db932b3dc95b3dc95dafdda5808dbbd626df7ca6dba576ed395e576e5f2bb61398b6c11cb16652b2553269b128f2b348024ddbd44773a27273e683e19672eb9eade7ebfbed653563dad5dd7fd57bde9714705d37bef55aa7b55bff75ed5c989102740345f752c77a5e5c2bc6badb5d2d9336dc8d6c719248fbf7d7d79fdd80a7263e693cb86d1f8c8720af395fa5a5b8ecf3acd0f9a5b6ebfbab245d62ac504c8827fd0cc5ab929264018fca333ebe3311e2516fde1d8415f330bef606135cb0b5ef9d8c23f3ab37c058f128bb9c4b4d18277b09c75cb8277d015fed1f9b29c7e402c31653466a927aa77254f45c5e45d7734ee0eef58efcb8247afde1d2ecd695c9ab75cd60e3b9ce6d258edc07ac92cefa8b7c3a57196bbf296cb3acda5f1f9e15839cc6d39cdd5c1a37165bb4630af74e8b7eecc2cb76fb460d66d5a87eedaa3b934ce729b6e5d9959b7e51dcb1dbdcc721ab7b68be52d57b6c8f2f9dd58394b4bf5d6edcc724daeb272471355906cd31b13269714e5135c850fcfa27fdfbffcc5e518e6d8767dbb0fd342f2fd84942cb7efcbf2933bfec827f2cb514f70c8bac5630844cbdbb2304e0b962d36b6f4e3c953eaca29ebd4be597ef26e09591887054b7c7293d6595a6f966669dda659708a09d0e2a334ded13a0bde01f33e0f8b7978b9556172a47060706df1057ff5c6a30b962de2d7163155ddabae8aaa54f6f544f5beaaaf54ef133cae547d15fe57fdf77d5356b75abdf657af57e9da2aef6c4ff1ca270e7152a74f9de2d194593b580f6dc87a18de9175128667d1bc05b39ce6ca76b1e0b1b2b0dc95d3f0293fb136bcfc70ec433cb24ea44d5b8b4f2fcbeba51f0e3dcbdbae9cae9ce01daca76ed32b7764e1b06556b9602eb92926afdfcd0ac51b79c894e6b03ff909de4169a7ded744529955293c899080770e913fdd49d4a2fc7767152d4a359840ee43a610c90394535c21cb7228cb29a85094bb1e928b2c3f9b48966f22fd6afce31d24df83c274f4907c13c9f7ac85e869a69fb7672dca57286cd34b69a64034b9650fd9a345397fe46790fc94c94f9f2ea2e9a9831e74610ebd610ec5218711303f30776637a023a279b29de5facefe54fb4361ddeb6d1085950ee41dc874fb43611e36c942799394efb04e118973ba9bb08ef7ee3f4d5c29e5c4405f4c5e4b44c6ec4f7dfd64252b6bb19aee114572be9f7e8477925b59ae07eb57f5b29eabe7e1f5331466e2c055ae62677fc09d1c0f87f3a577ef9e537af71cd3b99bce9debb87bc4e9dfbb7b44919cd3bf1f4172effd8508f04e72067c3f79277912df4f31eeca5847324500d6af28ecd546aee7a49424b59ea412e0cce17c7719f3de3d08ef1d3ea2480eeadf8f00ef3d88ef08d4bf03499d3b10d5497e64ea929c6c10b6443e8180f2a3fdd1c1a1c05b7fea4eb7bb477d273613ced373ba9f3e7f3a1d8575243d618ebaea753a2ca98c9d2475d52880a09499c72133112b2933bb9fbeebddee74e50d73be63aebb87bb74c71a595ebb1c9ea12e693b3448b9aaf11a9c3d3dc547d05bcc05a11d61af49393f72f61ea11d518f802f01da6d9863af7134cc99b65e3a9b1e75f265dc8a6c67727991c2549fe14ede3fe1b81f2159cfc673e4075994b5e6c9dcbb1944e9c9e4ae6ec955f55337d4be13a31c883a79d7ae38435d54ccdc9c578032a61ac5cc1d69913bc9b58f1448e6ded1abd126c140b4b9e82314a6038f9c8e16b9c7e8e56e6742dc6d1077d5b5a12eeef60660987324cc49bd9fc256465ddc39fb9339ee56688888c26612924da23029f3f909d2664214268bb4218dc887c2b86f3f1b11f72d88fb36b347b4a29f19e36eb91f141e37a1cc5dd24d96b90f8f5b4fe63c3c6e3c99d38e64cec495f0688f32b799fa5ba223e2c7cb688bb49c6d15125b22fa63a5c854467ffa34a84f65d46762131840c91d9d3b0aa2861092ba40f905d16298535a618ebadaa683f882d061ba4dcb8d7b69be9b537b09eb94ce618ebab4f797c37dc33adb39acdd515c713a66a8cb9af08abaec915a487634d60873b8c9b3132bdd0dd3db3475511795d11915a246f4a8f2d49e2aab3ef5a7ceaa90edb1322b647dec0f1285c9213a44617396bb8f24e5a64554c8a528a02998325af60570de7617872224b8a2dc9f72da30d121508920b9933b4039940417a5201ad4ba0ca2ae964dd73cc781e09c41cdc9caf3f94cb500d29bbcf084e6c9146b432d6a4215077306e0cca3bd8150e5b999da1047575383a2456d4813b24fb94d4bc9d1151eb93ca9bddad0d584b4a17e1119d2a0d086b421a1fa9305386a429a9026a441d1af216e0b4bd995c7f991dee436fda2750ebc3849e5e1a997b9f5998275bcc032e7c8a1580b379135b9f4e541607d0bc91a2021703d045903d403c65aefd32359b30559837a9f7241d69c640dcafb15095973f27e0581ac297900983812ebd72059b37abfe2a0e640d684ef571dc81a95bf78bf0a51582daa43acd71048da05599be8d7ec897ed1a7aa0aeec74a82918e21b77ca469c8bf41c761dad0949832c820611afed86ac82e1f1b0eb9df58c81a0d0bb2260b6f2dc89a526f41bebc20c3d030f8058b4a4c1b5b0ca68c1e8d879ec75c1fdb08b9df44900270e19e55ac75173c82b8270b8d1aa809dd20f796445dbd151551182794c2a3c9c526c7a309d8824790058fdc0aae452cbcba33734214c6c93899102713cadf6d77956b7a784b4fb9dcc6ad5657b92d65569f974a71757b3729ae4e6f27c5d5391985a1a46bf55e7df591f3c97ddc44fca8b97ef58ea6bc9432eac8299888cf3cc71d1c1860b661dc7141a678d4912926e2594a994945088090ed77b3da56279d4b924cf2b8252591e024144c4e6872439883a2c58a81c21c54175d74d145ccf59113ca3aae1f0182d65d380cc2758478172e322f63e2fbd3d17a373d978e172600c1cba1e09e38156421b9391c7881c810285090fb5b0ca68d66c194318f8414271ca4d806398f4c36489979eece354c35cc2da4cc14af519edf7e28ac9390ae5902ae57f0b3094da43c474d28f79b0909e3215d330252c67c0be48bbe0d798b1ae4ee429e97d12190c6a444421e2790274040caccd74b81fe0bdcba1de38a5e9a7582d93e99d5af9e5fcd1d13b34a56ac1e279d74628f36019ab2d06ebf83ce24f2bc77849e7328731ec541814899f91938d67920f915733ef95d473f2a6fdfc91b437ce918b7e97ec5b0337753ca494fd266b0bfea4baedf50748f34122f621d71868ccbbc8561bc85752a088af86028a6f3e21f28b27d27b6e3ba0bebb8665a8f790cd6c9e12d1ce6c4c478ccbb4a048e764651842bcc605868011c5ba6c51c026a7308f84d277091c536aaa2034267b28695eb7c028aae059d4c5621571574411d13a03de570f79305f0f280a6244abba08ec81070ecdc455d50581ba1d8a445ad4466008ab9dd00766791fbb036d2330be0683a92fb62d67207a45b1a4f6e1e59238ff48b0bd905c97d5bd4e5fa6abb90b02993aee6447082dcfd516b41ee9bb6306941835b5826419641083e4f98c205454043d27c225701cad9dd148b3108e99979c8a22018a0d806e138cdd2bc0424ccb2ccd2e33e20567ef138f3bd78c31cb1675500398a89df89b96e98e37aeb2dac03d38398eb3bb14ea56be97ec051c200ac9f5580e3948dbd0670ec64ed236b66bf64679f7e75ee5c5feb859836349ed82891729d4d00ed77538ba60d7acf72c91df5abde9e36c21c14093e284502cff6dd543cda571caf74a33280f640588c012f737847e9f43b2aee39dad1b301e89989b5de2f7b580bd776d5b70df3bca30a97667e4d1b3257af8e9e1db9a959db0599dca6c157dc0549eef6a66943d505b54884526eda18b9735386bd7d373f1c56bb1d77327d697257754d2507ef585f82532ffda65453a9aee4763d3fdd05a53a1319c0393f03cc9c0251524f79ca55d454823a8ae4ddbd1df4da9fe396ceddf147e65eaadcbc46774c1c12015e3b8e9c32a60c8d7ba76938f3aa8317c7bc4a95b5ca7d95eab4ae9b1f8eea158ff57466eed40b391c12513a771c8e9ebb38f3dc4b17c7c415d71671b873dca457dd91fb37e9375bd25ec22926400dff44f10e934fbca33bf534ccc3cb26d8e2f14b75281412cf432929b94d97dc59a2dd6e252547f956a27178442949555309361dc5747a4d55858272fb303491413b063f7188937a7dea158fa6acdaa17a8a967a4aea2929d76e29f59494ab60ad975ab782af77e4e171b78889f3e5483de5297854953e1c33cb0f876aaf77864494bc1ec7e45cbd098ec9eb4b2e0e13fb8a2d4efd7cc9c5513f67677bc77995b71612a1dd1e879b32c05b1c0e07771cfb7aede298a06a9c988797553885ca1c5e30abdcf1f9fbea8e3f504e5e2af5eebbdd3873ea3d6da4febd4bdd2e48d733bd009a3e76409eda611fe3c9f2cb418fd0450ed2af14c6809777a00ea4ebe9d777d4bf37c9bf1c5d26319de4df45a12a0e7168173e68174959bb0fca4d1b1ae6618bf27801eee305e6c70b943e5e40f50b801f2f50f2f102261f2f70f2f102de2f80724a4faec9e47625d703efa7baa7d24571776aa8d38b7a3d0a756bba18f03209e6519253160aa02cca3047717171ca4ff0386b6adae8a229a36b505e5f32c99aae06f57a4fd6985effc99ad4eb4fb286e4f52859f3bd1e9435a7d793c81a95573cbebc7bb9262eb400613e9a8e988ec04c1ba929a3a35ebd275b14c91a1f264779df4641d6f828b9f7beb542d6f800dfbd6fa5206b7ca88cc81a1fa54f41d6f8984fbd6fa9206b7c70f60a59e343bb91acf1617f7adf5641d6f8a057c12452a6be7fbadf4549997a927b9232f5a9fb49997ad3f5a44c3dea7652a6bebba65b9232f528979332f5e1c9ca869edcf8de7ee5b242959b821b2480ed311599cc008e9608f5d21159437315929824299ee4ce38cd0fcd33d3e051029906889aed65fcc81a9acf93dcd3bf59a6c12d1d057086ac5f210e98693e827986907e853968c61e9906efa0f321cf696ebaead29c84e403baa18949baa9e0bb09d25c7e37244fd1a6f2c3a16f1cf6d41938eca149d1bc1ebc639d914472431e9a191f6b0d7bfa455373f21195ebd882f2f194ebc892ebc72f572fd72ed7988f61ae26181f55581f4b39c6c7945ccfc91a92df2dd71125d7f124579beb689253c71f4bfe91e63aaa727dcb1ad5eb4892eb98cab2a604e6158f34b7ef686ed8130269916709f059422037b53437b60b793aa438e6c88c1bb3c50878fa35c218f5f46b3e0412068131c2f9fc6733327290890123c625be68c1bcb8e09b0303691e2590610a6f615961852a292b94139312558a8400d886dab4cc3d5c19f3e1ca18ca12e5f953c8539dc8634c52cda670862c4f9c1fee8c2c378ecb638ac9f450d7fc8dcb638ad12d80a14ff813068544615198141e61f1b07a5832960feb8715c43ab2c2b312b4d2b3220b8d5833161185499e70160acd9067c52709e9c8885594c3a13cef03d0cdb59bcbef467d4c528b33868b3cdf75407212b9a3df8d8f19f987fdccf4cb61b3adbfb97fa25df3b316e7ac46490d59fb344b8e4ab2a09f5dd3296d3d492903e4c363b668713e2689c2608c7cc0303149d9872b633dc018853c797e8431caf3a12cf40979c29e972cd61a28d234b05a0e02ed9ab5014920d2103cacc522b84a92424784c793e96f18c48f998500a94be2e8bef2284f50cac839e7e4290a9a090d51983ca2477a36df45443f3eb29ea47a449bd0c9977a679ed286c793651639b4326fb7693b4361df7e907e452fb99ea1e4e58b7763e60efc614ea2493489ae7c0f892b08a4ca24118e8489d455e516729551d82eafcc2285fdd0b8d60f082af3bc31b3fc0f14d6d225af7294caf2a6228c83337da74dd927ed61878d7dcc2c4fc4ccf247bf2406e2e87b4827b4a027ff90466ed0938de8c953396c02472e574a29a53d654fd9264527650c29233f8300cee9026983be739f881f3503f18441cc58fd8c55dce3c5eac1fecc210e2af75119a8031573b3f6e55aaea0932f4d982ac7cd39e9e424858da07c7771fc5ce7140efbd4bbf6b4ff9aeec538a8bd2997c21c25dfbec96cbaf6dda0aaad003a5ad4fe838e0248a2ce6bb1ce0f0727041cb9cdc424e59bc9894fa882b2627def58ae7d78b99bdc3064dd0985d013b32984e61f6a47b3ca95f98730654bf966a2d2039fdce4f50838652caf9fb3f9c41482a25f21d1a953b9db596e4ab842b97346f30f5d46a1b96339c126dc6d37ed651567cd517ac96bf4ab8443fb9dfc19e08638aa73577d27975e2638cc61b2934d2e73ddea14c286ae2b1c66eea69713fa98954e7aa2793bcd1b04fa15e26c57b90a1e59df7088a3b29377727d03fad58016ab8ded2d4aa0228bdcf23077d5b342ec607dc3428439b6870f714eae2b0ff114f2ed4ed99763c359790b1ec3afcc198095759b576cbef1b1727bd6955264b9fd32806366d657f00d0b5729c382c7f00568b166160bef08718803011dbdbddbeecc33471461fde29930887b7c8c757662178bddf6ddf8983477dc94b3397bb22aa759058f1fc82bcf610d1e6c7ce897caeb0a0487a0ae7ad4bb09b2d4cfd97c620ab558af7227142dd60f513924994b3c6fc333089e3d6f0029eb256f408bf536ab9c75c392571e1e3025054f21df5e8f80e3aa478b2517a7f2f0645635c153c8b75b116c97c7edc106b074d3b5d103793b0b4b29aa5c7e3966ae5246058f2a0f71015afc70547088f3ed2a58470e0a6008b478835861cee2808b24846cca2dddde99b99c1f286c9ede492214bc5a0358fa945d8cafcb31d6a94c0c21e6827fb18e00c2304b8a6155a944e506d0f4438b95d657ee3bc96af5bf688cc23e1c354f998e025cfb28895c8270f99c5fcecb5db08ea4a20ab19739d790841804d1d4a59964454425a2e67125b7a8d63e5b1e707b69fecca0299b3ee311d9fe5e7c59e94e9f21c7ac16ebeb16a0493bd630885f3e2f5f2ebf2040ea9ac7975f8ecb5fb04e5884980be672ee31d6914da0e28a59ec62495d50582bc3e19d6ced73a2b05e4e1452e492cc528a9c28a098d6d6296b57b5962b699b3665510cd9a1957e5185204d64fa91ab583b58b79ab5ab7d395148118514511469395114d500dca2206a51e66d1c40a6af16ccdbb77501eccfd38fcf61bd8e07a610c4084f6c5ec6fa861513916391288af5736a0ed2d0044f6c5ea7037198c5fa3246b75aebdb45a3188a82a856abd9967500b266ced3be9645275fc69a942b48a9890b6098436fdb766bf63c36a6618b79ec2dee1975d56f011c7be8a891286c26f5c8284cce846a0ff11045c18815521851982ca2471449a8be0e2155a2fa5a447d68d25027d5f7915a6bad3eb9ca72e5c9f53589071dd854d612adc792ccbdeb343c7259eb39dbdab7384d9dbd4913afd65c95b266f6ccddb79732dcff6aac55af45cebefe459c5debfc6a5e72dd4dcd0c355e62d4a85ea62300e933fd3833d6984d4105146c6b9676c0a12ebdd3b71180df4fef9a7a37050370e491c792ecddbbbda75b4d5a004779b479dc4f06a05dbc217f78a62b044c550087a0408bf5a62c80a77b2d7a1fe5519532d6f33ca09e7b17883e468bddb661ed875195b793481b079832eab7cbba71f7a1c51f8c80dc71703e66ee7e8069030152468d42aef7e413727db7bd7e37362d568e03a5bddb0f3e4c508816735aac3f5c21da551bb0bdca1aee1b1ebf77f636e00a2185919ce3d0cc9de6e8cce12a65b69f3e79b037de735a887e7dddb6e1d297e3bb35c4f16eef61af08dad51afd1a2d0ff655dc708d162be67c68577d6d9795db5eb70d8738da3150927968116cb1be468dba5ad52142b1bef25bb558bf2d81b48223520861084f7c095a300ac110ac108445ac1e02435018275d36a70e118ad1f795b16e7acd800e831598488af5e4f9dea069da535ce9158f3f4aff2a0e21fa254b25cad2216f1f4be4952d7247ddd328b1d8ea0cc299b03079f65575edaaae6cd73cfd80182516128b799bae7db0ed1b40f955172495f23fec2bb598c3725e1e5e6e8ebeeb5a69bf60ab303abe64fa0e085724c504b85d6c917e9b38468bf422877b6609dd103c5a9c736e9eb59ce5386bade5b68f5c0feace9eaf00b89341262761f3a7f471cb76b4d246e9d64e20f3676a585aad5ffafbbbf3b77ddcf6e158cce259ada890860129a24dfbd309eb5813b7e1585cf2d50ff4ee9432f69d894a29a3633d90351c9bb7226476bed3e9d7c87dfb8e92b51f0e87e7ab467b6a9a264b272965ec75faa5d3e2d43eb1f6dd50694b320d1754f185182579f2882dce4b2ef2d469188f21a610f336f33fcccfd4987d2be8ee5ecd2f474ba618a8be3ed1c9178ec2bacb4b2790808b3c4e1ffbe9438221d97e0eb144ae4e919cd2e924e9e91acef96ec23adcf7d2bdee26edaad351f77bdf221aeeded8f4f66e69fef49cf953e9f31ace41fdb3475d759d22397d15d62992737ac9fe84af532447f5c63aaa7be02a25925cc6ac8ca9ae8ccd2b63a891cbdb1db9d4d529a28304ebccd8e9881f9e6201584f4f38e573844e9104c818c547d0cbd8288d80cc03a94f62c668919cf9c63a454204f4bbe7b4d6d337619d21a8cbbef1eca12efb898f20f5532cb07fb1dd4e6d3b85a22ca7b8c24896535c2145c6594e71c54f66c941460ef7f44aad6d5993d695b9f5ceceaf65adb5d59a98bcd5bafc6aebd277ad3bc1d66dabd5aaad9649ab656262d26add5e931ceee9f26b59af7eda4f6f5f21545e97d0454fd621cb25c0a1080a7b28ea8364e90255efba136f7e2578dc91b95b7d37aad7daddb665a62a575d7595dba954aa57954af5aa52a9a8542a2a55a57abd2aaaefa6e426df8deaa28b0b28bf9893937f46f10866fa1692f663fd09ae2df4aa1f4521d36da11663663d7a2694fa6edaf51f2e8aa416a50f85359176c94dd641249de727d343f3a75f32979f4a98534c1b2d9b32e4e5a70d24ac8774c9a35ec0b88c71f94944d6c82c8b00fe60161941b08df8717ba8011c6592113d7e80f446e7ae42cac8779e21108dcf6c3d209acf1f1a580775c9d9cc395cbee3216b482e2fe3f20de8d7cce587e817ca5ba75a1248314eadbc53f1fad6cb9830af87f94e9671eb591a8738f7e597e4bb393961b9e3cebb596b3d8e53bd2cba93e9fdd19975f23eb90d65b92af7be88293cd6bcf25f7cc79d8c4ff2dda8dea1dc8e7567bc6bddfe6d87a94ee912d587a3749356abf57e0a8acef6575656ea0baed9d6df8bbfd795172060deb72f9f415656f01d7fd0bab2e3e5f62befa6b772f919e0e5c33f3abffc621cc27ce52c03e0d4f7eb57baefedefac151cf68d77a4eccaca8757c2708564e5e00a0a8a1718982bf34bf76fd7be239869eb18612df6b9e3647de52bb8c795b3ecc53bba7e40c0e0b1f37dfd3fbedc5ad6570e735b61f08efebdfdf88261decddbcb0fe7aee0ae5f6ee73e0caeac83f79f3775ee86ef6eca6d68a924448b3366cc58f98c8a979836589d4a0a06a0c53b4e7edfc730fd3ae53879cf78bde38f19385c793deb2c7a983fe5ca16611ea658477d38c679fa80f072adfdffdf77f3d6f20a07faf4cb71027372faf5bd4d7b2bb7d57a03fa757aabf525a68d995b47b969b156eeb89357dedfcd8ac5618ed3ed719c6eed090ceb277727f79d4b4c19acdb50d66d7de59a9ce85b5a5a5a6caf8264ba626161d158eaaa572b9b666961b92b7c65beba314e2f8cb7c85c7e37302ee3cefc74bfb35c9ae56780ef33f8c5fb3b99e57419a8ae754d2eca9d3e58d4cb2c3f21244e0e9781735acce18638dce9533c966b33c9e577c3740550e616a045f918f78716e5615c1f48ee3c0f2923af83e38a563a78084161327694e56f670d0d3177661f0ae032dde5ca1c7365de868052a0dce5f2ddc604985d5d07826006efb8ca282fa1bc7e3738b4d9d545d9f58eb33ec0ecba4d775de7fa93afa60e8ff6215d58d984425d4ebf1c1d98420ea6f0935d2df7eee8a34776dd74975b44cd2ebcc3f4968bd78764bd6bb97482b2889ac71e79c7bd318236cd523dda2d7764c13f6a6ed635b5cf1a40fa16f9299be821fdd3a25cf9fc6e5a40acbcf57b477cfc150c4c0beb8585f17db9cc302f2b5fc13b5a5ff9ea8e2d5ffddeb1e5f7355b8adf77bc63af76d4b364bfbcec35cb592ab52da717c686aeeed88279fcc8ad1d3e7432cc65c6d707cd3038cc9129e6412d0c30be210e7e1fbff161600eb37a3759b2722db6dcda22beb7a1303030abdf154e677c7fcff5ebe4f7a4a5efd83aeb2b3f39c123cbb86a1ddfd64fae6cf1debbba238f1f79e5f7acdb554a5fefc7293340eb2ba72b97f9be3f20ba1c528cd3faca5b5fe571e5addfdbc23b564edf5fb17807bde98dbb09da2f8a77b0dc5ebef2f89159c69959aed5965bcb7244a61e20424a99b1945b64a6b87f5c0c30b34bf8096217165dbd65729897c3b8bcf84cd9143287f4e8d1a3478fd28f8a726f787fd4f0de971ff547cd7f7d398acbcb57c7263030b8b57a6bd56ab55aabd6710bb75c5a3875dc37d5618c5fef791a0b52527088e3727a971497a7b83c2525c5e52929600faed2d71f35a7bce4f29414b0033bd0e547cd2c9c7294db50ef82b93b949750cefa6e70e8e2729b4699e28a2cf2c9513860de82bbb1b6546aa790ef66e5606be5f47b69799d36566fb9366ddcb79c9335f82d2fc91a995b421d341e86415cd2c034a771f9eee4f626ef685c1a4b83e60403d17c0a6991a667351763eca1c0fa537f562e3f2e1ad8b978a87bfa015fc053104ced977c6981a720c89911b81ee4fe8b5977f1bbe9a130ba5a8120ed4131d2ae7e17f3dd4031aa263cfeb09d76518c508c681f518a7217b5d8f5e7c323e7618afbdca59519a508c5088a51bf64092ee9b4574c81226cdef1558b93f541c0d485abd4f202e38197e2a90637445dfda1d34d1169e04d1149ead23078ad64925ac904d3b27164f4794a9de4a32cfaa941256da2fef47cd1a016c7139db5b6b554d3eab6594e2b8141f39e7ea44c104e33c6931178904f33903690329ac87d13d77525cf337d5f773a953c14ea03c1d2898404bc40c02b01c68028f198c94b6850bf28518bfdd5eaab75a6485227229346bfe49e7e7a871b4cd7f81d8d27206c4f750908d3d53fddd30de6a461f1788a224fd786c7d30e729f12f5ebf4d32722b48adc4f059540958aa4a4a494323129a9330afb88a88bd293d43dcd48565fcb4a4df4a4642a414131f14eaafbd51ff0a87bfaa14414769aad4e3f292625aa939386614e3f3465ba292a66ea09334585e9ea5416a62bf5859f1491514ec1c8d9a712f6b8210a4b3537f4a1d50b674a116929a21f868478f974b4be1baf55fb457bbaa44b3b559c22724911bd103d6e08009926e5314544448f3add74204a70b1d2950063aacb8b2e59c35dd6c068b2a454c44c58259a321a48a5a2a5567597ee3d0149992029034489c74e369034faa719c81dfa239a3228ec84034a94fb24c483dc2727686f95e49e82205d5fd30d8fa7a08abb02a96eba272348191e4817907459950a9f82205d7d4a346d9c7e682065fa23a522f7ed294cc68266f92494eb0522b9e902a56e495e4afd7443a0c3c31323b9eaf680b1d4354ecbd179c3a79fd415814e726bd0999913ea9e88b4ab0fde53137dca9d12e0fc78fac96d7374aedbe907bc1c90a42ed4f68149d84a7bc3a7d8e9e7f4236ba490ac914b38a13eaf439950256e4369165529aa278d538a48ca8c29a21491155648ca28eef9fd1ab7d76faf78ec6fe73aa900505291c450e6be5191c42c6bdf4024c09679657a49bf647da9a45f1a1eb79b7088a3bde4b548719448728c10a445b963bb769c4da660006e7887f6fa1940c3f2f257560ad4a162f4cb8a2dcacf17d8a0dc592f060c259b27006d9025f2d91fcd6ed9e211472e1949998992c2b33f282294142d4e8be5872abaa82a50446400ed479414794a812aa230d30cf4509f369403577934cdb6a3a4e81711aa8a7e494db35f8e96edf6dbd476c81635bcb298d505283f72b21b7209f9840402507e34cd72e759a5b3253d516c9a4999d92131cbf3f2a6990d02426c51a2923620485c3d9cca43394ed6549ce6c9f4146b44cc3c6ab93f359c5b44cb7af24173c5b643c2ca1a79834ebe0041aeae5679c6e8975cb56b72d9d485fd4cbf28ac797e560f71409e4c0fc628e141d6507c63668bc116419e4c714a0520789e0ccafe4a4a498728e10d5918a184376091fb3f9a8bdc425dc8a32793bc4fb09b808459226d9b1843ee4bef9cacf1b277af5f40e0bdf777e3d99f267ae6fd7be75ddbc4776d90f7d11221729bb67d9a7bb4d85e6e1bfadd9a89d826bc56c21bb6f0b80fc7b3433a20d4e57783ba4ddb34787be8b4ab8d90493d7eb4d8ef41619e97bb3d237e7030e88e8301d1649d36a866676dad1bd716edb9ec4919fd8a479d5cebabbe4b3065cfcc9c69a80c539b91a1b6587ad799ba528993693748aeb5564cbb4963d0cd3929a53caccf54420fd2607d8c3af9022c39cad6274f61e463359e3cabe90de0cabe748d87f5c9f6dab52f7dacb684c2789833674ecd8779151b1e6ec0011c25914d5b9ffc0385b1da3559ac59b3fd1ce55692ed8dd1e27c8b33c90407706469afcf356e3409934b204212590c618311ba7041cc7e2561262b3082177a60840a245011b397d29e93d6be01f4660f1ed6276bd8db30e70670669987e05167b2e6d9bef6edadb53e99a5695ef5b21d82c23afc039d1608da0fb8218678021c250ff9877e8d609635e307b28907d379b0e95769bbf5f67299fea803788b130d60d5789ed0996cbadbe9fe52cfeb458afd7122916727c9983c79361093939457c95b3e56d60a487825b898cc593eaef264e570204554503ecee439d680f9c88394224f3cfea6de72e73bd6ede8eaa6de55afd3bc960b3e2575d645bddb3c94fb5de59efe72490e73e7f14de570c5779e3777543c4aa35cf2586eea2b24ef78006307d9f38ae4eef35aee4a133130c7c560c4c0208a6c913cc4bcbf49f92279f7a7b4d1bd4f254ac262f494711de46ecb0187835290c90302bd1b7425decc6d1de67ec7f7f41caeeb291775930bde062a1748fccb057a81e2f2c2c4eb3ab0069f478395a77249ce72c1af5cd467105ea09818ae9877285eb7ba40316e22832e25062a300887b03091bb162fbcf305304217e46ec5077bdded5c58c06a793902f6c797918734ca249f6f0ec61392bb963773c5c35cd4f105ff7b7acbfdae82fc72815cac0bf4e2e1056addc5b57a71bacced5e78e1059f82ac7281623c06c6b56131deab141144a5a688022c8bf158eefc8aea2780217381142982cb21068ab912ab5891d4bb17af0b2f90ebab981817aeed82f118efe77081142179087ae4f013b10499b5d202a3e42f82e4f002c138cb058a598981f1935b848c47029911cc78bfa9f7c810018d8732df72497eafea2932874030ceba40312ee3d676c1787f35cfb50062deef6478dd6db9403167b940317e182f8171d60a8c77335ec7ba4030de42720c14e31d8df70bbee5a2ce724f5fb9df73b830de5dd605723dbc40e2552e50cc53627c7581504e6e115c36c140310e5ee6f67031f19d0e20d8018997d7dd22b89c7a2701e07d89acbaf3925cefc2b831e01551ae2bde6bf3e2b6ae0eeaaa1eee3e7bb2c68226d88027978e0169942dde5171f55e9a2585198ee3914bf2d4e8bd169bb22806b714807f992dbecc378f926cede7d56a7784a4a38215f70ae43aecb5d85c6eb00637046e3374829d098fdde5aa9c9235be78d2351f234f2fb70c71283d8f926cf14eaeb57253d45c2f5f7d9774bdbc64f955b07bc2a0eb26d894528a80aeb55a6a2da5b47259b61343d98e33d9ced0d9436514c669efdb9f19a10b5217140dc451d7764f5df6f68d06501bf391ace9db2b66b21d67b26d5c429232f6363d5aa16c1fa35f618e7e8fdc629b6653246456d66383f46bbbc55ca6f29d0562b4b2920a87c70e24657bfbedda202d5a4d03477994e76165dbec16bc78f1e245abe572b55aa278d73bd1e572892f5a202b77cde22c29f17abd6bb7bb1dcafd05dd5c85a44c5f6e24db0eeef42377ef1ed7628a09f07bbdf67d7e1a96e0779bfe3010caa3303b4371f73e5ab044ebb87b2fdde35a1c3913ab7a95daa1afe918682f21d3b7ce923532c5040ccc847969dd550f91c187d382a9ce64cda5f6d62295ae662b963c71a98847cacc12b00a200f9593d6999431fd00818777c5d699bcdbef7b9fe34aa757eda6d75bfbeea78ba31b226b78e42ee777672e8d3fb2566f70df8ec30d91ebb71ef66adacc5b9ba60c5b39eee22869dfce5d1cda372d6f58563cf2e0e179b3bea0e44404f29393698327085fe8e44b96278efb70d9f2ae4b691880f529dd29971f8efc424679d735f5705cc98617788127e4310138c21801dd1cc688c26ef0ac321ca2433046fd82c1a25f249c1ce2ecd015f63549d6a08c7596fb152535a69c8795659477a6348033a33c05a527f8a1909c9898befaa9e4279492300cc3300cc330a4a13949f70d8198f1a6298c33a38de84052aeacd4eb0c20484ef30e67601c122c5ba4c1d5a6984e637a637b159aa631d1e01413a04ae31da6d3e01d25eff3b0b291621e5666728b4b9e8252826b8b261ffe2a088214fc280df8d5f143a92727ff48beeff52b5ddb940692874f91a09c1e8584a4a2a09090d0a090ec641a7a9b3abd230f2bcb611133d7d730c423c96966b0d0d09cde19b26c67d0bcf6e4d4a9c927a768f00e92a3dcca32090e9fc2724754504e39eb8e1d48ca299f0a96200f2bcb45cc8c8282638eb47019c7b5e1841ec8961744c0a706589802154e9860074c007bd3b440aa9fb9562fe469119235353efbece5a8b31f036c53210a40e0094312522637334295924b0a9b395d366c868cbae64bdea9dcd5c39bf2d495917be6a85df33366cc58dd98941bb3451f95bbe9cdb83347e19dc9e2ce20c9e49b5b63304627f33364283366f8ccc03384182151d80d0c4c52bf6a86216b312629260b982318a37ea53e0f8345bfba130a54b954c19dc233334877268b2c629062b4b879e3bc73bcfb6545d1504cd214ce1093446137dfa668c151ac0717c41cb15c80e3cc1110013e73349345bf90648dfd2ea58d99eff2bbc15aad62c2540acfb8fd8c4ff0a6c63b0c4999f91930463058c01c4999f919d9b421a50c92cfce30421486836f403007c711004b0a239455475a4cd9629c59f514a414a414a47088ac9979de9404aa5437f7e1f6f07a47db93c790a7e6112c814172c7123843569d2869efe1de685d7f37dbb6c5bdc1330e2c880152ec668a400612becb08923528414e87d143614042d0c33c0c190c21fd32f93c0c9f7e9534894a51ca69eeea2597e42697e627b71ee5cac8b487d5353915e2d3c3ad31942b93618ce6558aa64a519e33405a9c33411acff4a81ce579131ac031266906c88cfe9c213b31b9333e33438e282cc7e75590288c009f5739a29244613d7c5ececc66642d4e1529628ec81a92cf4a4329cd69ea55a45021ea571593d21e4e739c12e094d2192133ddc3f56126296b01ce64d1e29c39aaddfd4e96ffe12865209ccfc8280c07c31851d7bcb961ce8d155310014fece63bb11ede03d6915c5011f3e13bb11959bf4af9e5314932e66524a53ee3b397232973048e3046a291949991753342fa45f2f9199f182d4eb1c52a80238c9191949934a647cacc6301ece1de87bae673dc13e05e465d3f2dda8f29482d33297306709c391a6766333f315c8c31f21893240307793e75c7ea849d71c7ea842909ac24ef09ceb038c76101153c38c74981ea34389f2547293229333f9a5e72faccf4d3474a55a653d377d0abb0aae423a5947e9f2a31210c568c1519161e5a02700198c0dc01601b977c725a9229cdb18faa4cdfb206e59424d354a67475ea3ac5e377f0dd07821f6832e10ba9f00c9f19424ef74f7c66a3f439a3b16c330001048007991830625ce28b5b29be5b2e9958a14aca952b94935b29ca887b498faca12823138d94aefe6e54349724e409792897556f999d47d6e8f0799abbc3eb2c3c2b45fda221feab15081ec1668e60e0276b68fefc5d1d5a9c6600c71920343427007c9c91e5c9835cf371864c2649caccd3b832e20f829c8c1f2933df65e8f668dc92a3eee9f4b2e49923efd3f3bcf1fdf23e8a4bc8353e7e1693f41d59412ec132436400c719202440fa35a6720f781a50cad471278338cc414f731a3cf6c8f42499e21d5675fa999b2e3dcd25a832a96854180887e2dcdc1e9ea234332c897dfd0c4b42ed67dc1413a04da5ec662d090d2539b994bc98c09cb4505eacc414d70c16959810062b86aa6545c674ff81d872243272b849c9c48011e3125fb4605e5cf07d0b4b132bac5025658572625272fa40936a068732caf33577a44800a0c61d4b6d0476c7521b79dd9122b1838c1c666cdc912bdd6813dcde6d9aec131c72fd4d47bd99230a0bfb23b85a1de5f930c8c750481e43599e415a4c026b7f376eba2d8dd7dd04eb8c6e7e3937ee83fc1880e33de030a78769831400c9623d7c2766d33edc30c787dffc06ebc8309820e6c37762f6279fefa6ea2ba416278aacc5a93aca477b1a4c72639254aaefb027c124b727f9eaa3b524b740907c873dcd6d3ae6c864c9a145c6672e0dac83cb0e2faf14170cac55e305005462c49a30068c1596cc2d1e4d5ef2cee4c61cb9315bc424d1d0949ce6a389498a394283c7e751856790e094a4142dee0c215d004753519e1f67c8724c520a529e8f49ead738431673a45fe38c2c668b182efac57d860c5bf087cfcfc86a005003f6da41071a33327290890123c695437cd182797109ae2ca8348c53f6b920768aa1a111040000e313003030140e88c462d17844a42a22e47b14000d95ba506c5618c8410e53c618630c0100000000020000401b0002f3fa33c23d49de8902398b7862d01a817206309ff6344aa98c8b98342372dfdd6fac262749009cd397b460af7347f7cab8cf4eefe5a340af7a79e783db28c6682d71ca4985f462312efec52ceb214b6d29efb5fbea9f8acb311e0be88d68b7281b141d74bee3ad7f87af9d8e1f70d28b748e161c761427d13d2769c1cd8d26844dacced4ada68fe3232c07ff39fd99a78aa3d47ab878d7ae93831a2e7a3717f75ec6e85d9a5d83c72e14fd6d22a62031f879805c20b2a22cd832c8ea22969301a6d84536f6201abd5d641200ba32b28aec082ac4ae3c6cca8ed0f8a1cca95874a22d4d4066db1a4d09c1635f90ee2c0e2c8d4073b00b6547cd42ec653a447f84665e0a4c27cd85392b428b91dbf06ddaa3cf21c205981fc1c494e56cb4ec873abf029bc88311aed13461e0c1460cec57763a3631e921097175b0ede5954da1411ecdc800445efe8130a0ecbda8402eb67192acc8a9aabde7b2d0c31e85b8c046db5e2f4034d515fef2dd58fe40e4c73405c9c232b0927ced98b48d6b61804a42002d5fbc4d414574f4316cae9d317c66ec3bc33c9e4c8b03901507458043cb39ff650c5b1fb52c29da78d55db441f8c919234c25d726e5071806ddf03a7313096955340c34bb43b881f22f84b1ead139542474aedb66123e4bff6801731228542a712835357dd38f7b8544f77d288f33af6b46a3d75f3277e5cadff086a5b77323be61f7f26fc237d8bdff4df8069b5ebda0e08f4c8e0600627237faff285d5e453ed443c7af5062ad8614d56996c140a2eeac8dc21b5bce6fa3e8deb185a1ab79a2b6de8f83f4d8bdfe4df846bbd7bf01df64f7f26fe29b6cf662e5009f62c10da3f77323b869dc0bad08e22c0eb9d1a017831827afa2ab1c4d08853e89654a4e3635194afef4661004f701417d66de38e04bc3b52d5476dae2e2405f66d9192c77ca5d071e002dce3d6a0eea06c928931e5637b0136d9b807163adaed66bf1dedd02ac625a239a3af9d006073ed32c3ff464c0d7481f9a7c7636de27fc00df60f7fa6fe21b6c7ad5b4c0cec5e31b766f9f8b02b37b430a6a0fe50fb2345b99489312f05fcf799ba2dfa33ae21778e0d38deefab65d30c039a4bc5b690c65519f99f20246ce536230e330923ef08df67bf54cf8d81c84b9375313a254233c7216b79beda4069f8508dc33cf7c1eb2f6c51a13bd2205d532c3959f38ac4a50e10fb07d1d0bfb8cc59b63444feb491f6f6ee85423071c822795eca7d5996b6ba587557994a29fab6caaa678ae640afcf91d5749b3b68485f8578b12e0e48e1239c396eb20d569aab5b4d49c890ce7879ecfb39f67bffa7b5b67c2666dedb162884f3381ffa61bdebcac4bc46ce0495a982fe7c2d4d9f7682d48a222344d71c8af5b06f013a612cc1efea8e56539d5cf3b710ab393369d87a76b53dd4dbe425147c06225511d351db85b68dbfd3669d9f6504e9b8af3ac1f0bb0e539fbc41f04488aafe54ca757db122c2addab0b1bc848ebe3a23ab55abc66ce44b3e45cae5ee80d044f902d24ab7cae0148a58f769bcde9a1f10fcf2c09d33cec7f03b408bdb4ef17cc99c199987bba6ae6911f71b29715a3c8cd78ebed8f550f7f64d9520c7409a4596fa8eb2a8691fcaa6084c66883f693217205f49f84aa3cba65163b117a5270e89c68a272edf758e91405877f94fea783a8823f027688c42860bdb83190bce9b1231c064802f45954ae8c110b096ced822cd892c2c346c7eb1593f941d3315822c4c0190fc7d3d3c0c9c2a491cb02a31bb6668888970b9bfa9c8ae81724096625aa3117c3890bb27c8a521b02d81614f3fa650972331d8cb3d6c4f5d6a64c621ed923698658f625efcfb09ce3fa5f3b95a3ca73df66ad6aef2a93fbe35e1186b2834a00f3cffaf45b30a4aaec778b2185c49b57ac9bd51d3bf64f4afe2b82a8c480e357b1b2b5750987ee5e61850e42f01346a8a0493ee3a63952525bc578b13012f16bf645048acbe2a0853b68b4879b8dd064eef4962d573a9ffd15c8c2bcb96ef60ec6d3edb4cf3aa445f823bd14dee4d55d92be76f3e89a82062709dbd96ad23165dd91791bb9dd57aee3abc75f8b0b46212964215c7ce4a3ab49327eefda85fa6cfbced0fd7afb02978f7c447508cf8c66727f86a433032d9011a82ff77da0ea94f024fd6c23415b4449bb00b9770f8c3190b2642015731d62033dd9f6efa3e561b7b9d5b3bd01c4063c7a542296dd0607156d207543ac34b671f8086b7bc421069da12fcd30f11fa8dff7168859dd8864d314bf40ec1039ba65b5def18868608e2f9465b1809f7638a4fbb66d57ffdd4a1e21808eff2819aaa6f3cabecfdad715ed82ccdc214df43facba0bff11375b59e57479109abf175c652b08a124fbcf21d15a1a66a43e881deb5a440785aec78e3f04e1fcae30fe621ce634f3ad91d9857aa69dab8c171445e8d2a291ab0a3f648f1978e2dc3f97201d3e38e05a6d1b95a2c208c0ff1b9b759c03135d7ceb675047c8630bd0bdf4fb89b0ca2be7d897a05730cc3a57640280850dc9da65df38965e576a19ce193107d8e4b9e4c2db58d1b9b69fcb16af75bc861c6ba6d293689eded062d6d280ed59acb6384dd1091e28dbe57775ba2f5625f44e010a21589b4d80cfc8ee46324e689723af1189bb986c6a4d3ebd5224f647ed8b015f4fba731c3c72dc558465327a88cd20d2d97aab94a52e3ae0289be4d5cc84570a5668058b886da612665f56a2b72bc7e486257916f9ae84ead31c7a7a18e3bbb4c054227078b7f9b17090121f02f274cf88cfb5ce2ae6c1a337559779e8ad99849d0221beedb508fc7118d844d93d7ded6039af9c8b464813baf6234c3bac0417f3fb674107d14324417c81c88991eb4d954d2f9b57fd0c7a43da340e48b3f051e4d0ffc54180a21dcc6423398d2650590062b20d09ae640ee4de02ecd92ad269b64552ed2fe9747827be22134c1e0fc0668caf092e23ca15b71dd4bd8098777d0e7f1af67c98c861a30347b37287492ba0ea79acbe9021fbc5eb5c2dcea7949bf567eb0d90e2de8e79491e6f5411d36cc916bafb29883f0a113f48214ccc2e0290b8fcc32bec5cdba286fae20cdd5910ede05994dfec96d6d1b5a788903e940bbb223448adef7140675d732ed25f9345964956886371630b56b2495fafc39a63f53f196e3e8cde4e001a05dfdcfc1dbf65a3cd0e992ffc6e961dd433b37320b52d4771086afca0f1d839dc939dd35dcf34b55d6cb50bec704befc0dbec70b074c2941d2e48ffa6aee355bbf16aae6309c304ee681d7cb8f2530903b1b04c00274c41e5688185a27388e81eaecda9516f2e25a32ae5a9b0c8e62a720db3b56c4ebdd7b52fecb6ca57e2aadfa6d9920ed4f2bea4ae0aec82836b623feaf5e4426587b975abafd7e27440b0672f25f0d262a297fcb29cda6910bf004c22c93e6919b3f398147c184312b49a9c412271fe837a1fe1799ef2c53af9bc4456a606690908a5a44f48a83921b7884f642755c487a48ed241543e31614f5d137be8bf3e2a15fe92b252acff320d513877bacda2d2b7897a033a603af5136f053ad781ac588e7dc095662abd6de6157b7b66db3c4b61caa322b512eaf6cc3223050ca51529b8692895c7ebb294f09816879fb51ba4343bd6060b883a32ebdb8f46096cd3e908a8060984feb3efa9fdab52afc6109ec1a832f670172f1317192e337abe4039181782ca8b95971960d32fde41700115b2f345ac0fb2aa53ba04e649ed353590a2d8a44e164a19c9e4c5c69da351e215214ff46dcd0532c80d11dfb20144998af29c2e09a9b8cc448c9f44045b0c806685fb151ecaecf2280b9b04bb5ddcfec03d7fab32b53b0170183e6f48ed082bbd98352b74d608d9fe4a2a998df5ff73e318cb2d8dbb8032e864a8340403dce5f87297c22baed218aff581b5711559e2f351b88c866439038a9f0e1b422de24f67c5a635179b8ddcca59d004d3abe4db5dd04598fcc5973a637e8ef61059ff262b25978c4733373fe6261d5384afbf599a1d75fd866aac9339c22964ee3387c8c09130b69b9d36e3f31c76e92785ee1db286f509944db8b7e41ee83f7320193fefe73930ce74f26286d2176a48362b7ef52d3f1a1a807abba836ed4350547777f191af399d9e5de23306899e7f97c6c9c145ae38780f9f9c7f50b74c4c3f9f5a6b0c5c9d049d68214a85410f74a60e990a2680acb0f90f1fd33452b6393a2e941824a94b2d42df2ab67f77da0c852a87e2896956cda621930c4e3ff37962f9e857d48d70549e63981f7bd5f4846f54dfbf98a90662da6b36651fb556aa54a02ecc440e9486339ce66539e3601623ad09985033006189afff1552c2257307537d34870640e75c4f95546f01dbcdaf034e80e282e301b0b0e365913b171294804db7d06224459dd8df6a708bef23c29058075d0639375c828cd4c14a960493dfed9b4bd3df0347c88c54c6a8cb01ac423d082e57497da0d76c159d81babae755fb1a454bb25007c2aa19d92401696552f713dc7ac56e83347fd2c36092704e94071eeb69e3a6f1d6bf1e05ee976a5e5784b0e65a34e1705478fa643f9c5630fa911b1a9a487c876d23030f6ba48242fa05decf4356059e51f76069b0dbae520f7c2f2ba66aa3ed9543c8eb272f1fcd4f0b7d43c6e964732e9f01f496daff3055321f844ef4eecc69eccfc03aa0b3e4ff98ce361480439f6cdb57b4509d55835d1a9ecd4b878cab22899963520409ec81be9cb6120a92c4a30d5ca0eb575d737805a73e7774b689d42ea2b4363c7a06a5792461771a45323ac2fb15a25e40c92c259341341d56f94a686cfe48454b9fac1ae5319a804c7a146b52b5630e2874c3c610a29c0252663c0a862248e9266012f08f320a80a0688b094d896ceb8aa60338831a1060c853f2b7aa4dd307d52485fabef9333e72b659d51263e65f479b1c497dcc050f17e5916905f9e28011e6c6308ee6019c1eb0b469a53f4f395aab47ed0a3f52205f51616e0506273f4de888124f9c20d48e6b6e88276ada21807a062592bf0aa9d4b5663861c4c586069b8208dbc6f3346995ade1c812219e5aa8c684f23f2b2df93f8714a236726b4e5d2abd1e3984775ef80aa57b77a441d53bc30ef54d7caae31b1b525ac5111823dbb42a616db6f870aed9d56eaf5fcb9f61dfa5b1c4a8b29919f4b981d82838a854d2c501db035719ddbee9e76896ffda5e8fef170fbc90c4246a288a386392d07335b028cde57efdcf1d91537eb34617f457b2121bd99b068877cdc32ecb9c32421a5055e45290d0dfa7e3d922efa082014f56080ad728eb672a4661657efc570238a5bcdc451ac6a4970103e724109c15c07c63b58506f3c1975b43805fcd93305f7023fba5e2fce7a2e89f4d3be58b3d780b5488902c3cf4fdea059f4cd1deaf1e1bb27f95f7950b40b0c14d73687a9544794cce50b583455b94e29f760910a03aac62ed7e1e602794fb6d8380d4aaa7f17878bbb27765d820444f2666307a8d0d36a61e0346e36cb29275c786a898e372018d94997c71899d07edc34dcedf8249bad002b50d094165947d8d4a347966ae13073d141a61d7620c790b55a48dbc5cbc6446e9f06f12017608af4c70710d6460d5b5230614d77a1c4705a4dd6b32fb0cf51e22445012203c92ef8d8e01ae64bb8795031473574908b05b74c526b86f268a8d4b647b26531d248612d088906163c44a68e55387359cedb3a11b412ff2a859a5a2d4bece5d0b671f95d08a192040d46dcee3bd397f8cb56fa40f57ac4258380291aa5401deebcf7623e993835048003b00ea9182bf5a58bde046f4cd12689ab052f4258c0e14d46e742ace1dcd34ca6ecc8bab13a2c8423c11f104feb298428001bdc19aead174d64a04001930cabdf7b5d878e49c6b2088488fbc311fcca12b43a749c34900a8c4d4ab799bd3f322a823d84f47f8be0bc4f221047d2224635db5a36eb40e42f8fc41e8bc16c4d768cf743319b6728a645b564cbcaf5128cdb44dfdf9e5876879b95fd45554b7d815d9e6e8c151da9f005fd2c729830aa038623e879da3e97c7be79e8d70d814c53ab747a2d2f41665f0e61b100a8de368fa2c213b72783d41ec200e93f592dd83b5e98b4c05fc3dd417513c2075825cc855146306eef5ebd3250d7ae7f33ec0c1a99f61757624cbc97a767d1afbd80cc900037833622ed397ac389473de61c0d1b3c9c10849b78403444e4d0f620c9b4751260d81a22399efde20b77782c9756ea0fc32c50311607fb8cb8a5ac95523f03d7518591a70c7246d268550e7c54bf640bc2988cbffd88c202afb09b64c77b2bea3ae67bc6087fd332ebe1493eeff43efcf1d3c568e176857bdcece8be8bd58702d84befa7afeb9ea7ac1d24b257153512a72ab26055b782b2a46eb500e177be2965d5c8d3afbf3c01aae3cd99e15b65e40b180ef73c51a30bf5ce6422d78eb815b50cef99c97e3d0fc0b0b958389a8d1fd4f881938d46e6480a1981ee55a8f6f1cc35acf4f503674442d871cde0307c449e5da8240a4a70543924b26cd77946b0043dae112551eb6ed160aadc6a9ba5e8bc66fd100b6ae9b3c331e07c648ea5d0d44ce79624c7a2e4b983f5bff0c0287783b1439439843d47be4f041741dc40a70d351ab02c670451fe6cd0b29d2d78071c6360e65818899115fd0a1b5ffb11e4e27ff9ace9f564ba07e40a1c70fc62d30f5a107ecb6cbe417fb084c3eee97b12f2857a06dfab7d3eea0585055dcfdcca23120128b8954c1bc5ed5ae5e6e0d7c950091603892e4d8d9f7e3b6da4300537db6b9ac8fa07e6a43d4c92c9c451a1e0e9e8cb0b1f30d3d58e1306b666289bea1b79585d74f6cbfd0760f21f1194dbe9017d3b7dd7f682be41144a2811073d79d060ddc4ed8728eb643840f221832bfd49b3286d72e2491a77b3346b65d8e0e781a79ef97feef2ab4834e0ae1942ee0ee579aaf156cc326b0220f56a30212226aad856904a34136137e70c150dc5569c063da5f6e7e740b08a72f8fb43b2a89d04e866e58d4c4f82846683d5632929cacd8e78c4606da1fb262e3b14d8a070662d6fd32b0b3805ca6c812223d9c4beb1480d43d9cc03c0b227d11647f822127ce06c1c2c0b9c44a82117969091d4d618470e255e0ba09b49f012813732c8b7f847b5e9e240ab778afeffb2fecb99a7e9f985b3ba7cae6d60d9ff8fa44f0b42b05a5fd800adfb2a0b9f91f09b6bf5d568085d9ed5a1c0153412c72688d48178d34d23671eb6a593c30bc8c9fd92bffa0c7fa801525a7fc5122c17cf28bdf75740bd042aa02bd5f330bfa6337f945d1ef29ab535861370677e3c84da7bcfebb4d030c94a87f22246b74d31e4536ac11c61b6edab86d7b548718b51fb19a7569ee1f308adbeb40ae1b52d0514879c46609480318900002c4db8230dd370d26b779b72dc865bb2067d388de1f8f0635ddbd6e14d570d9666db6ecaddb512ec70235b3bbc7371ac025b7a614a0927306b2335aead7517513987e51b929a3e750dabb747c447378c8728a0c427e808942a30f532e8c74116e9ca21ca623a82c91391f214698b288fdf43c467c4017c65c58edec3f35c5e0eff0392f064f5904b4b42083f937ebc098fd137164d78bd2429061793c60cdecae64311f7a002584128b4f6d8c11964540a7a502accfc66e0960e4c9d37a38b6b0df1648d2c10ab51895107fb3b529831e07fbca22e03d8e2763f3795aea244257d0e203ea3221162c015d7315153a9a5a3e8957aaec3be139015c89095d05e8dc0e26eff577f999e585865e221c692eeb9e7665e1b8fc3a607866e1634cd11040524aa4181de30a3c1336b0693e38db14ec5e323678ff3cbc7dcf14639f9690ef5fa718430a60b99f1a5225b97b4a3386a0d4cae4c3be83feb8c3ecfc55a2802ad7e78b825f26e59966c412b38b8aa869dd0c6cdcfaf09d6dc6bdb3ad5f15c90f8569d98dd1893bdb72f54bafdd1895f872b63ddd0d31e010e91763d043e0c8e6c2492ec6d69c108db0761968619752a5eac7a215b9882bf118ec169773516614b32c36e735b42222c5837fb035e4b0a05c769c2e39f698af9581a47202d78aca31539327274fba0f1671acd8b334118d20b7d44da872727c0c92543cb9dc09e9da29f09a66fa0e906b3be71499d7c10a34c1ab1b899c37eec1cf9b48ec80d84dd33d17a3c6ebf4dbd0c58e7044c5077305f1584df8e84e665673130046fabd20836d0bcca6f60805103dae644fce41a1735081cea1c6adbc8d6d7a8433619aed4b690813fbf267c045bf1e31bb6b0723502e5c829c8ba5091934955305acf80dfb614d28132de8af4ac9ea4da526587c9d74fbc9c7016da72a9a7821a12c0b7f6a8a8f7a7849996113a971e7aa40448c91600c9a6a403d220627d79df1dc5b27c4db26cfa4b95e0ab36c3bb95980fcdc3a98f8365812fe8ac97fc63315f31346dce9b223e4ae05a77453d61a57913ec23ec6d373efc3046363da199b57240fd8a808e4a12fcd9a4d681ea2aed4d1cf02a5df2c4531b10756f220e035c8e493ddf3e9cbbbe427a85d72c232cb6f16e92cc1f689954bd578fadd595dedb89c09a224c7455bb9e1a0598fff098bd2de97e08f43542e3471dd18e9408041e62bd31ee8f82db17c2e74e7677a573f87e15380e50b8767287922928114cb1f48e801b4860c827f3fc4867f1e8512196c362bb0479e91995beb97cd2018b49e9111978174282e8bb75cc11b7ea3a30129be1afc4b3b87e5858072c8bde1edd348f3d709f77f92900b53c7b4a403e657f827ac3c38c4b49a5b800140fe9a77009594587d7226f1efc74b60b382bd7e1195cf38e98f05b10355b6dec1040519e9ea1bee60aae9ee9780f5a17ad5b0cd21bdb51f606012b40ea8e4e7f801b0698ac332c18536060ad6883e7b93f29b2853c45ac8edd36c0ebc788bf620514b3f23e0127fabd6c9cc612892a56793020ed27a199e7e9ecf460c8f653d7eb853ff1090cc421d06195d02003cd6a90eb3c012bb83b716daf0510a5f1c1488aed4f34717d36742503b4d43c73ccc1005ccb6465ca9bf92071c9e20703e3ca1c80ece26c61da9b39b34906c0399f8b224c1ca59cf45130b345ac372a9ae67c23a0b514bf76a8cb6836ad42d1ff023d2951c5e6d9887e813b40354c13e6e4fbf15a10cf7ef2dad3786155392dafcb62a37fc40784c0501f19f6be0ad0fe2755b285e59bcb278a47809f1908954d90da583d20a1d90fffeb5acbe02a2fe33964f4a58fccb07d2260438d107a478f3255b1f43417e52c4f6336110708a094902111b82c35b29d8f2f7857058063343faa85517d650cc02b03aafd52cf5c992484921cf6d99b0d555e84d00bd26d56cb1cd5c52f6a637a968994c1deabb502ab991609797d8cabc350269ee6d772eefb25f9ac864605e62a797d410dd2aea08ff4fc195a89dfcd08ace06b9b2c6a2c1ff358c6e0b252aef923e4ad5af7c367f186dd70b8ca46a0152bd684065b9b40b5b522a1298555cd670bd36ef2100068540e6d292c8e6a0612586dd6b133cc3319374e47179400a2a7077baacc17d92dda731a45845349ba41d1bec1cc82230e2b51e8c47a3f7310aaa39fed6d35b584059702c7854465f93009b1286afae6a9d72cf81e9d704e4f067e5c13512e0ab8c6d9cf5f57edc39804dbd771f1ec86fd19700f559fe1da6dfe5ef733d9c18adc791cd300892bfde68378f6d1aa171cc5e78cdcf8617ec6cd071357bc9f5d2975141d43b79c0558c719d1cb24ffe2a503d2fe691039afccb1936e400ebc4fe2fb8c3276f34edb818c6a940188db178349a81cf811e4f65cf1a5a4ea2b19cb83f382e8d66b4298721521e8c4379647078ec9b93e7a0fc258f32963c4e475e828a0661e24fb94450444fbcf21c50b700392c4f3f01b3e0a71c60bb70b985892bdf5a8cda6b7889d10527511848f91739604d1bf08a968d928bc5376020a81d905124249e57050f9e2e5076adb84ea9bea8313e2089120eddfcaffb5494309451b14a9ba66c58dca494c69ade387fc31fd9a2d12ff69f840b5d3aed439fe37b22f26ae4d8bca9e0c699d8195e2c814b75cd33cac8ace0ae504d951077297827c708935b20f7af595b04301d0635e9930f8c3240d7f0dcf9f9b8bd803e5612013feadd3f363679472e2bc2b9a538821275e522192e5a9c0ecaf6b15dfaf20b93ce8ea4e19678bfa448191aa6bb9cffdfe7582f9908305629a0cf9ee6c788186acaed31b164414ad957e7b8e733d6240d7c3df00fbd5f2b2467bf5997ad2281fb69af60057dfd22c8fdd6817a2d0d775d855da8ec374bfb947e97552c21aaea486e8192c7b5906587787b4acc856f618314504a95fd66699fd2df24f52b5455912ce96072d12bec45055fa3c4973aba2871abd39bbeaeba8ceb5f5e252f2b4dd36a64775054d0a9b468e7abc94459964c918fa20ccc9c07e7a3e6e81501f89ce2d9cc5fabeaafb95a94fdbf4d448f7c4713ddc4f14956d7708d6f96be994306ea66a233c5315d327ce7f746db159b800551e8ea147e0a7c18e495c7e153cd8051b89857f2c96094ea9162135c4b92b25fb89aaca8bf8ba5f8b026d65100f0dee2e86ec3644a0d222e945b8ae5e3ee537806eff8d91d84972abc307c372051cc82a830872b94683a34a75fa2040f3ce7f4bb26daaaec80094791887a4c9f5c770b44530793a3cbfef0e1bbbc9ff7c3abeb786abc6f1ca7cfd477995bde7b098daa9cc8d30cf9c96920125a7d191d9a0ca3b21d5b60141dfd2849811c2642c5c0a9de9b3b22f98f322975dd241170aa8ee4a044bab0f059f32cdaf0ce1bde2a44ba599388be8f97bb83c7e7fa365c6eeb3e490e26f416fe84e95f215d5c2fe9444c8d5f41e21478265446e2e74702fede041b313089f43b590b085f9e2bf45347a9ad29b124d9938689692bf50a61ec83b6665885e36e242b28eb423e1530bb3c624cae0c53733e8aae78c913214d8fe0071077eef2500b753b6052443fc7dde4f9b5e4f25f904f7addaa8f7b05de354eeae06733319b7945c07a2b62311dca78e21fa8fc0aae7bb9c9185f3c8e06fd0e95e114fe33b3b89f37261e911a05182460e0eaea0fdc32a0cc71e9f910c8055a135e370162a33335ac54a4fbea622c18bf56b7e544e5b897f6758f27783200027489f2086b7b3b10ef09007c19ba2307de6383b85cf2753e559350a433aeb10ad2042b315749ba6c1abbd9a2db768b64cfd8e63fea3a4fd622bf1f625f63c5c0a0892df8dbfcdeab96649bcdd89951b4b4b9e61be8d6ed244b0298cde3c09366df126bb18cf12ec4b2972d1aff0cedb863e7af84b253cd765c8c62ee580a306a6687f357c2544e33a895bdcde3b9abefda68b6ca96f9ee4b998f791aa2502cf090a184256dad9e133fc409c87563aae4e5b0beb742857d1b8ab5f5517012cbff3b05a7c0b096e2203d4d7822ae1c24b40a7ac2af1de722d2bfbef82160dcf2c0769607efb1862614e52cfd0f539fe5cf797ebee7b85435f42a4e9e8a8d48c16f92f095ba52295de663243cd16077e25b8466e6953402def181da5aa6e2f596466d614447e2d18e0dbc218bd11f2e52889a431963a776adac567b018ccf31ca5262e62641449a7c8dbe91582fa511478cb7c2fd7b1add41466c3ef9f3b18e2d04cd6cfd3f4d3bd4b4b33d4b415e20262461d0607d0bac5032350019c8386b315b2bce4dd6ccde7fd1afbeb0649551fab8260dce49d616088cac38f24ecc01f0068c93879000368e96e849439ed90b08b4db5816e4e11c910286eaaba7d8ec4cc7afabe1010e011112b5f72adb9647a77b973fb259383cbe0cdcf2e38fc993a5a9a9318efc9657d5cafcb6e2e86030e284cceed7f3a95d4c89d71c3056300edc031a8cc038fe5470dbac422e24ba2646dcc105dfdae4a5459bffe8662fc1e1dbcb2f4a8e67f91c66aad2bfc3f8ca7f8e862453370e3725405daaea8cc36d8c2368ec2241a38604526491f581527a0f4520db08552a737025b27e82773443e238d58ba4188e6cf331c4189df824b3ed0d0b56d13cd18ca56f978c339f8fee0ba94a7b19ba43b3eecb73c489bb5228941521c70e98a142211c49b4550ced710803445c99d064905c15f059a14c65c0cce4c8b65028b21e09ba216f5df81b6c4bc0eb8213f35e1c0c64bbfe4b72acced61d4776b8cd8ee6430eb1d93ccaa27c59bbe7f80db2dc7d33f12ba6dc0cff464be9a40f00f49f7378ad046de40de95522e08ed69fd073a01688d473856cd737029a64628b055aaf043c8568b6e2f9d18f1c35ec67624c8ef04658e1cbc933198cb3a59003609c35e9677fc6cfd9d08ee80f3236dd476845776c491fb6ff38181148baa5aa70158e8c9f4b8ece3873cb0727f923e9396072c7f6d58f0adab00c77265aaabb46a8b1071586716a5c0494c5e576ac3646f0f4eb2d22f98f4118aec41112dacf8348118348f604111ecd058804692ee8fcaeac7f9ca8fac8e4dea591558ecf415f22374a6230496e9d9a749e14412d603da5df88f74a68e120ea91517d807c79bc70193431b6ae9ea308aab01ce1901c38ab6e0797f157c541cbdb83090ae3b35eacae8ee8d0c059e2876b9815ff8211650696adbdb020036737175c64a4074ba019aa65e8eaae8df9db3214c0fe14547ea89e96dfd96ab0e008ee0a9d16b64265bbcd5cdd6b69f715d7a90844ebc1629bc5f8c3ae50c157a73e62f0f9a5b853efff02eaabc1dfc3c2bba07f4e5069b15cf9ec03c52d74f71bdf6e33e891d1ee316bb25e9b86f558ee02a2ef255e682d6882ea74ee2138a5e74216f38dcb084657f94ccdb3dafe32635bbe5566a2bf5cb0cca34f2bf988f3f44a4320435a3d53e2d37bf1ccac93a5f989e2db0df9b6be000cfee60f41c46d779993082ee69cd165e4de3018df75dd39c519f7b7c8de68e45ebc666da1c41471897a5313885896fc80319a28074c42b3c55c237302acf336f3f5f0888bb49fa4c8c7953b03e92991caf0264d58408230292f584504164e7dac9316eae0c699197489178e5a4f70bf2ffbb782decb7bb190daaf66387f0c09eac2f53b5c0f8a7061362b66c8dc96cc25ad782e6a66c8c3bb704d373431b3f23a6bb4cc8d68c9b431972eabc4bf38787755800f258b3157c93c61c065903e5405bb48bc6be79692d231f802bc13fbf5918d6e57343859066ee4df6603e46ff616c7ccc56e5808095980bcfcfbf0c8ef1ec1693801a1674b881efca32dd5b2e8e76eb05fd9dc3071775a1b04fae103ec092256fef39820c1fbe935e76e9c27be0e94c60a293e45dc1252665c47f7bf2cc3c357d4ba0096bbc4cca003d36fa3f60b9b9d389cf54647b8a065735785c071896e3ac87cae6ef5061daf753e595f7f1d1fa8e21b1993d640e2ed37c560e52f1fd29320ea17fbe4273cfd7e048e9ef9709ecc3a84892c743a5f33157dfe7e6742712e831e9ae8a988c13b91a5c6d234f7dc23a7820e4e18ae7f370c71f3737bd429eebdd0a3eafe8dc512602f7cd4e7cfefe610634b23c681153cc8bfec1661b7d98d69b871268d610aae993e18c8bd2ec1b6fbb049308c3c3fa9ff7a527164faf45b10d1aaf7a8c9b232ac3ce625ca36c609bcb0f6d2f7fa8577d9d878aea4cd7431a33cb2c905cd5bd697c5e613550c83c003950516679552c1f90ebe039d4d488d12c0fa142cf189456237e2d874cf16cc80143bbdf903b47e1609d2b53b97f51c52fd65a950f6b64485912a862f1fe787cb6d9675b85c080387109c89e0570e47b079ba52a260f802587d62df3c75bf4d41b25e643c3ac876f58431f7174803a9c6dd2610f48c2cb03f8112cf18a5d9d27cb4f0709fb14f0f449008015d25cef5d3c3718e691a5f3c3c63d3df1369416c3b90bf08f044760db188f45a70dc672cd6df399813e76a0215a6bbbc44899c513cb744b1e6719c4e61023af2e4b2a38b83e0a891b51b036e0a24b3fa28deaecfc4d286099c5073bfd92f504ea89bfdf36beb0e3ef7e5121de09e42a702f7a5a7df2a355cedf5e038492868a02bf0d9402db1a491d869296412e7147d2036250927e381d128f407ba134a25388c7f9ca1d9c81c65e13ef86a123634bc32ae3e8dc8919ea6ad30e58c7d2b6390256644a5ed9027b9c9b8847cb664390dbb3dee3350e57032a00ce1a8d6b19fe053877d800bcd6d77c01d8ae22ee6b11fcedc51a7083c50535a654e531a5983a6b4d5cf94b6f0d672529557c0c36da4b0587f8664da8698d206237c2e2d6be7696ec60cb4e2e0151ab379469a953b2fadccbe561a370dc8b178f03ee3d91ffbee75a6ab8d58852f25284468f1e6f0a23abeea0480469917b40751cf4ec04f5ae526de73d380a0033c592f545da7eca6c668a549f842a272f7634b97cfa7a8c8684a7c7ade9494eeefda0d82b518e8c5310ddeb9a494285a475750a90d3797057aa2d3ffa95d5543524e746958e4b097c4f5b1b9fc467e55b378e22e0d53b0f76722c5a473994fe03acdc984af68bb4e109a3b743267c27a492b48465ab345bf397bbfdf9ba6b60fe3fdb3005dcdfd04a5528721c750b5ae0cd8483f12218c5c142cd66987c352064815e8946e62dfe51314652e98bd4f319f3cfd144fed1770cd9596069d3f58a25bd95ebd64d792ce2e6c543012860132f02292d21e983e1715906dc8fe2001fe9d0a8640e14e489c1944bdde7e1796886916999c7ac765e06d3e8633d5a5da7202a280a15e08e91971280980afa58ae676eee2ad5db6043cd8a3bf775ce4c54dcb35045f775ce88ffe800ea647a9619f749219a99632f5160bc4c8a16145be542501000f4c60934aef4374c291fd659bd5b342b94a701f717ade05b38bcaffa8f10a95dc824f697113f88b14e08569be2778f21c41569e00c3025ee3f87c988360744501ecd7e878306d10b53ec36163b8504b5fdfd12b5e96f99dd209a8730458d51ba0e38047c2c80727f977b4a5f7c415eb0c0fef1cdebe7a51de35bea932441f4f8ab2c97c19980e51e3ff63566451ca1f23a0f4a3c07636896a94444528c1ace28418d8c6704ed112a3115cf69f00dd8f6f461ba47856fdb4b472d7d573bc3dfaffc92afdcdd17339d832ac73adb1b7ae8a6405fe3d15dec4d2fe19aabe699011d935bf6a05503211e651df27048cc6c30a2b7bb7198cb030514dca44a8568a0be2cf88996332960ba0d247b695ac41aeb8eab321f434b5b4118af132aa3dd341e34cd703270c6ec1bdc1df76291c89f343706e04542e258a0ee473406d532b6898c71a0ba9407c7d251cce3a4006c05bf97a7e7f4f55b4ebe1a24e701e7fa60b940567e769c15953f6a7c05758c42071bd33bad043a2681d3462ead6b68aed7a2297f8dddec05046e4d95518c55ccc6253cb3352a53dc5ae3ccc803101aa34fc87b1344d18c1a0a383f04ab7434a0755ef4cf541438b28aedc31d2b14f6364e5e9ecbf42ad712f686c359fb3427f01065acadbe1fd83a7b20c3e90e42906af0c53de5c1153f95036a6a857a1d97d2fab1fcc1d8f82d1acf2e705c4deac3a6146e3bcdfcd0b1ae2a4606a265b6ec9abc4ec648d996b022bde2721d4d98af4332321e6a2a3d86d0df3dfe64327842959f683af8375b6c263ba594daf26a345ae8e1cf81db081bbc8648f76ffdd096cf06425ceabc4f9e465cb785b6be326c24c633c75bc71da0a93e24c876b9753dff1c6f61d7a3b77aa7107ad90c10eb41375e6878439a3a9b568b028a92b120565fea7eb107f6dfeaa0f856673bf818c4d78b39c1acc05797e8c03c4548b59021d0092ac7af07122bba42f9533f7f9fcb952ed74040540b48499ae8350a53c34cda621100d6062966a44b99d86b76ac607459d99a668ab359f596cbe6570cc03376c9bb1d638d3be9a5f396c16cc1f516eacb60e3216d641b4c2aebcec446c329878f0257a48b791afa128b2bf0e54fcc1c1bfca209e620966ba03bed4cd259bc5e7563cbf252d1d9243869d46bdba9b7719ed1a0fb7e6c9017f56a6221871e69c9e393582a2d1b57518c6e3e3eab32f32a576ca18882e91880880a4402f48d7222e14fe5c1f3fb87fd48bf76cb860e195a1e53b1d594afc96343a4b895620829ffc0e58d6607af887c15cad1047f132de4097d93e8482f30d179533393c2e43fc8b40aaccc30e482c3deb971ebf7e2c0531c3f98c5cd3ced5940ab880b0278e143b97367e2be5b912c9601b155ed495eb4d34a0bfe858b99ff7064fde287ca65f24a1eb4124dc15fce08038c569c167cea2335bece0d08b06fc539acd7004cfa06d7a90c697ebdc98e2d1ad04139a1f18f484274ab41f9caa06741af6d8e2358bd406df87858148c31dd3cbae2183aeabeb34749a7671efc2bc72c0230de70060da1cdbd31e28029c5075ccb58a531720a6820777681858d39cf9221928467cef12d9e708d1bee108d103a6aa98c9892e222f1bdf25cd75eff22098a87e0264b01ac1439c9726eb6b0d140bc315008d7c4350caed1f65210149061d5bfdc70dfd1fff00ea576c1da34a80b2bec26106852ff41e64e328d76abb3ce7ad6dd30b6aaca0caa8e0fc9971239a0daba5d9042effc05f1c05c4f343a95f9d282a9bf4ce2067ccc9ce33f863b29b44b67c0b63b3064400a7f12b23107c9cfb3b4c87c44c6152c659cb3351799dbf9ad95520f0c8f02f952d52fb0a1680763022b6801a31124d553a989e62056174a8abb433d8c46410c069b051cf3ae48528458f041418b01ce7f073e134fe1c8180f93934a688ed56988d8d6502e2e9a72ce4ac5c963195e3ca72f86b408a6fa7bdc3a2409e59fad5f011af4ec358d2151168619609736941072275ad3a82f95c9efff100fda748af7fde2366e8cbbba521cb6af4b72f81136af2f1475e741e1829face541be33dd8855822c9ddcc33291321957577d7077fe94394f7a3a82fc1e6d6b58ade04b00099517c4a1490e9416d60222d605b7e269199b8d6c1a0d602916324b9ac359ba973f4e2500b3075ec90a10666eb38a03638d15e798e27a52c62c7e6a9d807661137796b1e10276bcb4a45f8a8791282e5b35315cb67b5900fcf865f4852ade382a13c3b284daa321794936f63ca2f9e3c0a8812052b2148f0de6cae44f787efaec9424561414d4ee0fb0afa27eedcb24035c75ee78acc5dc809e321d89a6cd56603e83b5e86ba26ca38666a1b4ac22a88224480f4f6c306fd68678cfe4751a10e31e3e0b34b631a0496c64f4111c7e8136709590f65f6716ac9d541f47fc4e9b6bd7158f256756d875002b20ec31bccc2d1784218c21d6fd9e8b365655f0fe08f6193883c8b2bb4a9e2dd18c807e2231896078612dc8d35b543dc42865ab688ebc5577c20c8d044d5747d72d2e6e003a98a20fdc90394fdc9711eec050ee9c63f08be1e65c55f69f7fcd4fcf564d1eb67b402f4709f1882bfa3233c8ae6fc363e5013e7619da72cd5c00d325f302c063730aa13d0ed3456489997464ef33b95aeff54f6317b5fd7d370308100234ccf10c7d189decc3766d8e1293d242600d1059638ca05daa5dd37ac8d4c5c701795673cdc140845ba581e15d6ce1c51e897ce179dcf18d1a7fc7ba7377de1505b43cca9c4eee2aa97ce47a4fdcab09934efdca3ca8ffd1b9d99e247e55b879d527d5a85526bb12b6a80e7da5bf27a3b5789dd7e0ae0e388139e3bbd8d1cc8764a36f9cff07235804fccdbd1e6062ee618c4ea3e64e9659917cdfa778a2a251e09a68766560732167ad01338d539b80339afa40e2518f29e4ea1fea45eb5331c0ab8a2aba7810566818839757d749cd40e232ed6f3a12ae6f5591dd8d0ce15e3478e3acfaffc04e27eacbb36d2986ec8ab7a14d0331a847192e17b68bee463d6057c5a3fc4cb2a5e0046121ca782fb5c95ea7844c342052e72385f976b3ac331b3284bab1ab745abf61683d253c8dc71bbe0f208f04e20ec1cbf7eaffac5693861dec3f1b21699e7ae28d027388a009db81e6683df161e73f183b26d494200314e18837349d102f494604a564f4adade0809e0f01f11810e036f3f7652e17f3e1fe9794a6ea0cfa05bebf73f6f1f94fe4559295ca8b2454eeac1f201ae01d7ad263151c00a6be6b28b741459ff5144f7af09e9621fd375c277496842b1c5295d13fe61f0145bbef8623a6b5f6d67b38aace0d22c27679698d1614785beb3029cce84e82161fb3b4d5ce50aef6fe60773e0a9c80f53e704047e336181c74a5095150999953fff50db0ab4d929234eb559df585650721b7bbae4e74fb9ddd933dbb88b215ba67749d3ac5af127c7a2ef3a7fc35ffc437bb99941860cf5abe02f987098102f2b0e6efe4b7ebd2ece123663f01190785ec3857494ec1a02b1f835603282c2782818f627100f5fa7d07d6146bfae4f3bc7bb626aebbc9cc5e514e96715520ea051fa0c79f4fe2e647f6e33e33d003ec39db9772d011f46634d3b677165a2d93465b0737a97ae4a6cc40cf8d4aab6def2994c7558fb9d21e3b8358ff54c89f520bbfa68b9d2f9f6f544d15baab5ec747079f4262c7a0bc205b5aebbd84acd7dadd0bbb99e16a53a0bfe51064bf368d13be6944225fdaf83c2203ab2614b89ca92ab530db6122e840db436aebdd34ec9d29bd90b3e6f668892c5163266172ee544790a283144c0ab1936f4342a6bc6da3e48ebd4694da39b28d26748d1a51d93a2dfb17eaa24e0a6aa23682d3db7c5f339592e9ac16e3a9011a5f10987dc4b96cb40ab32f0de58c9479e150fcb8e7f966777d6bec09ab79b228cdacaaf55aec363d84e11545a9749aa96028f7fb904bd11c9c74683a7921173d788309cd3baa00cf22f2108ea35fe3f0a50a6895ca93b019d9ea73cd568f1d428cc77e2101ce0aacf515e79e691aa1f1d4f712eabbeb09a009622d5394b0d0847e0b95c87b8d479ba246cf9fb8757d6a5a3882ecf31759aff777480f8ec531923af2a3bff18f8cc3cb2ebbd29f352b54a64d2e1b6381881a877c1a37db0388baf122cb35c3551b809749e08587c3ccdb10311f6015b4439e24ae56a08e8c71d222daaeadfdcc6cae79efa01e86ee8588b3d7878a61fd52f22d082a62667baecc4057abd9ecaf0e76e974370684d598bd2b97b6e68e26844baf220aabfba67569c46fdce78456cf0d8900a82f2f9238f6dc38afdf1cb667c80be9a33aec385098b636e861faba027f18ca535e175c50b7e5a68462a0b9570efa6dae9f06575fdfbc7fbfbd28d2c5f31d7503d631261270daf52a33cad9011959a0e0cd56e16d69ae88195aa4a64e9c0b79dff20130fc08bef36aef49a6628748580c773540d454b862b0d7d45fd485b83abb224e4735ed409a8d5f5b3aa0bb847dde052a5f9d57432b3db0bec64912f30e899ba60000d08067e76e10b028e13cf7c5c1bd9537148c63b6e1678778cbeadac3c57881f2fccc1a6ee7428328bf0fd37eeb6e63516c08ec7685cf4a2156eabc778658df578b67d96bf9ee202630904243aaf259f49c8fcb1ab79ecd8c67db72603bd23c7cce292c532cfdf3f1d9ae3a3ee0f6218bbaeff56f6a74a6a568f93b4439d92284e109d51d22067e3f0294542f415bcd54a1401114c6f78440b646a4d0b7d1c9e0f0937ce7d12acddd1b085810e4b1a7d11f956e7e74722dcda6f22b3abc72426e43194f811373df21001ae929c15aa322404cb703354589888249358bdca6a59ccc602fb389949121722d194b523f0f1c285b4f78b17c2805e07e6e879d4e0d62bc8126ce5399e61de4dd76bbdeac93bc3e0111885cf42bc602be30bfe679103e5382878e0d6992f1bfb7d3db580f224a95569487690f82cdc026ee3901a527c067242e3891f08f5af8b3445e9ab8acbac4798d001ce2d6b10f6550f7c61e56b045602b6ba74889190a2c5e2059561db2f6cbff5c9a0a0fa156ff6a5cbbb7264784f2ae0bcd215e512983cd95a8899adf96ac99afc47f890b2f1bc752c91ea1b9e48498bef6c99fdf7d047925127b32389e20c9b0803c6a5a09dc93ba452129689a08b5e77180e1b1d79faee2dad8ff5bc1ead63cf7f06ba2579c977d9798af8d001d2bd6d9cb8e1c0641b07a8bc2855f41adf03017653379f53f925c086e50469bb46e269ffe5b6837aec0ff757b258a5187a136472d8a7562a11da1b72ff7b367de68657bcd25009dde4d24954ee7c6ce7be482963c7f20ed3b31df0dcb65c77e8646c4cccc494620d44472408570f8c8a890036651265800f4cdf6d2ba215392fe7731a92165a429611875201d3aaaf2b66eb84d4d9c3c4a3d3d055180a936bb4b5a4638739030cdba6788f9d17462b31e574ae43089ba3360986cd2c56ad28df63d6df372059dd7b8f2ef574fef6171f4a5ec3eee65e0050c90faa10e7743a96728d862e7e914402d7c9978a909d96280469d237905faee6f9913b5420ac70d6238c7234e1f9962adb18dfcc4a09a6ebf59ce25961f67a6e76d4ddf201229fdcc4f8006034c5ec06c70f82b0448bef3ee26cdca7be6260f119fb56883e82ed2a085e8e0b35bb385a66c9e8953053d8f5715573da2a397ae900f451cd86e96912bcf49e857a83725b24ade751c86883fbaff40b840575407cd661d0398088dc37153681f46e8f480206269fd43b9cda09fc37213c6feb56fcf792852bac721a1220a45102a7965abf7670482d7dc18fd60380a84ba5f0320b7f0f90cb942e057d037594ccbc09cc227d6afdaa541a9a762170cc3208c109a62c60f467cd071c2aad73e4c0fae91ed74adff11d3582ce896c21fe31e555551554ac061b53a30c2dd29f1ea9e2140da45bf231994a5e5417c60e6a4dc972ff0034707f31723817f24af9646579ccd01e7db945d6e2be3beca78d5fccb15e2cca9c3c7201c43992262c32c2a2d8de16441de23cd14816842af30e253ee969f6b168afc0502870567c3d854f97f1fe051b2ac53c47d01ce37083e12a22b4baaf710c9a2b69bc96c011061fd982abcd062c93ef8febdf001f68c361783dd6b9df7b81766f1f1e1ade053353c6725c8d77ff9eb03b2799c58b94683ebc3c2ee831d61df6066ef661e2b5aa7e340228bbb26f36f7a1b6fb116a448fbf716c84e2a9bc974a46cb9976165cde47f2546376f776220cde4903187717a3cac8f2d91965384737a70212bb57302fa1b61f0a82fddd6f50dc94721cfcc322baf8b88ef9bf669e0f2f98a0dfe8422515c83d6243b1da3576bf5e8e3784e60bee79d52637c03e3e412754ff33110dae56a214cf443600e5915f5887f184abc8550cdbd4a487bbe3b4ad9c5d8eb9fa33fb8d0a56c18d78670d0a8d840dc48126aef9e2faeedfe97fbfa34d7099df28b670fa87d33334e5539e12f175c0b643fae864465f5677520dd14193522e526892af81b9ae87f309ef531f46db9ee201d9e1d25f884df4c42ca4c5c19e2fc64f1d159c3fbe7c628fac0de56a361cd2d91fbec5a54cab4f6097648eb4489f7ee559d268130867ae28f74f746d23afd2cc5a84217bedf5c46514e70949270c94718b0439a5b60052ec0a4c3bb9fd10fa1e98a22bc41ad51d4e8a940bcd875df5914fb61b01a66f010a2929c7744b47cb37f3e59202bda8c49f4bdc3ecb131e9ab6d4b0278698d0db9e502128e6aa36e5ead587e6cf179c07044dfea80cdb1fe71acefa477d30208edf3dbbc8d8402a39d11c4d5aaebccd661a3e32789fa13f253b77b8579f1312ef675178ea950012e5c07de27401c87c7a9438dac7d02592c3d04a14b13cf0626dbca7bbf62a8ff7705c3861459978381b3db4a0bf8e92bb60022eeeadf8233673123db73b7a571d452b856f7455edace5afb5ee50e0b2fa8893e13a1e5b8e9cbc3e270d82ebb0f4bb79efe3fc6010190abb6b248ad9157fa90bcf3758f901ae6e169107904abf82ee0cd800d8166eb6e1037ab8fc9b4a4b697e33b158ca2f28ba6a1d0c7160d860feb34c04e07b9122f51ff13c183a32ef8d02c1a31562f163254911f96c465e925a280001d7c1e3b0156555754d3d4202ee3cb656a4c97c7e72918d72014aa428c6963edb28bc98305f7fffe9894c616c4c00d3627644d71e1af493eb8b3d910640c7bf7005bb7d5650742c76675ea3791865b4027b79dcf57ab87bd6252770e90dc7446f8b85b7612a359689c690ffabd1684aa27126d27efb158e8c4bdeafd216cce008974d9bd2609e7b364900636987aa3f1a3670853f32b3fb3de9f8a19d40efae4cacc439207771118e267ed1be1b2f329bf3f8b4a901638a7f57267475a0392c5ea6a3aadbcc697446d956eb15eb1da62c63d3c6260cac3faae972de21e50d6344138328908a803122fc8a110f9cd832ec061a8c0e7189ab34503762506121b65ca43763d96a56c472277cdd969018ffaf0931f502836bb770f74f61e692a2adcb3acfa7dbec2f01ab453d2d2f8030c930ee25200437d40a0d9c3a0216aba876750af0103d1d0ca5ea1f73925de858e45e7cf458ce7178d49b5f3ae71b0592d94242534d502edd0c8e38eb702a9330dd2f57acad81d37798776e3cbab33e8a67d75c7e868615d40b1680a80d14329e721c3859d6e28c91221349fb04a05bbbe6177ef5163357df23d3c59cd38980f1df76dc1bbaa1889cbfeca4659e8902ce9efa703106d878d756e9f7f94c96a1da7ab1fcacb51492b4d9936a3457a19bdd8f7a3dd1ef689a71d102c6beef36120585b0421f0db6e594394e13d3701515d481f0bfad89e0f521e735306c43ae35d8fb526ceaa2b3aeacd7ce7426e8c29e5e91765442329bf062d5422cef1f5d623fa84ae28e1b3a3de24e93b28b2760558ca2af7c35237570dd51386a53340543016cb42716cf9c5c293cfe9cc973df520314bb6877fb2ca3a9edf5a209fe299f0daefc1425b179d2ae8c3fd42754a6b8fa346cab8be1de7fe11286becd74fdce8d0050b07e067d517d2497b87fd9766e8d7f02045315a79de134638c84899e13b8ba06059f8d42fc0f2c73d27fda092838d1bad5857a2b801f8efdc2b56da7f978bf84e19fea5ad54646098224ece994651dc3d2472e9edf0d46db04fb6029ca536374f8ec6b5589913a71d91c704ef8cfe707f2a29a3f8eebc1a14ab83a52da51812d85b0167a12273b4ee964360029f66a6f840e8649ae042cb5f42f332ffb06f3ed64870a4329c50902356830c2acea0795ed0e5eb367d9b18c7154135ae42cd912672be65aa43162c133826c9ffe15f0a0e693014e6755a8ef26c0b996e6a40bb917958d16f618af96335906435e292ae5c6dca5b32a32f5a7b4786c952e14f5579b782c5b44cd2a67a8ebcb308ac7b414d446b25838ddb0ed27f9a2eba1fb4472d23e5c211a357b8fc439c052d90f12302e0830d808736eb437519897b51fc6cbabfa347227271c31876d456863936f8d2c63af902f57a7bfd28765eca65f2da9bcf57fdcc2198de97af8480c2ed977234798f9b800bfa8705643a843515210f437208a92a49c54753a867f3587d5f92b29a2318f1c0649fdd520f01fe92f72b91981360a04df6431356ea4130e938db96d634efeed064a914173b796e7e1a76a33d13b20c3d5869e9b104d7e8d2c79fd201585b6035494140a817add3e8d734651520ce1678b82023ad6e5afd4175cc68c2e0acc3e3029add4c82662b5c19e460fa51d6f29b5b914864fb4a019b2e9c0275b0ed6753b57c823d2142515840ff74eedf95529670c17d676eb1728ced59ed7cadfe7a69b4431c618a609446d433fcbe06b8be1fab14225e53ace89b10b1542b3d4166dad6ca3cd639f2726b91355b7b632eacdb505983b189419cc16e663eeae95c7a14655b15776063e8275b23fbd459e5beea9b30c9046af96a4d04050eb860173fd7afa8e789b119f8277c7255fc26bdea77ac74f2a4cfbf1b0ea749122bbb54ed4024487bf42c6da9f8b3c3cbef057f29372bc093d15f997d3eb0b248abd40d7bc78daa840be882ed0c539c7b696295208237293e82087bfea4d29268c93736d932e142844b7b75166cb4b5219530dc3ae91813e984c5c8445d013091625d1a2085eb88bd7d17816363edba8b57d2ec2fc0790f03545912a7450ebebb2a2c496ebd2508483fd43b4bdb9d972b1c5c44873c1e14d85d48aa48bd887c1c8672496279c9651b58b154c505baed3fa9e74a55076d95edf8de963de961041fda92297be8febd3e03cd354dbab5ffc11df7625939a46744d15f32d1cdf36798437d6b429f488f2acae236c41efc41f32ebaa06ca956acc325d0a46b32aa05c71c113ecde9b2a2d1ecc1d28b42c0555a929b9cf4e4cd5856720025a18caa38503d7cc841b548553259fd068b6559306ca4571f022611b0e78c257b896666ad03203704832d186b05a6298b776eb78c2a3088e70ef5ab6abfff7f5c166610ea05c3113ed9b4ef26dbaa6322f50728a5239a5d69f57be9864124f485060365963bc07a8dd073c8125a0563d92d0b76cfc1c85814e4317281346dd15d3268f430c992dc8daac3b716588e39e014fb8aefa69ed218a27b47ad7248b19acac708f6de131c9e3efafda6549cd39d27d559cedceac4079dfe97a4cd7543593824adceb828d168439f070fec92ccef2812987d9a7467e9abff47da820f94ef615c2895150ea47a6c0a8305261718444e6ce0a7362ab7aca4cf044b22255ae173c0c971e90ec0d59d8bdc87b54811421c49f2f38857ece1a2932b88b944088e7dbd3ba35b78885cfd73b6d8da70c0fb306d9e43912a65cea8a055d180bebd27c75d446ed516205ef0db7c27d2ba70651a823220f8f7344c4c96cd35f08615cfd293776325d9aa2cfb682a5bef172aa68a070d2b74ad98b0b55e02238237d6c4e1cb14b642bcae76286b7b12b981e2ea1f61441f42b1f9469fc462e5356c07b0bb799aef8063dca85417614427dc9e3d824a08a70082003389367772d1260d5a8100041aa054902e303c7cd3781af4a5a368bcfd663defbfae86fca64e8db1703a83cdf5b52d76df998be1dce8d307e99284f58c2cf337b4c1f105f71d0c8409c3e2e6ad6d6b11c46af369f80f60b115256eaf440e7420e537d037e29fd1e2f2b4776cd108564836841967efacccac3be57e59709d2886ff4e17d1b65ba3507786e98701ef5baf9d2b8be5e54eb6e99995407adc84f0d63d043d548e807fa3a273147feb7b9973119e84027f4ed1ee871298ecc2b4f3a7b473da594e5c4acb504eb0a1f039979f76cc38af74dbcfbe13dfb343c2d39b2ac6a22134c398a4d802150d0a2a814b4a8c2c54c5488f2765dc89c13f24a5826d5f435d98ab5f229fa2d575af18ba1e7f19bd711cae5aae670d1abe673eda11bf8778d5f00eeeb1bd6cc8cb8fbdd449f438b2e4653c3b00eb943dcb4371623fce20f8921bf2ffb7daeb128770fbb4f0b805d6e041ae8b734efd3243c8be6c7daad0eae365ae5eee749c5bf26c89891c5f43b2f8042c578e88bc58debd3207011283b3d2c684e5f31a796638140a7b85bc08eb697e28afcebb9b58b4459e3bd2bd8b61a64621a81e702a3311cbd1824d6fb32c7ea9b7e0f9c218794dc424b128467b36fde0e775de47f2b9825c4073aa4c198260645234d09c4e5a4abe097c1e07029f07a7cfa9a4e14ce93f71d002de732c9b52be709976a0a5d221c46d812e55a369cd903fbb46d039c38affe2936622f59ff5b9f44049b891289effc604f8aea6015f0105f43cd8ea01a66e8cd31fc38f661773483be7f1970d1a8c54e4e9c3722581327d9874d850aea44d20e5ae14efb38c0c44300db83445f68e428907cbc3a3ac97e1eea91cb4a635e5229f53b1c55d70289f79f9aeef48eb76bf9a9a9ce261aa4f07eee39146dd02b3cc2c5b153cb710e5836aead9f7744e0390056e4f6253d9c0012256bb07c0a6951fb32f66a092cd5600f6f80f9c5366b1faa360a37ff46416d8ecaef92de9ee66003dbc27c10f523cbaf681eff381a906f48102dd73556d6edc1cd4b92a3f3abe3eab0111cc9b9352316016490201b2e941705f2a224d2103242984ebd6c8ed2257aaa38625fac7a563f3ffeffe9c9f5af4406d122fd3582dd383c5ccd116b5fe6d182087c1f3457994c6533bd2e81a7af65d63f0b83458016186eab1156f7c7b09d3c524062332db0b7a627cff82246253d50ea8a7ba34f536ba12a2808b00da39a9ebcedb3f10ea88f94b5d4d5fb72e38ad32f4856f66f96e8609872c742bea16bb93a9c6f70b635f1b4d113602ab283e24655e49b587ddf1b821236d57d470e31a995c4fdb7a37ef5ef35914a8103244873e5f9c67d05b0757fb1287d8e4f5db41dcae6a2ad18841e581d3afeaae34d315fc789f4c47b0da54b2a2d44fef3d0c676875be2ad77bd5b5bb6e54472b37c1cc491fac8a8f4c7ec11c9160dc4ecad28848eb897ec09642557ec579a5f5881ebd9277144547572ac28fff42dce6a4731d3090b775852c20952b5e6ae1fff9c2a0d44e3e39e1e3418e7bb72d21965449696e0a17458409cbd1e319bca5cf04de2058947dc31e501124f0ca2938c21417395782d6ecaf21a2b08e6e1ffede8310d349c0215d505057ca8141fcf1901c228b81fe888603bed76425dfca60c70b1f76375666d051c246a0eca8af268aa127c7e71c597a354cf7cfd999e2723dbc2e84050244117c9da8e5951887438dfaec7112910d61af4eea760d3433ab093e05ba6e96b0faf4e7a7fbb4eb46517b9ef2cd4afbc81c7e3a6d911012301e892e9eb8f8a73fa039e55b531ce02302d9d0cb55adc55ac33a0e414e931ce6340e49dbc75b2265c82621604add9212fd22b2733b2dff65aabc12d6a9101f3c09ca29fec0effc20c07a4b102144b11b9deececa18f3b456741d48a515ea114e622d91a4f9eaa66468b99d8123658ce43686d7d46e4b284fb5cf694139c541f697603f46c5133bf094032e38248e2d373ca65131c3def099af6f6ebac40daaccba6faa2b5dfef2fbd364d8775907e67c770d032556ca5dccbe150684c32d85daca8ee3637106bd6696e5a2d51384f882aef5ed1ce05bde0fc7683fe82f5960727f5beb6258c63c6e0d121a9fb81166262db51adcb2ce74171d7ca31d3310d329359d6507b6c7619d523741aa43656b3af8d7d9008347a4a3f84096678900c69da7ccd448b5bc2a43b4c731a3c2e5813f07547dd82eb2b8b9fdcb8dada7139f2118100a5c83af913a4d8061217759b7032424c5927ad1bb8d3147a70df53715cf1bce2c45879ab23d7dd46c5b74689382ae8c0dedfaa6e78ef6efcf2fb525d74db68b27e80b4c2e525abdd20bb114d6685698f8132a39c88383f50f8f63400aa628fc4eb772f9a12f8d8f62b797f82104aaf3ab72ceb2137c6db4f246ecb3ca708d658eb2924c94e0f21c5199b96055726490a154d5327cf41e68bf93fe323f1aefabd10b2da65b151c47291e7d90567e98ae56a8167a3da1c324d88804b371e69476d10e12a2200acc466136f50dc5571d9494620ccb97bec825019c7d3986ffeb9d2b09aac71b25fa62ec488f1cd38af3895a1d920773e15cd4e068f537829f5a67d8e18500c0b30a38bb288101080a395ab8393c94abd0786431da424b48c499a38194e7e8a595984d40b85e872a9e3df3d42ab698db1d86995efbb1a017ba7bd517d27d8d8b36723d840553d09100259ed057655bc95d39684b98117fc2242b9a8b6b4632f6cb1f6a24eb1a8309fe6b4ec206ac69ad176d868ad994bb90ce59d43098ea2e9ac4f806a2105ae1573e67f76ea38f1b384c75fa5a62d94ec3044facc7bb0282669a2db23bf4aa672880c5ddd41b561d146ac4006990e4fe3e2ca38112a011d7178ed8102d08574196c61b3b4c2f61890f59b4e1f40046b93c022e5b4ea4003687f179b087a40795e96a242aeb55976b633d65aa16e9a7c750e46f1787bdf046629a060fbf3b9ffbcae82c916bea37ba8597f2fe12f11ab88b876c2353ff612321710589171c1546bf818cc80f4f548f27453249b66a162882f0624bb9a3cf7f4f5c8d6d0ab5202fee31c3442b5063c4da6398b2636f0c4a9cb3252af985982be0827fdc3572de9b34f571f70f903b52193f8156c7e949ae05e0018ef5493b6f3548d1695bdd794b73e18c1f3ab8f883a1ed9fdbd513e19ad22b01e09554b3e7ec2df0422b0e936af83f8b0e19708ab97bdfcb8f5d9ac74b60b2a40bd12fc08b46d94d2d42d2f2b9e3fe12c4e8717aafcda376e02b373415d9e5369dab2c90e15e0891fcd0f9c8787f2769f177fcdba16c3ac944276e87a3322b208dac73eddacd681d20b2d8134961fc4474403064eabb7c3f43323439053cf974905570ddb0ea170fc5f332096049c6f76db4cb336e1c0cb569d5b1d14101f6b89d66b28a23cd3048341bc0e6bc997fc4ca25d424ceb6211533b27ce4f6caa0c21d9e4c2df91056711f0e06bedac37d46add1f301c7e21a0363c2cf3fdb82454ec8b7924be17ef6dbdcd0dd5e4cf4befc44ee610c9c2cca5f954c63bb6de14b19241c742d5df6717949b678e27c9c8daa0b34702996ef5320849651e2ba5388ca9c1021f718d19fa90e6b4893496056fb30b8e0bea7672747775865bde6c7b454587d6988b664fd6cee6b9eeac9029b7ade54dab1f40e5c81944ba3b1df3345df0cee261cc943660264ad7b4ac62d05517ad7e0158d4d8a13a6df26b11bba0a1ccb68aed0d64ceb532f18be1759544f524de1df653071ce195d465225c5b434a03374af52e6b1c9412c7f28045fc3877be1d4e3c4f81f0b942df3592e27c54a918f14b4ba68eb55f77609a7a649e65fd10c9a2a0b396504cd45483e47edb44c4910b1e140234d640a84e42e9b9002a3685828b716911e31f06f22a9d5ddc98802cf1ce52ced05fa7317e17966b0f3e0fffee5ba76926451f929ad4f218f5f35a36880c5d055f8463804366d6e73f996ba1ea96e2632ae5d7769d9364145f9b58e3ace4d8cde42d63afc2d19bfa65e64950c8ba4b0cced51d371f794f48c79b04b5d1a292e34252dda0577e816d20c67d480b7c891781d0325dd9a0085ec5b1f80d748c3bc76905f5fb1b7b735a6b5181a174c92c56d095324e5888a028b914e03db6248ca7b127d2a1a98ba3f160bd91512568267d59ad42028c7995ab35dc76839ab8a8ca79ded5719d5c567db608450e340bae0a8643dec48c0e401eac9fc83d0889b70afcd0925c56bd41cfd07b19a3420799ed8947d5084431d8d80bee89e8d230d54decfa467bb35a0154f18ec8703fab9324598230745fa6e0eebbfeec6064f9ac17a97de2a376e4242b66d2fdc1171e2970ff470c0e74cd4dd8b0ebbaa351fc929f8dc8d253985ab60ae5a5d5babcdee9cd936798f0501a60a60bf8f4ddaf87694a2682df796b1b5c6d476173942212df66e4a427e4564d997dc723588c151c03783fa74c7b2e6ebfca43fadf1a882bab3d8aa56048d96e79747478ba976ea5f7408402024916a837094c2757b072529f655c584a8833cd27521851056484a24f02b60a8d88c0f9e626628b47d9c5331834d266b534a34b7b69421748c4188bfb3e101354714d9135e5f1f6a35cfa422330fc2494d646be1a36dfff46ad04da678473f1ed7a8973c438128e5bb980b4f446ea75869a2d059f75d2c42126b52f18589880b3a03986b2346db3a8c724dd31843efe9b4d46fc8f2c25c9261be09aaf070f02edc602c4190c4e6bfa9be41f2395fd8dff79950b5b6a6df04b2a9f836c28790485355d5cb2909d3590184d8ae14635530106ff675538a908d1efb6a202f9acf561b8135da817c4ef12b7ac11fc43237b435541c121b565592872330b8f1c52e17c7271194e45cd1e3583227145f13c0f3c7d1054b39624fabcd7ca34b6c48a8e6c00e708188aa998d593f9b2155683ca15e09e4cd95d011cae9b8695aea18b9c31ecae3bce9c822185167a1fcb365e61580b99f2e763e5eedc95c961c1bfa7522571a66d6c4235bb5e47dd96bca7e70c855273475f8b50ed3ad3734087d864ed0c65fa3ded23ba239a8090ec000c5298d0803b09195ca705365e91cd31dc3a642a24f02675596d4ca8792210a3f54228b34f35d34222d2a43ee424094c8d371c12011c80f99e0092ff50806c841f655b1e281828a37a2b1a6d2cb18a629cc030be884c062b1a895d1530dd5010ad68a068d07fb9d5d9ed340756db002c873efb79ffde552283408b862166ad68a46c9fead36b9c012e3738de4f817781be6459cd21f29b748c2b1aaf7c0dd3547fd298899d8fcaa735bb42310d576d2b229365fc60f8288cce54e8e7eb2e948ba5884c44ba774cc42ba6099c107f069a4469bcb8f128d0a2a6a449385c234bb146e59243847c5bb3eeef61e3388d4e1f52e2ed8bc844fbf74b83f50fc01120e44ee08c35cb705aa7f58e4a38d931f9688a96cc6225a0f18c1f03e6d7e263ead801f915da8d74f8bb439a96611a78e4bce76934f83e80fe6da351e2443991447c873a01be8d8b8933f9590722e19ba789ed58a8323e1648723473d78840620f9b714853b226c9aeba4e8e3cdee3119345e184ee74438314cff42a3fce8b203a5acd51557df78952541e52147004b65a8f919e1d234370512fbd79704f389cec83da67ad3d75f3e010b559486e274beb45e6f9aa94c2b205d456580dcc5d34967adde6aa96a8e5e04f7deff4f75230d89624ae69a212c19781a352fa029e8d32601ef71f13ea239e1a57a0146d364fa9d2749c02840d5c00258a44b1af720fed71adf8881647524824c383e327516349a6e55f5be817ebef200492c68578bbb746a9612e5e668ea98ef3850bf56e1489c04fc5fe328df4d55d49e9152811b26df676d0db57e8aed17711533f916b91837077551f2d56a840f2c01c669d721ad06d0607b0f2dcaa6a580958b34f4328146ef4b5b5d7ef055740f3878a9359a530e6d6eeb34c1ccce4a5250b825eee4bbf8c9487fa17cb2ccf5e8aea749f93b870a1859ec12479db13d2291d85b6c1272d757f5ff1a42f4b89415a874e6a3719c99fa7804615b8b4155d2045e0afacad2921f8df7ea670c6ee0eea9874b6a9a59bd4612448486a0a82d33f92cfc9e9d0053b841460201d803e62e93d5b6c5fda4f1e62a1eca226bc5f890082fb1110d42a87b60b1a63a13fc6727414a2054594ba992106d543c510fbf14053850b4a0bbcae16319eb2a74c0ca2da4271ef31fc65ff30205882848d32918ce2cc6005e34aff451964374a10f538d973b8c72d3e5d8f033f86e5c2537e094b2d0c23ed7832628b60e0ecbade701933096656384274a39b4a73506826c4610a1343963aa3d3c2f61a949a3058b8cce1a6cc726b6ff13a549a08115fbe0d5907d31ad4919083d62bd5f89ddf739b4a9081e42a21f69f22cb76ff429f33860c79fd8da25729d50627cdda082c4804855ed65a9d4370d1c7b16aed251024959b99c5d2fe89639f91be0f757f11ec6abcf160814e4b879d82236ce8eff8e9ab956c8322885db90d2e7052fb829236e224238f209c38f7c6f68a81392ca02f0f3bb701a8ce33287ba746c53bba894754c4bfcb3915f5d0e7b0c69f2413dc7a10928c6569d8ad8455cd0c4505facf563e012e43bd136d02174c8d76cf0fa18dbe80ce9b6f166119034fd56759c6913dcf651b08fc9cca48b7cd07220458158cffaf7da14e0d11439f950d0018edcd11e935aebbdf4370db514a00b67bff35a2dc58b790019df582a7a023a8a9477d5c8fae9feee89ed107ad58c5fbf4b5ae262b21ea83c31279cf30739f3e56e065380d741ad924937dfaad481e2a5d8d4835bcafe4d6da752ae34a947b66e0cce5474a9ba03ef828f57455d1ffaacfb212943431d4ee5aaf6725ab3cc3f4a79f04e84ce1a2c232d7c6cd8a6856b4151cc214a2bf543b6bfa2a4d23e0e5055eb1d062eb2a13423f907a7df31d7b293bd0d0d50a1352259c0742508e684a620b9b4d8cb17f95d992a04b7d7871351dc1e721a87cb0a1ccd9baf3b77af0ca214011b494624d8706c3686035d2492bf984a91b4c66d211d38ffe9a0f076cb77866941c3525986c98b6ba1442da66a9029a16989988100c69af8c80c5cca570952f24c7f871d57b93ad79c4891b075661b893e640659d1789657dbe19c6144ae00053788e50f00796063dcd9163bea555a68a8072a12031a3d315a9756d3c53b460d7387386afbee72ae91e4fa338a6a01f4af0b7d0ee070ee29f5891f97086fec1507b628a3194fd2aa4e7a4b7ada2e6c4087033a2bd0ba0676b3c38de973e7549fca0dc590081cc0cab24c5edc7470ea7b5807f780a0da4e3483137e03a8b4fcc23a59c9485c23e165857a971d9008c71050fadf1a492baecc38eff85ba7122243962917679ea80242c1d06bc788afc2e4c04bcf0e6bd8b9095efb26a7546d3ad9a59c89cb9e905a185d579d0b50a2c9d42b722cc748e00312bd64947562917a16aea5984aa3109fc766bbe8e1bfd1f3af43f36f40c0d6340590b501bfd235d25aab11dea681f39147eb7ce11260254672a2b50b631a4b1cfb1906d14bb6e5eb586cb05c26240c787984a8505f85565ec1ab243319f24f40292f5ef4192d91486317aec1d9a983becea53824c29c95fba286c292628871d77ad6709a6717f09ed5b8c12e626e346179c9e32ca87001b84f40d20e1f3798a6878392d481ddea9607293c8019c79b0fffd1af388e008b1a2d80f811c6ab590f865f7cf52245fb6c9d8ef8bc0b8ac8e127feea7edaf5e6acaea375c1580dafbe6fd9ca38f0e2f39d2c9c7e053ae3247848c626731ef6740f49201fc0a7ef028035bc9c732609cd83b1e3cfcb857695d6b56af5578bb42f7a745c95c3f7e3fd5d3fb7247e312757d83bacd54b3140ada22d084927089cccde4f1899a2bff291977a410c49f9b17189b1a7c8a872d90df3f400116b6f8016572d15952fdd1d3712965e6310f839b0b7939845b41fe00d95c010896b980083cd347efa94558531c44814f2e27381b8219222c79ef4f9b7cbf007886fdbd64cc11a5951cebbe17bed8dda8a5dd6f19d2c52c7411c584bd2f1804a7c115acebbd3d5709ebafca0582da784d280ce9b10908ce7c72adef1bd241d158dc6cc203207bef4d8c58b6cb9b890bd144abc2d554afc72d290a64ef2cb97c9ff8de3c661e3afa4a5585c8a3415e34edc32672ac011b1606d6d14dd60f4cd9cc6fa9923669d155e603f18dcb4d05325f503658291bc4359beb4a1aa4c9c1ff9ef58176ab79648e541cf5c292d418a88b4b358782b876881117117180242fc7183e49511f634922e2e6e49f838000b93e9e8f4b28cd468fe54985ea790535d512095cc33c543e60c186d486dd5059a04ded6ee1fa9b289e343d38eb8d6e2ceb1c34dc5a8a8aba83f9d03bf8ad827b400a988640d783df9751e7756fa4c0a8e17286d1f154bcd9cc46e88a51f76800e101cbb99e0e03cd4c77cbc7a243410c96bef125ae6485447eb74a6aeba89ef0ce8efa5e1567de448b6aefa2e108135dc222433ae01c7e6fb2ab86bc06891ca15f9b9fee8f8499099b44659e092f3138300501440f2a0d77f0fdd9b3ee7eba2ac8d3c84cd9451b260b740bac5b4eae7ec4cd1c2b7fadb3f4c15cd5a6d3e893c9290fd87a755e052068ff3d415875095eb12b77054db0ddc40ed58c397ac1a4f0cb820c404682c53f384b38b8a336614dd496b4cbd704adabc7f099865d3c5109001d54a013426b1673e964558cdd7fd04558321805381e0909f2fcfffc0d97f2a226f1c6f0df2946b3b7ad4ba206895da5468f7ff435ca3b697505bdab7ae06fbbb5c017e298c27ef4db93e54c00bc2b08562ab83b037b6b50e1c194f6f73488a0326275fb15d11cbf1a110a7100bb452d855cc4fe67a8694b84be0c9062ac135951ff8ef58255a44adc3d653afcd54bff7ccf3dcab64c3e1fe3c6dbac9be1c840952a8fe20ff37ce610efcb1bbac2c7cb7e5e12a022db313f77694d04a05aaa1a61e4a25cc2c665ccd25ea6d58bbd9429d5b7ee59f5481860433f919903db8868a845c36d86d09d9dcab381bdb4313cb113b3c7f7d7e3e32cb2da9b1f2cf5c7c86a8fe366d236584061965d10019d1b399fe34f87442e6565a074de4b568b548380f0a2b60a742f575fa2e531c2b50fa016963721bcd94ab78461cf82ccac9458c8192ebd5c099c3e0b8c3458c2bd2697d1c36ab4007f72e41348cf28c37ef75beb6b8cec458576cfa97659cd70fa5e9622abcede94f2ef43a8e7764875bc96229a65012321da13eb697a0903cfc24855861b08813e9f584cd8eead421c6c8b02fb5c977af42c0fe2b670dd337b6a4d419ab4938e8991f8c3224d0bc5002569a21bd91e4847376a485598a1c3619892aebb2a72d2c1ad3424a1f8054a47363f875f91f54c560ffa590d53a724fbb2eefa6bd5aaa5ffae88be22e41206f7657a2d9734b2321eb460a254af2767b0f00ce12c230a9735b95ea59d1f979ddb42977dcb3651335618176ac05627438d95c592eb645a0c82d1e92add1b5bdbfa987a0b09990ca7445019e62489f512b81750f22c9a38b98802752c9b1d880b7f708d47f6ec56eac0ba28991cbc572d2229570734cfa22a22332622712711ea1bef08366d1758f6c37b137688b4d341c520723612e2a5b5b02e8b98aa9d5517d0f8ce4452926c27a150e3c668a778c4a2b1de44252fcf21101e5200d0529283a8ff90170f7252327713b54a435565d989130d97c5f54e1a4a4a6fadda169378222d1b47f3f264a2955c2995045a9458c9549fb4645252cf288443e548fb463c07f5aefde48b597e82769e1115f1791c420628fd64b42cb46d9b79d868a91b3308aba92d24d557d528297445a2945896bcbce460bca4292c84d61d40fd90ebb53e8a5e1998e3998795a13435ee62e7303c6662bcfc7fc2892ba75cf639dfaeca32e37b9c0b25891f1476245bf6a031fee5d0011f54879f0176d6909de53b427cd9f87d1eff716289582a331f676221378b3fa4144939d12bc9ae900ec5ef5bd26cbdb02c1b0eeadddc937a85a33eb8f30001eb17494311eeadb25ec4d9c5c379c431171afca31a599ed4d11dd5406307502e797f4a5a341a3676544e19bff431d934b2166cc66ac159cb89597bbf2d56b869ddce6b319e1070295f337dbf67ba3aeff32caa838257a8e57c2fe402a194cc79a550c519a84179a052a0620cf9783be6187525264b78f60cc0e49b09493f6983dfff389ef5a247b3e47a66e181f50468bdceb2ae4f4087cb69f9a373656ee2a693bb23225ef5081e76e6155898a7e730c01ccc0598ebc33fab7d177326597533b9d8129861e70d374bd3b154f3c5e24403c120584365fcdda78cface4e48da878cade966c2a0c04b029b17061eee6cbda58004ea4ebca11bca1627dddd365a637842a1a07d61d9de3aa58c3f532e5afb795c310a334effaa928a5464e9055efaace9a9da6a565b9e005476fc98088fe35cc483b98ec1e18bbaa869332b69024c7254b8432b9cce855177600d505657403993774481a76b4abd11370157b5a2e6ea0db21bd1ae224d48e6cb8720f0c87748504e453ad8c7aa32cb5b8ec909f79d92a649e9d96dd0b8ec87575a99595d48cf295b1a9fb5068e783a92a79edc65de04a2c5290a861f4a297462ddd2da00a1ce39193117ae944e58ae3f9393a3f5d86e0c87afae5d8229a8f64cf8d4cc08c9334864e1838ed8f9e8c4b51032d2d3f5898ef7abf14324d7bab0e6dbe7dbd58ad4496680fd84735e1312ac26d3c457db5a34e6ec8aef2e6ee00576b2be9cdea834701fa9710de9dfda010d8dd48a8466063a1e507e329bfc957dca87023a63510628e30807dd885dff51a8329555d5dfd9d4ea5e032fa438658f09b3e29d3e18317ed7b390e98549d46873710856136248cc9a98ac93e107f20e6f08e41c0fc9953411b0ee8e3e852c4a1259e0c2d3db759eacac5113895249013b76bcf7e63e420e8905fc723eb56eff80ed4351498ed3c69844e2497561c6d594aae27af2df3e82523accbb0bacfe752fd1bed4459554a005218059d4a42e0015482c63e18fcd47c354f3149aec79663c8418b0f5ebfef816da0a387dd65b649809c7c4456385787c4f6d613114e8ac70ecbe34d83c4484acea8fc03c81737200cf7cc6f57e10f6d59396d21b2d4ab76cd3c1e6d1a99bd32b14253ebdfc9e99229b0bd333134ab89e46ae782f3059c1ce477f9d3a7ad6f3bb6ab982fd4cda8794050b28e3a816de953bd8c7764d5b5585e1a651e183026704418e92c2dcb5655ef77ec32b2efc7157bc16ff8c9b3981e6737f191b5870285cee7964d08ff574e6cea583558670cabbc823242b32f6019307532e391399c88ab71c611ade729a5e5732cd04ad954c63b7b5a57c4ac35157cd5b7fa2de27446bb080d9176bee26ed3bc93219f1eb90e8893934d64abef03941432b7893dab141808d1640dc422a72a6a908073f4cc7b76e746c295d4b40c7221a131114389364077e609ae5a0e64af6cb7d274dddabe80f950fd049e457a0cd9d60812e15a6d3bd54cdb83bcf4a2349ed5fef79c39e518a89f4950f98f93311963a2b31818d0a0b72d4f5eabbfff1aa90de600b45ba10a802a52b42fd1c4bdeb9e3b1b5fa8ad8f0c42b8814254b87c401fea9e32fb4fc3bf5d1411f8d28b7c2dd7c1b9282f342c89e628e89a75ad891b5849421fd3fd6bca3b257587211084c01696b01bbc5f4c9075001f6015115de3335a1735cc2e276032f13962748ef7893ae2ab354477dcae372aff732eab780971dd393aa7e401279d88e9d64ad83293f88d5418577a9a557d3171b9764af7187bf866e793400100a11a264a4680ad729858c2371192ca024847e163188b6f8ca45025c98a0a91c29980043090272a802d4052d39e55302662d9139d0026a8ce82acedcc18b2c15749fccd7f873e807f493a2cdd5eb34f00f75058f5e471ead0f049aa19923c298f3a5977fe8c64f70d7e712e79385c5b218da40a444c135383910a45637f224bb5321c23bea37a8ad6dc1232a45ba5c7605dc27e77b8a0cf4d11d31441f82e91713032805e20705e0b83d73da4d6c76a94d81adc2f604c50f2d0643e0fa8d83607b11b1a2c560e9aa744390b2f9bb8d3496f36b6a552715e837acc1532a8b33f1c7d3da371a9619f52202b1fa712c253b44d58970e21ee9afc12a7a94a1058cd53188f572a41699b1a53fa71006f8fd8708c25b2a9cc0828e4da42e90a3184170979ef256185d33fbdb97b3b8f45887b7c742d45bdc628f74e6b6c8119c0b8eed214b21a8c8fe269215beaf38de2bcea231bbc023ad27e2cd7d402379349833ba5c8726ce0c2a8ace7899d91a08236a8ea1152359bfc7bb7e58eecb75d0dad3b99986ca65285667e64f71f096d1a7a13d6bd928e5bc91b41a22ec84b5808099937d5e6cf27e19310ca1000509e284fd3001cb3a7d2c92bd49552f484b43af924a5f641182834c4e1447630b578765e4bad39bd56e58052864691bde73f629678fd1108887793109a9322e5288cea8cd4f36fc5185012c99602f3f091b84e9080cff936870bbf1b108edc45e002a86940a45f0286ded7c21db5fd16f4d2b515bc1024223d4255c7e8ea1e73da54adafe50bbcb422f9dbc4acf8c029741bde9f23b99451197835067c350401897704b503f19a5644a6ab021292280b67374244812c2acacaf10e06cfb9dbfff1ead237f82bcee22601ddc423f25e98e3d4fbcec207188970d55a50bcd4b5e9d6b165ac8e4827a41cf7d7d0f358bfefc49d3891470e28929f8c544b7c10a50b94a7875ae184d13299b55c1a3693197ec6c29028935896df61aeeea3d8a72c7ba63dc12b8d8cb0b6e0c7de116a94e9000d4daf92fd3b6e66c61490c77962203184b682d4494ff6e01db0e93a35837f974eccce89370fb4d11fd4dabc2ba477b14fba55db29bde1908e294b16ffd2b1e6156efe17288129c39258ad9f6172f820f0da87b494f68a091ce9f5cd4f61c1fd62883ff8129dc71b2463d0663773e6c6da3256503d664c9ee8749784c9a603c0add8d42370aee25a4b13b9b9c2611a51fc0a8423fb598cddce8a78959a87cb02947e6c53eeac6713bd685dcd0306ce5dd3c6951a7eb17a72bafc339ebaf211f4beb1e7d5dd1d05290c4b4dbd335c1c686925c1f94f8a64045ec31aada368f4eb208ebc9f8a169c7a4c02c4141d4c9ec27791a86facd4417925f3ac1d2268d3b7a76d9af5ed51ecf2bc70bde83dafbd22f447ee81116bbb4283a527922c6e8823213a06b5c1ebde53cb9a3b513b8c186da276892e3933433dab14e48fd3f1699c06d250d18822f3842c73307d555700ec6d8402ebc353f76c2316b54d52f06e4740344a5992460431f1dab0009b0aa0a01def8bae274df68199ec5a816f89a6cb1fbf3cb12aeda6b0e8e39d5a6a02fba5e22477e0b274d2364522619df09fc692eb89b1d0f11fe8105909280b264de7f195ffdcdd50ee1fe97d04b40f1e5bd5981819708760cdc392bc13c20d0996e783fe8c5cafaa6fe2d9339715bbfcd81c7707c0b26fa58b4c506a3d3cad753721f4cb4a47279599c1494aab4b7bd4cf3060bb2201c83b19b01b3ef237a83fac0a2ef1fe1ea84d324d160112b7125186fa224cf0c2ca245c1c2d571aa0059f303690aaf9a93f619e2ca3dc72f3362f90f0cfddbd5ada271cd3e044f8400f2aae3deb549067a4a85d37574a809b4bce4ded001072ccda44c9fa3efa7af0bacc09f99fabeb34f1a5602ea300ef38d5eceeb297abfb400e2378130c95eb5c611d031621776758d10eae66b0ecde05e71b20f5cd352bb5d1405c5f745b23099cbc70f273f795ff69ae5cb0fdb33c8fd5be93925f5320341301df37e3e561adf2f4c8f77ab125a4ea6578359dbcad822efac696495300bc7e19ee588f4e2a90dc44e791e568f4cf22576f9e4f518f2e1f3318dd0fe3edda7391db4ba41d5ca71cdead4bac2613b445d19c580631472327de3c0b659cc85108af3af480dbc9eb4a6c5341b6ace91a8ab42289616c713095d156e3c9878f089a343567799572db219b3a8ccb8ee0cb81aae1a19dc50ef772c084e43f9e4e7eb2a4e0fac8da0f2288a55357b654c6c9456e95a458bb5c46686678263f64cbd3788282cb3dc74d579352a9fdf237efce447444f3b9603d67e355841f23defe0c73221a76a4998ecd751ef5538d81b729331d7111e9688f51c9145fb3a6ec93e3f50ce8283fd5ac4d4c02e0cc1420ea2cf673accf7760e3c2150a241976b23ea60b2008b5be36abc99ccc0cb7cb2b7a229de02dc8fc06128017f101b8976ea05f606da9429328d9cc5d8a59dee9a5de6564eba827d834889cdb2763739c69647ba2f676e30770d96651f98a63dbbd6793b88468f24e0822b76184b957cb23366edb051b94590ef0b003b6606865b0e6f8597050fc78ea3a3264a1a8dadb0fde65e639318dd469b48dd2444f38d8d963f8bd7439353a880c20dd92a4117e5e29f82cedaeb7629061fa68180fcc3354e3de45e7b290fd90c594008c42fcc3909ad7ec9fbdd8a778bcb7b496bdd491756b0ea77ba82210b239e85c1ed7a4d56f9bc1579bf29d7faea714e1e28c3185b8ade721298dcb01a2b18aef3b55a2fc7da5de5e093cef20ff09d14fd954f0230586d6820dfdd3b2fddb54beea29d513b2046417f2b76039d0f8482179167581ae20864a81cd131c23a58867c847570c78990557160bfe68fd98ded8affad200a32b6f398a36dfa9f8d8609c81da9c6404a089aa8a8acdd67a6619d3071aa563f6247ad90018639eafed37818622004a94887d95b823054c2e894100c0fd5fe6eb3633b158792c7bc6462fd0461f2ea6e3cf69888d912770c084fbc8d3f2ea3424d26e3683111bf087871b561412398f265534192a1365d9da12b4b4d1a92e851dbe0e9a8d01c73723bd6f821c105e7c5e6e9a36f971bc25415a4e16a0b34eac7000e046d833591cd91c449a1843ab15f18ecf67a10d17749cfc08ed34629a907910c2b9f04688b242a7a1c96ad371817608567725aefec3ecd2490781ddaf7ad79351f39383030e27d7edb1dd0e65dc17d3cb2a1f5ca144374e11adc97afe2e01bda44c9abf1c52be66d08d8aae19f3ce698005132b2855d6a42729817f4030482d02e53a8bf8d4fcf026d1f66756b6e705fb9a7a38400a546cc1dc319072e318aab648359ffeefd33e0682d6da4cad4aba00c538a0f4b80bc59fe9cdcafc339ce8871303cde4c8ce82f274254e1ad9f65a729c5097261e46ca9ac36e7a8fe63db72b00ec71bbe953bffb07fa2c1742e29a069301194ea136e0bc071543cf79b388aca41197d48213bb65c66cdbf0997320533b18e871b419d0223cf43d9742263abc3e6b3e288bf80252740ea36ce2b5212402f85375ab14ec0bf23948194a0c64a34a4a23d7919cb36f06df0bef0d99d7f8cc53677518beb356c5d5b12080982b8ec0e2e20bc8369a2ad4ba5e80e03c64db1fb1fb6bc39a686988b6e3e3fd5510f49f803d0dfa81759e1d2e1dac6599168b1db7655fcaac7d17de8da95e0a7aa177f4989909db99280eafae448eae1c794a4cf9fa9844bdf6805ab53b550424c213db2f9c95e8d941eec28c16af0381f1d7ac251e95d9fb034de3676446db424069358010c82a52915a2d45d9a1b32ea24fe2432eb731f36d73ff3c3399a6962e30d88effc3649dcbb13c37a7514ad8d50bb0c753c344d7e0181810366c66979629a1cd1e0120b9bc84f43adf8f3dbc22ba7ad67ff3463111e1998d85effbeeb2008f1530a56839150b6b50d3caa6da8ce31d4327b9febfba590b913362137b54a99169f045a62a05b0512809c7a2a977dec07fb44e90a66db5ab5fc7d675152fae613d1ea10bee4907408dbe51a12354d70b823c0513d227c23b303a275b0beaa98606f244a6fd49484d630ab827c720e63478887ea4f0cdf5e02ef458de19af860b9ac7db54867c74ba2682003d230e0423d39803205162cbada603b81e6f17f0044e2740009813f46bfe709ba58a84a2eb5ebd12950d0428c771f6d5ef90a6440c11895187d831579f37c16406ce6ca025d159055ca5e7a478b405e4c1c2bbd6f3c30c229a2dd47553e7d6d330083479b0aae1821f3fd2d4700e881d090c4a2b9854e3160c08d374778490548c40f09d67fd29c5908f634748a84292b17924b37b00d1843ef6a402c428e22019b4c9cc143db73057efcb95bce9c330ad7024d10c66289614021ee50d20828fe6fa6f32c6c067b0d07a334ba035e621ecdab552a886135e79f292db77f58418b6b8b7a61826cc7eb3bf491fbe82e228ef05b9ae9ac5a8360f70a6447b21d630345bb46b3222f0247da41f5975f02e67ae620ddedef524cf684fa8f8c6b8ce8f8c66f807106c144c1123bd85802ace33f3ab53d3fdb3898b49844b46432586af8bdfe086b2c68388000e1f6fcc77f1fd677f9a7cb3eeeeff8bede7b3731417fbf1189ca61d7360ed043571b8b20aa18c822c3f8093d5d011bf8585926468477213cb30f6bb5bff32a00f3c9d251e546c8fbb0c2921b993cf1560409b33ecc5467cc5aad9d73890cd92e614c048a12d6f303b9b326c730da7d69071b6b2f2b11350f6980d470b6c29e6846d1a4c922dd6cc989117c3de8c8a8ccc99aff9003566dce8f331060b3636263eee5de233971e23e085ce794a5855a7d66702b1f3aeb400043bce439613bb5a84c820db3db9a0695a0019a5a0ef1a5a1c377dd288a4dbbccc47597ee22e124b977c7942561fec08012306549702cfe01efead7c19159370603793c5705fd88f893b2976537fd5ab3a743852f8b6e2441127ca86c747f30373a8d3cc07e849a1d802269257a65c052e3bb49546aa2cc760d4da38b84d34516714d73ff733c254c54239b9301770fb0b568289d8253b91fdba38e697b63056a49a5a58846dd5b35ffe8e5991688f78153a743433c352ffc3f69b83dbc67ddb686dc895725868e06b19b92c3b2ae25b4a740e038abd4f14f766a0b92a011725a602a9d0a883b9380e141a142691717c49f88d347544d22f2ad27827889ea56ea9e02da3e1bdcc6588244241811ea06d0aba452613426a1be8d5aba7dbc4dcd3a574d61c3a5e9691fb39eda8e9719b64f9a83549299d6f8297822911917f75d2a46407899c95bcfeed34929156330dda68201cac647e97200860c96fc0d43af11fcc56e0e6727e3e47a320ec4239b83b391225103a4eb96678cf7d2d47061e9495a3530be2264182d8b8ad1926015ed0e9be75e0d34d2c8135b9337a8ad02e725ebdc275b17a385150ca1fe50a8d0853effa4985ab4812b7823cb779c8ec04c98f60729c3005be7df8df6a83003c4c7037ac1028a5a409a15a703ca890ae10fe171e06269dbd14cb3ace3c3dfcc121963f4b9ee1cf16e9c231c2e0d70208e0ac5956b2837401de56bfa8484d02e6141637ce4b7247f1b108ecc2aa4b712c6abdfa0a72624c9de5bee2da59432252903ce088a089d08f6835f4c863d4f5ce307bf181c1d06bdc42edd410dc47535a08fa401320afd75b1ca0d337309712d5eba405e4cddb726b85e9ad12846896d64d07c101e79a9cb386e5450ef407d4b0e85803c4d324fc762b8067bfa7c671e083f1082200c416892458d0048640863402803429a3d845886e152bed87ef00c11fa4a7ccce7ed0c085520a401610d086580b083b004a10d086d641efcb275b134320c9ff2ca0c2c2d34d49043c70dd30c2c2d34d49043c78e956c074b0b0d35e4d0b1c3b4e1a723b7d050430e1d3b4c27961c2999861a72e8d8613ab98c46d7663394b735d870e928878e1da6938b0d336418a621878e1da6938b0d2fd9b583dc5fe4ad348e6c02cb54773452e53da1705115c32cd124bfe734cd799ad3654e1be67c9993c79c37ccf9ae5243a6cd901d67a6938b0d2f3c6e00c08e95ec0f043eb727171b5e78dc008000680568c91e485ce2a57e9761b82e36bc78c6236637c80c00330b000e5a0190c8b3d287611c392ef1d3eb1cd90d1bf985c70d0008000e30f9e4dae67f52341f570c8f1b0010001c6072cc8e0cc39d29c3b00c35f2859f8429493ac8021fc3343af80a40866195ec5832d1dd12974c75d7524877ad8222492fb97f4386e11939a63301e410b31e3ee40fa9837316267b19c35c9634ddb55ca2bbfe98a44d3205242a691d231a9a0d80732e94630620eb5b8139c330c6de733fdfc145d4204754ad727752aee6f33189b517b6913fa34f639834000792af37ea64c955c7c192b2882f207e72b919b17287698e78cac822962f85e4a7b85e9c45ec197259b63332487136828feadc2a0f848ad6c37f82fce31f2c1b8deeadf55bdcc67847fe250d90eefae549e84d243631c1a350067d3ca19b3b79cb9f21ec24d624ce24b61263125fa0b75b2631853a7809c20efe5b6528695ce69199edcd2496124789bd4ff08d8b4b0c650884534fd52244a6240d8fb804b2b4833ef2258d9f802c6987403a4710feb4a83e95bde76712062f354049636384723d9206bf1709644a1ace860c1f255b50438692c646868f83133d03f1137ca58782df0a7c0902d8651a3f717002ef345ac03bc5f7859c512177e80a0d78cf0a2f0b5946ef091fdd3a472e16792bf0b77c1ba660887d2aa893568ed7c4ee0beb716c7b04c01d75fef5062e43aa0d64c93affbefacf2871211da5b449fef5ef9d1555cf6d7c08b5215f6f2f6c43b69d2a725892c61922fe3344eaf6ef5d3ad909f4a398cdb79d5dfb623a2643d80249c7f782fdfb1c0379c07f9f830e7df27ad365d4b68d5abaf7fc0423a7811f8b40ee515e44ed47ca87656370e1db21b60606177e665f60a1a02929d9d6ecc37a2f2626f973c53b45d5eb7c0d70ea478bdce136287e8b62533de144cdac0bacaa0596052b68c2aaa093d914581458211fd6a7085a8f6db327b02628c128b34c5812bcc8ec08ac086c4a46cab691e67bef3d49258eaaee606c1384f00650d218bccd06b9b71de43eb971d55b91383ef1ba297277322aaa34967397eb2a2976c17b628a074503418a184557b9c9006a520802c4e101ac37c83ec4fbf14081f056fa35563dd1585205599c782b3a803fc8fd3798c2a34d741a4d63398dc6d26e3426e47e89cd2dc76d7229309e112a9e0e971bfad471401042f4a9da74e16e511f16ef7a9e2228abad94df8684930fa1f893218410daf8e9e4e6e4c626bb14efa4b1dcb59611ae105edd7a903b3148459b13e47ea75c5445d583bde58e714814ddf5471bef1869ece0a8f2d2741a2f55b7ead5a7922123a3b64d08efe434f189777216cd6684510fae14b96790ab6b1aed60ade525ed486b384ec3a1349cac69aceee2d358eeee1a4b635dd06fdfc13349c8e862e6fe8c125dcce7957089f1fcb6f2936fac8cda9010553087138f86fe0df45b7ce295faa59841ecc1db11a778518ad7350d95434520e48e5cc7599d1c63bca22a46558c30decb156a13f6a213f29db6a8822cfd9fbc552b5ea95a5157fe616d197a32863f38dddd5cf14afd5b9edc87a5c4cd9d6c6336f1078b9a777a2f8b07797cae782cfd1f2344277c6cf9e23e2c4c7a43f9e676e12d7faee7e8b970670c6faa0bbef68b3b3f31cde2fb156faa983b31dbece21703b1bfb683418e6b3c5f71cde7eff6b957763aa7a6932471998ed64b2c08c3988ec9401eb37554d009080328034803b8fd20aaeeb515cb1cb3f5ac83ef57ec1984fba6a6595e6275cb446b9cc6e96ca3459973ab9d1d6f2a138d8bf9934d1cb3ede01703b51ed2058fa53f5e78ab79dd0c5e5783579a22f76f2a934de591f12a41b82eb1a56629a74da5a4bbd1286e518fe4fe0f12cdc78d0c391c0df18dbca9bc835fcc855d723612a164173eb51151af56a29317173655161545e60bd28577e0806463434a91915d58471429437a480a8ccf2e9c038bcf9146921b6789860286aa553062d4009298ede01922bd04cda67a2b57aecdc4eb54a75e133184088921e3c234f48c8c96e99f32beae1ec765f42b12d516f5a63b2c2a0bb7b21c8a3556a6852b72e788a118962c812230e4fe58c38996842046ee22a33e5ae466990801814a4395c71766a9ab2ef5af3e80752167c2193e49f868f15656dddda9844bd28f7174ceb802712d0264b9e2add4d55f91fbb096a8b14dd7c490b45a33f08571d4950766c41e62884383833f5874a9ffb345eee7668decc23768d4b8b08d2d341f17bca951ca18aed93eabdbf694fbb02c08cf108146baeb3c9c1c5c7568e887fd606f7585affcfa6560c970e1aec3badbd9c82e2c43092bddd28decc23572c5359ecfb09aeda507047a0afab9b98ddbe0bd60df321b864252a01aa8f2920a168136ddc169a363034776611a8eddb8372eacd2f97e1c9a0f96fcb9e9c0c5513fac7ce11939e2cf0dabaebc54571f2c3eaccfcd112b46e0ba7a2bfd2b755557ef65864d95ebcf15d4905d98cb19aeb93f7165757739c745d48c3dc445dc8c71db90eee67f6ea2e8d3e7faebd3ca587477f36175c781db2694d3649a6aa44d91be9729c40b22c906c5b60db966d07c5c19e32840736768c92e8c7356733fe2cfe7c8a7d51f24f721aea1b931f7e9e53e3749504046e6c3f2d227098be6e346feb03670e1a33e2d27a2d186391c306e4616800f0b860c1951e5348f718f69ac8fd89481738dc45e2c8b31c69a6d0f267991dd0f94f0de7befbdf7dedba627e42a9e2a8e538a1477341a8d46a3d1e8351429292929cee5a003e411ff3dea457e1801e0e925e1464b0e148ffc7ec63bb5605785d8559bc21e6e8b9f7af09691ffcde47a68f85e24eca107afefa607c9ddf4f06e7ae8e1c6888d72d296f1dfa31eeba4f274e4f7b74fff537e747fc8d2df0d79d0fbffffffabf8444a4a4a4a6cc863caf76e70e1bfe7a558a1186c4a7e0f210ff9a216970d9e5ecbc4b73f3ed60259ba05f26869c9dd5eeaafa39617d3d272e35cc4c8ef6d6b5262e099b0e8d17cc4c8b9565a4ebff7de7b178c148410c2941b19ac55bbbbb156eeef884932d6c6dddddae47e7fa192a131a2a8b29b6993fba5cc6e72bf1c65154aa6b41c2d47cbc98d4f724b94654a5aecdaa4284b9992725b8154f6a9a02aace25a2c7bc7c0335d2f8e8e3c9256d25dc8d0c5a6885cb1e40dce0d4e2b494e2a954aa552b2e5a5ae3868d96ab55a545423371f81919391a93636ed312727272747ce542a95a2b2255bb2255b18ace232216568644a0a912a3904a7952467c95ca5a44d2a954aa56411b992ac1b89b35aad56ab552a954aa53212c49b88d34a92b344a552a954aa99b3f2d26ab55aad248d4c4921522587a8542a956ab55aad56da1c816a884d2cd245560e55cd9c88d34a92b3e4e6e6e6e6e646a552a954aa99337366cecc99393327f76b9b0872224d14426f6e542a954a1587449b5824ae228bdea8542a958ac22a2e8ade5471515586a65695756f4655c693d09b9caefb1f8d5237a9ea4baecc88e6535148a553be6a436fe8a43792dec44eac32abf79bec389e24f7773854bdf7e250a311e5503fab4c9751d52677d5a6da44183f79ca4ba897b2c1858ff2547bca539eaa191c8007ba1a34f2fbee7aa96dea0c2e3ffcef63b409467e6d4a6913a94d2f3af666af4da3fcde7ab2d7a693fcde823293fc5ecc6b53e8b50924fa9c784617a5f36223a564af4dd96b936d1326237b6dba2a7e6d9a2ad96b53acd132bcacd4755b7d2bf0e51129a5fcff22ee52c618638cf1f929082f09e1922595724e5b03897a58e44ea194b443949109f6a9a0445192fe65425a72c28b44de1694b92d99993ec1d4844b3021c4e7fba3ef3455538a89f2a87b22bd7b8789862921c025e0127605855cd694e299ee13deb469d3a52c3f99e47809a74d2299be4fe8540f33284f7aa8fd98197b1414110d69161199140185408faa5dbda34b942080a01807674621e11e74782c7fd3ae268e91690a7e750e7567919e9c49ca9f89288451370b2622c8fda0a7f5e878c75f4aecb11d425972472ca131babb3b144d7427a4bbceb0ecb5e90467bab3b828b884ff6942d13ee79036a13c8a91bb3391fd5ffb12d9412d863ecd229d7754975d244ae2752299ee9af4ee3639e239644281c58d8f9aaaac7af14ad418d2cf221eba1fcf5f0ef5371322a2f1131422d3269190249e49a40213110da62778b405425c489b68aa3b153c1325c1ebfa9b9e60fbfe57ea27fdc99be4f889c45dd63561e960b5b49032eda9aa77f4532176e5fd2db0dfb9bec9cfef7a5b3709bdd74bb5d2549b6eeb8a24d2d316bc13e9fbe90948a39a91a57413714e3309d3d4981728910617769236757ee55debbda38f5d7d969743752d59d2949056b752ad1bbabbab5aef1d8dfebb0eb3abdcb7453b1cc8e6b6de4adf56445d2b5018502850370bb91fbb9990dc54b85fc8ad9a50cc215146497320ede024191da6bb2eaa3b6cdd564ba43a691960b04fa56b9b6600d24ee47a9b56f26d181da4fc1b6a9394524a1d7258fd80c9ba833d6020cea1bbfe5a43f7a68c6aeec554fec2359dc7baf8a14b40a2b8f2650e9c105df6774e025d26ede02f5fca9f3fa5bbb6c30d2e2e12477e38ee3730307efaa14bfd489c03b2a31f46206d02f2561a88892574ead8387538204070ea0504c87b79391807ec0cec0c5634cfe3c36aa6bbb75a6245d3dd73598547f718dd638c1e3d7af4e8d13dc6e8d11d93e25fb966191a9b16179e7211f4aa839003b50055d0053d3cea6921b9676570e1b7cbd407dba39cf4ba36e71ec80a086d3c9c941b8269ff7e23fce41c10a147654b7203d130301f88eda2b5c41afd466ce81bcc5bbd97976a62cbde5b392fd8e97426addcb66db466f0ca90d334edbd3c7bbd9798bdf7b2e5ec65efbd74f6b6ecc5ceb6514a630988b8f521f68efe177d8093ab15080d91202e39025d4622663e9e99af0491202e9908a7a1c6488c8a1994fdadf40c3ee4aa7cffe7dd1de516b9223a3b91223abb73b5560f42ca0a2bcca4463797e575e5ee703ef60fa70eb13d11b533c46b43643fc47dcfb32e338b5fd62888e66392c0a18b9c19254631a057e2c62401519f1ae2ce10892dda67ef726db613eaec853fa2930fe8e1853d4b26aea901af6bd9840fdd41eeadc0c79e924999bec85946c4e7210bf45c2affceecc22821139a5d7874e1930b8b72e7c226d966a150d640275ab685e824b35a74b15b27ebc89b3f9dfcc92ebc6926d9b52656860c119663fcfb55f385690ecd1ced8becc2925471a83bd1475c73f29dde21129d7016656465dcce94cb5d4aef803332047daeeb5e49efc3f885238dcb75b1c611fc0e08c3b8fdac83e55f3526a1d07f424ff331266f2d4a884cff4108bdcea7414ba52903d788be836be72578634c422d34a31893bf38c81191a3f918113722bd401985b2960e3de3f7b7746b92e649a731668f3887ee7015197e34c2b2c17bc96fe5b570706ee05bc95204ef845f09be0d31a0c1050f053906b2c411c013074bb0210bfc171940e9446e1b29c8a1fabc76683479822cdfca12211a08169382a66891362539c4e347d278e993e17b4a32fc2e2cf9a16fe812fc891f12a982579f787206d9b18eeea07da84e0e02bb98f87529872e513983775279250fbc2740efd1807aad1d40b0785b94a28e0c71cb14251b55cc9d9d4305c932931d6880949187a4812c9f0cc41bf04e3204af244310b9e8c911c70cdf7d057e03dee93d20b2038ff4320ae63944a69c66fff3afd2a65b2f10c8d27466011ead07cc8c5cd453829381846002d22c4954c5453d25d006e1a29e122d08cca1b729360a97e6a3f3f6231b059b2d8ccea378f2b3a28a2c3cd9c610fd80b966c6bfd03eaafbc9cf0a2ea8c8d9ca90da0f98454fd23eaaabe56745152b2d5b1952ca39a79cf1d169323bf959c10591ec166719f6b733a6123ca397a159c18512b2d3995f11b0e083ec6f55b20cdbd53df9d7ace0e208346f34bf22546106d9dfe2eb6fb94ce29a35cd007646bd66c81974063663c66b9f013b1bc4e90e1e01c2453d25288843448cd86399bd628c32464fee6fcdc71b0397e7dbeb27fd796112bd50a49c988465965e9b0c59369f523aa7dc222a92327bc57944c6b00fe219cbb4e62b8867fa92c69fd7c33c2f4a9f4e1a0466fa42f3f1c690e75b1b99c432fa737f4f65eb4ef38c3a9fccc02fe68351d5d3b9580666fa12459a413ccf29e9c4418ec82898493b681fe3c3ce8c0f33c4695377b613bb4f4dd334ee7591c4953f91883f73847114f6a8ec51f651d7cf979884713eb04cca381f58f62759ce07964917e70343c53c831c91e3db2bc3b0fdc17d56fce026a3c290af77cdc77b429ba4e42106f99851325146860499086d87934c399405f2c5bdbf25300b418411322d831b3ea104e95b4c3c63272602c4a5941527fe305d9ee602bf837f4749dec54f9dec2efc11fc575b4f38e62d53dd6fad38080c07e142c06aeb67ed32d7bd3e8689c8b73fc007e2e27ea84fb51e75d60804d4de910c7f904040f78eda0e3874d1451745c40c44e74c25a24b3a2595d2e5fb4bb7d97148eac032c53774d7b37b1e65edbaae75bc95761da7fa59eefc54391dae438737ec4d870e1d1958b160ba1cdaa4838c2e3c208187f7f27aa6238eaed3d1d2d2ee9fa384f24db9e1808393a0654eab813cfaf7d74b155afcb28c7e63469fb3cc6297ddee5b812c80b01f3582953e8acb8f4ed95cc8d0fb4b88508e316e71630b765d302b988b8369f1138ce41a83f1e8a11892f1265a01617b9452ce4adf561c3385137aa6b8673b008896104124814070e456b9195b33a96dd76621131b4c552c7aa07620bf875040ad879b0b7b8f6198cbc5c5e5caa0eaad400cc35c86a08d8e96166bad12d9e5857f34c2d17559ccb41efe3194bd135441151c026d322ca2eaeefa88211451a8a0ea0333db06552ad58af6ed40cc8580cf5a7ffa575a27ad4398f06a0091901b4ae157371007b2fc42962a56a86a135c7517dbf472d7fab006ef546baa7370f55edecc95ce862bb86a81abd52a360542cbcd34e6ff6bcc631f2077bbdc3d4806b8be7e022e2e01ef5382081091243fc533f4af97fac3c40df296642262fed1005f8c8f62f6b7d3dbf4f3ff3b76b7d462e8ffbf97273b5ec1c2ee86a317f2177d0bc1fe6ec2c66e7f39f647ee4382dbff1c891c1d421cc8af81ebd3c0f7299ed493a2f201c9401acd38a941e3868dd20b94951928a54e393f22839bd4af11ba4fa3c6437ae7bca8682359f288e59da5c381a3c3f19605cf10712858de5dc5e2b14918661cd86d6a7c880ff195e645baf32224aeddabf5e019f2f426a21021f1621a3f71e4142e1e4bff073b159e155e161e175e185e176fe50aaffbc2ebfae52fb1f746a6ee14d6cd0613a954bd3aa52568682eec763ee332989df11e2c03eb06a785a4c8ca488b61492fb1353e28df1ffb517077217bc1750e4949087b8ec897c8d0683e2c0e3c98860c4d4a481338aae2909c259ab73c78c6d6e9683d48275c96225048743232e99090b45a4aba4b72afd6e3f356947930cea4d1c90d1bd5e486a648de4f1be995e8b30f2702c2307e84b937c1f84ff07f08cb78118ef11e2c233b7e19ff8df196c38e047b1257823d077b177c7499f481560c9170973f1e1c23cf96f796b72d7886485361e3431f1b2113100a8cd08b14130f7ec1c2c2758a86a6554270885232940cc23536dee5c992d1c883478f34f260118a079b641076242f6cde0b29834ddceb2dce3c1804228d4e6ed828c96a724353f402c33cc34aeb05d7b5d034713b6fb9cc833fb984e30dcb4b2bc781921c9b1ba2620a6fa5683eaebcc588e1c11e2c583062686fe1bf9721e31a59ad62c8c8f0ffabd1c9241c6d5238d29c9cb7643cf7388c956cf9068e393852317224dd258945baebb8fa4c1abbf05efca91071b0c8e3196f06c779b0f6019146b108c481094713f256db78cbc68796bd35850c9907671984bd853d49774a4eb07721ca78c9c510e27c890eaec42fdb0cc2300a38c483af2c534d081102858a0655aa9a3f182e016968e688a0404e0d38d39dcc9c5974d2554f5280ee74e864c08183f052bf0c1a10ddb576f09cc279abeb6c641e79925984c4792b2ac9fda82886dc62782f2f87700e271c0ced968017f425adc7c71d3443e6c1314b5c44bdc10554f260cfb206c70d1c6d702ce271e59115b13811cd1ae26a6cbcc5816788441b9a8f2b7fb84773037bebd1d06fc3fdc683bd5db9f46e4d2de11fd9f024de6ae9baeb5afa060d10074b3641d25b33dc28e5e872e4381199e4088172e4f070de6ad5ec0f873ea5d46b340f5f802d91482412bd1793fc502d5de9f108e5fae1d9436eeb3cea3121df473d24dc8d1b695c67b3116f050772d2ec7a9e29feb3e209c0a4889bfd5127f9659c115dea3742ce46749703ff0b3c93733774078f440e1e792b7d045445177e2f3e51f27b08f37060de4a5f9946034c642fcc5b8156945d1e145c6a81f1929f5e4cbd38d4cdae0c8f6aae97b42d3bc1211e1497b77aacb72323bcd45fef68f46f890c19058df0537ea5fe2b64b4a0690217b005363b0783030b63f395556c83792bfd0f86a040dab60df2a0c06c5d1fba6b2319959d7ce801c829e36666ec2701231393bd121f83fde450d75b8981f8d00391f918fe01e6e8ef863c1e863f4449b91f8efcf05e9e0f6d9ad3afca45096453be07591af278510b9429a9bf197d353af118020b33e4c1e34196f9a8cf9b1caae60861106a81f2fbfade8d9c2e0f863accfb897b397ae9306fd582c28d7fa274197dd792a1430923d7d2d2325d5a5aa2063c2f6ae0de8e8fba5ef2db0d39b0a564afd62a4496a1c7eb1f9df279a9df7b6f76d893b8d06293d67a796eac9452babd56b1a6512d34fcf23643247edee3a93f5f4c05816aee7bf0c7f3dc93fd5dab7ffebe0f9e0779be723e7cea7fb8770f77c49523afa07690c4a88e52b7b123f40d85003803a9c32c6bd308d35e7741e8ff7879082dcaacbe536cb3d0d1d252e14ca537b8b8c05a9520c92c56001d58263f2cb62ba9f9a82f2bf6246e0e3333da0f3ab307af0bc25ab1b4360af7467e5978008a4c3df808223e78fe7a38cf5fac7b901400fafb3e7cdef379c9f900facb1d41443e88a38097fa636b43f870ffc31d010477801713c40e57961caa87fba89783c5bc18cf6bd1432baa5108c8efad2cd19c85c2ad9c0dc2cd1a1c82129ca7f9c0accda0f6a3bae603c332d468e0127006ce6c19c5e84cad31766aad1d2dfb2cfb9096dd4caa320acec019382363066a5eb179d15a61adb5d6ae8e3b1c5d773f546284504208e5d78bca182116310dc362adb5d6fa53eb51ef3d62dfb15dc521a24fb7469c4092b3959a01a076801f35571c2a9d4bf92895fab25e1d595ffe85491a36eb7545fadbd3a757adbf552a257dfbe875c94abffecc1c5e99a45fa9a4549398a45d52522a29a5d4297d2969ac0fe3b94db45e99f61a362ffbf26df62169dd072dc717362fd881a310d8245cf8f15f955fe555abc568ad579b62f5f93ffe27a61896fe98fcf1f294500e410a9afc32a490625285d01d3e8c5a8c2fabb46994b5204364af695f31ccf46b6c48b1cd22be7e9537064a1fa552ab9455cacb625356595dda235c98df10ae6022fbc799327a63c8f49f5d5d9906e5f411bada8f78693f502a8d326e998661a616e75608dd553694057284691e2477439a4af7840b1fa542391b840b5feb31842b68de8ffaf243b0be6b3e6caeefd846dce1e8aeb3732af067a862da0f94f8306b3ba0e4e85f3f26716b8cb56ed75f169bb5cad75ebeacf4c9d8d45dd3b40f615a0f0d57ca8d5699d24fe2d6874f3e2449af1162947d144649169bb456fc3294557e108f52457ea8561adf3578c285d53b757fd97b5492aa9cd13d734adf73af723ef7cc7d9421a594f6a0f4dd22e1a2a0e35c4f86ff46d0fdbbdb669f5d55b6631f1f73eb3eed11463e3acb4741c13ed426ac0736391412196ace592d6e7e2fb54710d8858fde4be5466fe5fdf3814d4685e07c4288d2f7e8938fbe509b64f76763b0fd4fa5633be68e8a95cee8d7855dd70cbd47c50c51f11f29b6f4231e465d8f42cd7f1789ce7e59468f31b6c9717cebf1c5f85e0d5b47cf240c2cfca7826ab14eb8a8ab0224b973670fa10366b0b093d2ce360fe0bd17ee48b3788d99c4a6466b9b5ac7c5beb3d84014d9daac6258cca19c1c84e6223e26f4fd4fecfbdb0b7388bec5309c99ec80041521e822a390a042462657c06d647234828a43b064b27d22b01b5f2293516f25932d27822c90c5881732a5d8e4ad8cbaf3b7e20fd37708173e4a9461f812e19e64ff67692b1791896c0dbd9721e02a1731737c7f93f7320494c94170c83c5c196d005026c7aa0d00092a5232b9024800c146c666f6adb56f33fb94abf6a3c52cc53ec39e24b1275dd8932066edb4187e51db016b53287b11996c6d64b2e582f812991c390cb2f857c8e26fe4c223efcefa80ebf365a74c89e651939aa64d4da39aa6554cbb344dd3ac0f1b8a01358b192ab2cc62460cd97e7c1b32bdce3ad4a64e8f07a06a5f72ce19dfd6ee40190a667bb5c6837cfba1eb6b9b300e75a1b0cfb8886b04e2db8f0016639b300cc328fad8d74aabb55f312acaf998ac4fbaea93607dd2ac36732e674772f6b1c2875a161a882267dc5f6f310a7b0c15bf720fc6613ad61851d8f84fc5c60743f7aa7c0ef1c1fcbeceaf34d22350587d8a49587cd245821de99c53ce4961866913565daa7d9975740784fb9e3e4a4765c2edf71133cc12eecbef7a18e4d132966636629b84fbde2ae1f64398f782657fbbe1e8ba2a5c32bbb8707199928c0bdb49198daab07961db8216b1277133bb85d47c4c54fca924bfb7e257f35139d20e3ffff2420e7596ffbea3f130bf7e08662c62107b58638e3714122fc8f561671b8a01b972efad643c4cf96eec13de32b927bcb1072cd21d258202dd5d14c481ac36b5da8482ac8c459bfc8adc0f71daf4aa98020e4492dbdfdd29a52e33fa244bbae89322dd5c9404ec6dcc5c7bdd410c05b7370ce5499d94ef5769d38b1bda1443c6c5df5f8036fdf7f7d0260dc68cefeffc440308574972292004107e82343e4384b37e922587b2407e6f05c62936c24f504877f79021fe5a589861288f829d66708857e88f65eb9be8e78be6765d5524125d46b8b28d2874db50b66dc35a43d93694387fa3db86b26d1bc6c1d0aed836ba6d9063f9bd3efdfaf519353360124903cfc056efa0f12a7886e66368701049975eef404125b907115050624641c1301419c3478c9e37dcb26298352284cb610482f87569af6199bde2755dd73f28648b617b73df10a448e567c514c49061c6e2c719c433a531d2395ffba8cdcc5e536298866134f747fad6b3d89da8f5a0f3a164b54844603eaab3d4b488c5f93462d28c947e8c33caf954e239a3cb27259dd883429e19bc5e1fa6c1b4cc84200105339c792b3786a873900ef571ffc2d0e45b8a356b2da531daf9f6e935298539da2863949e8d38021747b25c8956220c351e227784bd4456eb119fbeec49e9c08bff693d60fcd37ad82cacccb8f931b0bf0c0c5bddc1227e822c088b4c25b89d59d899c5098d3f409905c11be36d28cbb0cd02d6cc7898f6d82c9ef6a366f1b42030c3368be7c1b0187db20c6398f6a3f386190104ca8a7022c230530ce3a9c6c1b8c224e6170428a8e0cecbbaa28bd2e96429b8a6c693303c52838339acfe705009c4b102259b81610c0d0c5bf86910a73b229c6427d5c3419c218c326c516a7e469c68c2f37ca55da8242df864450a158900000010007315400020100c0706e3f1681ce879deec0314801175984c6a501dcbd324895114c49042ce104200400006044064a64d00ea2bc09a2724a73057b2ea2914d79a31dd40a7d5ace371bf5cc0ec7e143766d196a571e9a6f9fa120431361026765f0f5feb6d95d49b0fce626d2520fccce42126174a7c165f3d34167a32b3e6ef9447cf161a708fd8c2e84b411232f43f7ed9399ea94c4750de7d68baa0f55411f6a26a6f895e7d75eec9ff56fbeedcd0d6d086ce1f1c90f0fb264a129e59b717a09b919700ce2f411b6c309464116230af4f1b0b693b3077d307949c015373a7287e7be08642a716e44f7720b153ff0b0070318a8ea71071b9120784033ddeb00785ae65801ed27a374e76ee29b2fd0d626ca8d76e8b983667c810cf509d480c8e4104866bd2cb9b9ca0440a79062d80a5ad9b27c826a57213b889f24b4e837362527b489f5c9e05717262d1e144017c7e3e6542b512e8104eaf74620e6338067a29a1ec5acdf8e91cc0a444020df92f473c42127e15b04cb3f20bb731823a1f4d5f393f5475d53f352f9f0513ce28f2351593bad2a12b14d93cc5acc5906119f7f393b08d7a09b734865cd45ddcf09400fde55c173ba6a757a0a213555f82450c7e68e43c86f0245801a9c464937153cbb64928ba99a61272343d64337ec093d2641fa96b763d87af706036d2a84df26752fc2fec50da043569683a2dabaeb1d2f365cef8836ecbd3ff00c774cb14a8fc0966dd733cca3f832894a188534023a18f35bd739506213af8cbaacca0af9de9a691f5caa70b67baa7b84c14bc62a9fc375a54075c51cdb46538ed3cb503b0522054893d08aaa5945dcdad4021e024dd092338d7eda8a48554b44da76890bcb0d16b51cf5b113f6037e746104e6e1fb4049d3d2780e4405f817c637855b1f0a97082bcc4049181484c9620554c58580722e54047a88c19d62a1174e9a92c319f1131670c0e0956e1b0422159587c7d5b401a071ce8e94915313f28630d2d9940ba30a566a2fa0a9124d72c84ed68f06f6df51b2b1092d276c0916fe906743dea82153f62e532e8bf96893d02ba2c6a1ffb84e1bf4424310221451e15e967df3a01e4866e677ababbaa00d0535be33e4500b3d540a8097e1bd8c12895abffe9f44fa37ffb0c6fbdbede5daa8e67ce908e21ce515a6a18ca9f5aad020e3b4504cc6d42f4cc3d48b2359c578e38b7f050979431b29f1df293f0f5ab1a88e621931e2b76e902c744fdfb1c8147ff151921638b982db3ac9857194dcdafba5e40191383462cf7465d97eae26483aac2f34476de17a5d8d6848c104941be9ad0d822c1992a07d452b348044a9e0e225de2f1fb249161c114b13bd20dd86b0d8431ffd5a258a8d724402a49d502d8b0bba725181060c2263cbc8b1c672d957e7d1810806e5e902b190464429c0aba642517e45f21b28eb46e4daeffecc7563159600f908a948af5bef66b4545df539c2da998fd802a5683017dfec0effa19efd64829b1900520397596dd40048d14a7c8bdeded8368e6953cbac6f05d4080bf69f23761b4a8eb1b2c5ca08a3c5bbc02e92a9074f2407d4605c34ddd4d0f1eb2b9e90176e1dac66760423435192e7236aa3e7217a8c4582600ac2a317ed69a5d3d3c75ef984404594d03baf4acc1815ff951a7bd3ff5f6d922ed16041042411f969805e8173fcf43f758a671a238c7d0de23f216b25e9ffc1605bc9bfc058c72ff608c08cc1c8b37cb42230ffbab7ea49d3b6d884225cde5569436f52b172a263f6c307d5ca990ac1900e3318250fd1e7ae86cd186f17e252fc1b9d322e1abfc759998e5272f535456bbc842a60afa49c83987bfa283804767777856609b88cfea3d5a0fd2e9584b827123fcbf06db004a7834475991b46571fe3edd621f3644e0e4cc1aa1b8b321a0f35db2a80bb962233a653e55e7210b61403911401e968b55c67cfdb8443e0c5de655b6388ee57e0a332ea3f798017663ec1a0a95be3ac83e7255de14de951eb9983762656bcaa0c2c609e51cc99805caf00fea0d41d98b59253bcb372009ad43a00ad252c08e7d3aaf27c0ccbee1421cb004776b1da766153e728447be7bf40fe2ccd10f22ed900c9400595405edd027167c368edd8ff59bb41249da82c12aabb75424f470ad57a5d6a230e3a708fb66c4559b3d2837e4fe06103d5d416f7a50397cb8cd3050bc310ccd1e5685caa77748a832f61435c96f5b32f19312e9ff3373cc9438f69c338babb2b0af74064afde96a844e59cfd17c2ce70765153e7a430e845179cd3d0bdedf6c5931dcca33165a725063efea276b7c835507c8837bcf5253a1b3a15fe51a3923f28764c5ce369e046d4b0f0b252c30c545884691c03e6b128915b279129884290fe188d7a883339633c9b552e9f35cbae467bbe555c2dde11821effe2d8ce07c897a94d8e34308ab65d031eb1b007d027b00a20c0124c8bafd9d67cfd01b7a0783bcd131764e71f6ad142e2a3b883f2d2034f92487a4e559bccab584700a93482b0e7dc1532c42dc1d05f8b31946a0d5ce26a9529e793b85bda9f32ee1094b4998da848b8ecb1b2495a0c2de4c07800196df7fce5199ae8b1d97aae2e377c4be27ee6244ef2fa70699824f8062754234b6a0799fd74f97006deab6bf909c7033077c84da13827b72af8e01e6373263d59e8731369bee19a9776005722f7a466f0a9d876b52559c6095e8ad01bef83a7fee1ead8ba77de7afd556d1d4c6418180c5c8c3d9a2349f23d7506336bf6990aa7cb2bfcccd03a436f7f754d75e0b318d960c13288d6d2661aa65b1614d445482d1b169d6a0dcfccf235f7f4fc218b8d04bbe241498305c546307c2b8aee1eed7363fe3bd9596b5c962c13ce143e61bfa78b96366d310c8b23b10428ca00e04c7be4ba2f45fe10434a574c35034177ad2b3a1a13ea7ffb62c2bfbf800ee3720749eb9d061bf659d4ee327dfcfeb99562e620cf593d513d4d3d68729073b805c81d2cc060442e43f054a4711413726689246ec030444921f4a4989258b205dcaa175ce7000db0fec815e2243a8d4257043a05e411953fe4d2da6ef58c61a410740d122ef0a692e60351efb22c29d14743a59419dfa5abeb3b1c53db3c2a7c0becbe87446207fc84a85b120cc408bae3f3f2719eb6f425b426dcd831ac678353f10494ca0e269e3c1a1a206e691fcd5348089106be0d164470e3af79366d802812a5e75618dd5b0e18a0a373167f6ffb4944a0a0164bb5a7b6c3585ee2ea85195025fdaa020cda535c7a484f2da6d2e6e99928672779967bf8aa4bf10b903db478066e8a5565f2bbb1cf3723b519e168fbee39caa2fa279e45f0288b49eea433defcf2390a8c4a2ea3ac2349eb0d416bade85651a7a1e5d67a5b004ad32acfe35a877f6edf0f4a92fe0e351e2198cceed4734ebc03e7bba12897a48615f529f2a4caea2e14614883ee384238b19d515d160d4741c3a0c9d2892d079e7d805305c32570a58780ffa09631e22136fb49b6433bb6a5294808f71dd0c142ba4ab43a4d0ce7909053e033e937ba7216873d9a406ffebc8d10b56df6e58cd6bf5af88d0f1f6c7ac010a21f0b657057500d4d7a2ea1f302303ba6f4e697e736e4676a16fab7c100497a4258d09b740a66c3812b2a75fb6a20c591ed07910f9143256fd0ca6d9c40dceb069816384fb3a8d0e453c0f92be1a1f8839c4e35f49b6001675143a6eae3ed73a042994b3d0cf838a563fadf7c854123e034bc413fa62afbf887f2717e88068755ab420993772286eee0a4b0c81661f43cf37e08c10038959382ce789e33cf5aca78fe9c959dc9377a48916da3e19645f908e8910208f690c0295bd1d72f98c5d398d6b5d5862286b2aa916d063a5ba2c71a24443933532393f3b612970b874f7a6d0c15036b847326a39b22ab7a656fd65dccc815bb31e5880dc7801559569235a2e5565ea38e5145da210c5f2f910a415e4a55811281724b1c0e750d34ac84cf735964409e63ed9763a4c006bf0ba64c68ebf457080417ce2947e0cbde87b0e23c8164b9194d8e6a0a22fda0f37a4195a731670436f2b04fa0bb81058c83c4e0ca9b9a286e51a330773d88d5549c1431c4928619a22b31c62159ba5db37381704be38c4212a0f89076355bb13581015485b87bd2133ade04eff1ed088e9384726d605590e37600fa0a1496994a0a7a4c1baa48a5a02e63f8e0c5707b69c634bfb9682d8478a337da41a6575d04651341edee26bd1c8d1fb5736fd1d13a31297923072adc068d481728d1c9c899bba6dc84d0d3f86f67caff338ed1f5ee0986ef97e370a21f2336f34d977af59a95102e53a8c974f1f4931e9f3a8ccdc5df783f0e153ffd57145d992fd9e27ca5d21103e94f881792d5382efa51989c987f33d9822cde619ab4eecdc7ce9683997e50d8c4a8505883a72ac049d7b3b986671abca977a0c54f8a4e5be0166c6f8650bfe00c1e89d50012c9bf80e8b5c7954f678b16b93fb0a8dbf37b870023f04386f4d9f223219075c17cac7780a2f71394941d1b71d30ef960a4fd3d02ffb9ae4f734867687a33218d3d55eae2eca41a16e7511cbfe4eb8e778d941dccf733b9fbd004b5f7b5afa17dba00ef239cf96141ca4185f8fe78be766b8c4d76c5e88c65fd2052e70a96cefb0de6fe8b4a0bf221500cd671132e11a29839c9bc31646937ae4a4249480d67801246fbbd1b8157f09171915a0f79164830125c441c7fc105064e1b38a6330a7231b5750d0088ef28e34faf45028c4c8a202a1370b64da72d50efa3251769a770a4722a36a4a82450e46e8e0c74cdd6740c882585b72307f060d98839884f66d8041d4935feeb21469ce08edc6715b4ae80b64f7d25d21d02a70a8dadcc3e58ca00e858e95ff4f8800f9f140f94f0bdc82b744c7103fc717c26afa27865e8f3f3f4e77f0d5b4c22577e94c5115c7f26d6bb32a0d89c9537058b29a327d8dae9b909d2bcd59437de9140dd068845ba88669242e9c8cc4b59cbbc75ed1c47652866eb73533eb940e03fc4691d3872c77d5e372f714944bb55cb40ec7f05184bbffbcc0452923af796fe121501f734cb7af799ac2a6db852d141392dcf2bcb0491902a04ddf975e6341d64f12623d2bc9c8918988795a68a5820885b543949a03807b8833231f6957b1db71b3b4293f573905309ca1a21976a433751467149641b543b2781f590aff0b680044c1dbe5e859e9bda52cc0e8cfe44de44da3976693c188c0500e10426baa73b8152b9ac6e162d3332fc3f424d2d98b783b115a557a52773a89946729a85f13d1798b764f27b6f11621815765c26175f47af0161e36b47bc6d0618910683bd13b393d49bef6df1d9aadb1dee8413b7fde8d9a4967f10ebff9f7f21969d49e97a0d516575c0fe4974c26d0c1cd31d09cfbf83bd25507f9032d10b838dad8b20e20d5151fce190677577a165b3c87a72c76a2ed83e1c49f87856c1c1e6e3609d9a3d270b4ee5c306fd7ec19c6355b6c9733768794136978a4445ef49ac01cc74d0ec753b3e5fccfecfe40c37fad0a9ca0c75bd003dd26ec63e8908c8a84dd44455d7326c6d223d0e5ded6f0ec7bc8b7e55534781e04b68c9768e76a234479edfe2420268143737b11ac1123ebb827f929a2974d44a24d36d743138bc7e62d55b6b58729bb31d5f29306868ebd6570e5d090304abe5bd1662ebdd15d4406ebee50cbc1535be820eae5d0e3c649058f5a7219f7d683ce0ace10f5cb700ab2ef36feab6a4f20616c031c330d9c7d3b7224e2edf4af48178a11ae1b2b22eae1d071f1a1d2514bd2be6594bec54aa5d2ae46c50a514f4767889d2bf6a2aedb76e0425b2e9ce5099adb4746b66ea897879956df5e2ae3a307418f89ffa72e735be18d207b77f04649debd1eda00d1cdf1b5f425eeddc7fdd88c9608cc4901f0ccd8c4d6917d33f8a709f62a1880dc683756f0a73a5603664322f4761a951ab4c6d978e696db47b457c4461b9b6c112429e052e0280024a6d7cf4f044ec75db9fb800296a339850c0c0b0437864743de3db8837a20c258b0afd0ff0b85dc8594e939b0511eeaba68730d318e31bd787e06b74f41d3962720eb39a33c990c1d1d383e449ce974084801f1216ccc708ac774828fef0655a44f6c907fa8aac247deea933f0f739cf2c9ab6311c06ae66109c60557be0cd7a6533c4100671016485d56f142173d12ac4905757e5cf62b3cd5e5121bcc11a121ce9db19380e58f112ac040b70366a49244bfbdb030181a313ed2e44f029506ac0f2e0422c351c34c5e426d97ad544f422432e4e0c2061b8745233e330f4cf94ea586b23184b35a83b0f5e8fd48206f6f96b8d55479cedacc5849c589bd792e42cbd582c5a24b60dbd7df0b3188536b938300236aead2d226c32e90e722969f099c2f77018c42a6bb7398bb3245c2ad0aeec1f4fe8f2d619622de0fcf9f536e5167d856c11d43c43dbf2378ee8d269cc69fac3c4245c12acde89b5b73e327766cb731358a876e5c994acb6e0614ba8cce1f782ff4e7294303ab25b2424369c30b45837f00b240fda1c2f2193d456d7bdf515c726390496e4f064513235ffd44f257a6b1029e5faa5340c26b2125a5450a430f7fc6efb0e487bd4729d26474e7a0b92a58834b0f14002644fcd70e9e7af32c10407105f4c1beb3f418f57460fcfa61116f7cc58fd100841806b1e2773be4eae72257f432a26130cd7386e8f01d4b5871a00289b917211b41017cf9826f50039dcdb4a2f57b0e4961bbab9e0feb59039cd04248ffea62c4826a71f550e7949025ac03101f5ef22448dae8f35ab5aa0f4141025a34c176b78b653f20544edc40f7f3286871c61ea7f91c39ab89c81ee55f52c196c2f6c3b40fc5572a805c804a43b5c32d364dac08847b0856b6db211ca0108c14ba7a8b63175ded54276427d8df74a63c8aca9b6ec0e0f3732cd3806945476c6e68596accbff95e8c6c4ab4cd6fa3b9bcb55729de3d69277956cf31b63838ae439e28dea52b1053a529942872c0105e3a8ce8d087d062af86eab4a423c79216b6cf0bdb72b56a8267c89c36a540afd4a46cc9218a74c9aa780bec5841925ef2f4ad09701a3d13e111c9ba8dda7a09969d1f82e014751a434a8e65be55a08330071cccacd6e61dc3d9698c14000f19a992af9242b2e6b70d471661461b3d731496bbfcba90a7227ff6dba5952867e73a44dd21d346b42ff38ec6b1d45ee3d86fa147fc531b83cd08ae2dfff344235aec83c2b6ebdd6b2aaa8575ca248370dbc37810411dbf3875d8c12e75db435bf9dffc379afe60d3d4915bea9e707bd010921c76fcfd828cfc1c3b5efa8cb4543536988d60fc5ca48f7cd37326c80ec6ab251d0ad56ce1710a776a01fd3105716deaf8f98541ca1ccaeace1d9888323e01b8d6d10ab88902a2c7acc3d19ec4289f726758fa1d6b517c0ab8f8a48383e50260691833bbfa0d169cfbbe037b5429c8e7cb1dab279cffb4e222c18c4290958ac2ef1753b53e77b02cecd1378ba0bbaf58a76cb325bbd80841147347f0f28f0fb231a8529a88703d40624b0444383f284e08d339cfd6ba616d2111f2d8320b1c4d8da6576225a53b21440cab0c4da7a0d39bf3dc16b2174a395bb8c661e45ce4f93d2bbd48413f9f28b7ad496e1b0d984d33754624e66dd8af73eb5d84a00291bc409d3956c1943e052d746a71b0180c07ca32267ddd237f1eef85adda354d233dd773b63223e0d499d1294691d8badc04edd7420045cb86a6f06b07a6b07ed97049a88a63a0a9571e1037f14a1799e38008380df2f8bf7788b8fdb4f0abacd253ae0aeea8dc85be6208f20e82b427382241974f8814e6b66d2a331c8a9a199d0212381b2737a1aaa03d5c4a4f85301d57d1aef2722e651f5fbc59b1e2858c44e6436c4391185f87f3391a8001a27143a4fb1c2f35c1490abb6c577814c4c7a78837ec684eb1399e146f24af525e8b3cc415abefa34c1b7a0bf7987dbfcd88d64f89a22e96b7e71e66bd770905b3b22f04884ad400126e2186b8a3991b3df80a7512c9b5634bf6ac6eb5e2d160c64f6ed2106b25c57cb32fa06fdbfa687711abca6a3ccd682d3c258dc9c9aee0153bbe2794077dd893ba17c43bfac77ea3f1b842e2ccdcbfcc131f47e84de39adfabdea34795649578ba1f8be584ece6663d63273f76e779e28aa76ea4ee4b504a24c4cde2201092864a20b188f9f8ae8bce20d3a594c48c2a7710cee9b6a40bc6f3a1550c6f25159f9d39a64abbaf813e3dfdec362331ff0e27560524d7544b5b4de2a940c92b2da7383427d30d8e341b8752d165d6d236a8530b20b8865a981ab0f8c8b4ce804e3b469f35ee18c7acb7cddcad3f22f2c96cb7e3e8549db2fc12009f45f4c8f5a6509a34d23b7ce7a65388dfa1540ecab7630e2d42da3f4ea7b4064b5a2ef153e8e595f6e329701a2c00f6f32a8127c9e74370234f88482f94e08a5ac92abfe1efcbf06591fcf653232805aab1c3fdd26009721b415aee39bfb8880efda21162cd198c5b4652bff7c0992aa82b5fdef8e254ce33c59d51322fc8f73148db3e2bec7bbc84ed7b80796c6195d9c27f884419b4a95b13bd01567578802dc684624c635bd95d4a70a835948c8bdf39a15d448a181227c38b600bd102b00dfeed61f28d6100733cc2bda1982cb3eebc77377a529c5254d767805521ba862874ca23cc4edebd23d7d3862a3a3c0c36c104646abb1148f54df9405dd13fd56ba18cd5d3a43ffceae389a6a2eca751811811ac5c6332d370ae27ff0ece70eb2c1ac3a9061d0b32719d9983e557aaa866d5434d003722d881d2f8b63137ecf3472c91013d76191244c0c511c13247410414fbf7c28224e4079af5f94b8ecb4e5bfb3a360fb177662a336637eeaa2912b55974819f73c7b7ecb58269208488a135162f3623edbf16281a41bfd7dc0dd3d884e85afdc0d75c0f4bd765ab8e170fb43c845577dd78d6bd8372ff883c10f9cdeca74ebb10474aa93d0b491c6451204909760645c47d111585b02084d7862520e1c5b78e61d1368778edf62738921762e4b4f2ad4db8be68ef83ca6909b20aefdd7032bab6be81c09f733a7829af5c838bbcec3233ba9ddfb48f59bf2ca088223859849f4d0e01a74aa49c960acc51729cdfecc964ce983ae6669f61a2f9e5d698ac07ffffeb94f9d5a7fecae30fe06c84958c53a108833d499d0655421cc1fc2522f7aa745998d7a5b0e0c46003c3e5b2b3d0c3ea8f6fe921517179c0b2ec824e9e95d7d66761bdc78756a780c13a37228f35939cff21128f6a3ace8b3cca6a6d79741e2ed62c3d420fda44e59846846457c9e54146545e0420eca05652e15531de0fcbfbd7087aebf859dbf5960efea72e5bb62a4afc07cc6aa87f69bafe41a020982866e749fd593d5d98df897c7b931c5df66fc16da977f4996a0a569ab4653056ac5c007314231422726e0967c7650ace98b1f664cbb2b28b45badaa2a7b77255c25b83bcaf3a0fad35ffaa6bb2962e6c83591f992a46c760c5a23bfcd2ef92f86f691513fb231666fe1afb8cda952931d41273b9b12f79d0f47119507ac635391868654edd6f15985e92780e2e68983966d192d3122c118757c4fd67ac95769145fbf51cab9dd77dd17686361268c1b17443564e4d22f382958251d8c4a81f50a8aa7b78036a22a5f669a8659adebef6c5f1927c04b1acfc729a8bd1352dfecef8b0f3f2d5985eb5c0a9529b53612fbff9b15611b9c697f46f8b01860ebd8c0c9133e24835484a581e91bead10772e3c646cb41d21c98d2b40ac1d0a04c3d877c3943aaf1b92788b049cbd6c60719529571cd22e6fa73a82f3e5197b76a064c2a92626afe4dc1ef818c159481d440df61275784c1818ac2716f5ddc244ee8a149eaf506d2a2f609ab4bf5c757fd7b15d8dd4c78dd7190ea99afaa2fd03d881c26d563bdceafd18e42dcd8f6d72f48f71b0d5a31a803c5b196a5abc0b5b9841ad7ff8aa180b246dbb6ea8ff36a60de97952a8233e8606534e5a491c0e4ff2a279420224ed834481e9c932979ffe7ab039f1419f11bd344e18d2f3a541352044f02c2d8088270ca85ec11cd7d40353bcccc909b558388cd45d9bcc19e6e92c5c1bc644538fab7d069b81f8ef26b9cfb359037b3e6328a02193cd781ac0a34be482146abf9350b212be6c1ee58b390eb4d94e6d0c30cd9816991af56334e3c005b41c3ba6328bcc5c37c89c6fbdde24c8db10107c462bdc027eef30261444fdc515774b115f782d20ebe53677f177e1c69822a4de93651261565b1433318e5800bad80e0d7a562e299a86c1b5f7a4d856939d6c887ea15668744d7c1a2c7c7a4532b5cfb55290574eddfaaca3672d1890f9fffbe50c58deae91f0186e540ff7d9115517db1db748d5f2c2953f8381881a99a21e1be98f241b2ec627f7db7f2de8711c706025f0c614d15b4149934d490bf8387a7df76c71b25fe7cf8772bc5a28d71e63c75e6742b950579fb6087a26f7c087280f57bcf72df35eeb360b92b79d5abc75b6a16feb0197c5e491772c9a7781024e2bf7706a3992b3845222a6bada2ce90d2eda36e0d585e3b266a910e2f916cb3abaa59ad4ae45c22f24cdca6975dc01abcb9aa0af9ec35e8f09e2b002a1c591815a06caf8e68e6e4c851a82f0be4ec947477e44c3b439d6a6b218b81086eb68d6f0330790d83e388d170fbeeb78030a2695c57824e81e2a0f436062317c2f301de70b97b61a312f4e5955d70420b15404440c2096e0049e699cb222e061a5416af8944c88bde5442dc0044e6c61de02e8c73d17775847fe8404bc2f37f735d63456273e7915affc02762513b6942f51169f766f316d818760bda415ba2f403ef51f80f46eb98bcea5b212e9c3c7f48d76c9053538671932ee5a1947dc5637d96bdde250b79eb1761f5117143181fef099991e8e5f3552ea19759bc533a8cd6c4cf074121facf46eb1f17ea7844afef889ac5cba762d7c14fb4189c699723e419ea82094ffac114dceafa86511fc80f6a5d6a035a51d152879f0b3469d9b737111fe91f6fd84ba4b3c7d7ed6dcf17234c0f96337d7d5032b8495d2bf27674734ba3a93d41369eac71ad608ad82fb0cb897b69c4b2dd9c9435f91a1cc02c022620bf995462506409208cc111f8bf0cf520af345eeda1db5103de44bd5d1e50cccc5845525ae77ca20bcbf79eb8283bf6220f9b9e5f49bd7a6d40cee214487888934f77b0c2c24cd1f59c9c32c0188e6c300b49eaaf89a1aa5c8621b5defa1540631f7ded0e20d43e24d36f4d6af14dca2930ac2d4e66e1cf44c1f6e4b330d0a17ad6589f0ed0deb7edeec71e4f833fb3e57fd6f426ace06a530ee1119ddc5517189adfb746be8fac4f20aec5f263f946d323570354d7938ba5e6b1d72cd2bfd466e9ab87c29bc48f516b68b3a6ac19b50d727d6c1184c73a62e2343e4f8e097933d390929610106a402d270d1ff88a88fe3b4b898575de21d8ed570490caf9a7689f665a412268b821f622fd01a4e1568cf4947b58f79dcb815ddccec6eafecc7055a4f7a28aac73de4a0b620fd832304d9ce9e86f7ac060793997abc82384c48bbee3cbb2dfa27cfd902ade7a5fa26ff0f22fd8a43e613e08fcb5a31f096e601bc6028d29b981a43ef2580594b8709e95e481c78bcc2df9ac80cfce7d64d13df91ccc78f5863b78edb8dd0af6bd8ae0667eac549d8acfbc27d5b03fcd93f49d1c3d9e0e060a8f8994997d081b036163223c950d4f52a26c1422d0250a25458ec1848b3ec4c01822f09f44c07f86e2dc58c009eb5ad06198dca0046a484028daccdab570f08054e4c507b913b7215aa7e74b515857159718de11fbddb0f8357b261187bd3daa27f2d4e664a0ed2949f17588226f122a64dc8d40b77eccb78381994a0789263b54ffbc1f5154feeaf00931c75e6c3ea208416b0be6b5ab2ba4f4d14dabe43291d8ff91e3a8d67cdd9ac085ef40316b779c710fd5d9392d2b11ba19a274c9dada2c4b53c08b5841f3702c6f80ddba4f6f669cfb297466895835b24b894ca76eb688e418bd63c94972a4fe077d5c3d195cd8488d3b9dbb160d19c4e880e65dc697a93eb7c61ce18c2ac881162d2de44156ab0f28b4549fbc7c456fb8ef0cf306e9dae46fa123f7feb2655ef47678c16217633552ffbb793f2815d64c66d04ed9bb4a2c7bf39940fc61aac116b4893e45d7d2b920cd36e3c1666224efb59974d9667b066d81f85ee8ea4c9015602fef53d5ff18c218e041600925b00ade20cf6b3e65220efae25b167d1212cf8bebf48a635d99d2850123980d6efc0ec19dce9ea7e1bec871a22db9cf312369e0ae7f25e02ad1b595a496a39cb4235871e6364bf621be17118432e28a1df401f9707d0cb06997dbcb2423af351e2412f793084a5f9964d867b8d8db7b85146d64d7c56a8cdbbce31594338f01e10702e6b1dd8ab8036124785a102139958d97ac4a07545b84ff0a264a85e5a6039d29ae24b9529e119e1408be059e041d49a2a0a5bb1c39bbd0aaff35ac10159f53c4c0d0a773b8741be9c90875d608f76e91b6005f8120afba88b22161140b67cf0b5bf713e628ad26d4ce7bbd78aff080847da2ad69a5c83d05ae233453b854eecd0a34b11611c45ac2f2cc73a713226bc7d416d574f64501d797fe777e558a3c1e4c0898a40a7d79e97edc7e041f3f16638d72ff9aaa37caa035ccbd4ff7b914d94526165f3620e46bc2bba8ce90b9cd32e8e950912fe519dd51ba88e505a45770d273594bae5ddcca6fc8f8a9973b09644454a578f08ea4ca7d34d1be81a518c56d82784f570720b90d647b0c211418a09a8de5aa0e0b45cd695c0e055b8acf396c943e4ff264a4fdbe1ee6eabfd9e7d05a9fc6c26c875897331ee7710bfa81c1e7297a126c6ab1da829e067c624f048e0d9857344ebb15a6c70d32a4201bd16858d9d121c90053761921f34c3556c0af16def823fb64b585598100075d20a0ace0ac2ccccbf9043dbc6b4a1c4ed9747dc6ef00d2d18f9d3f8f36a136be2b3891b96d4f058823b8809ab71fafeb0131b437f34235ad72d400ae0256c777b1e1e80e0d78ff2b2c4fea168f99caa9ca8cb5c439ccea0350df044cac987f52ba54a15d8b7c2de0f3cac601e6f9dfa9e89e7324b0355dc5df4a558220ad13b120cdd6987c677b82a1b3b65624a229b4e1a68902daee5ac5dc7c7df8ecc90766593a356dee3685c9175f183a6ae9beb09faa35edf98ada803d6fba3a02c5111060ee6e8d2d1c36951cb84aafed1900db4f3bfa6cd10d0b9d66596b11f149c1071f03ba30f1cdc130a87d22f2da457cc67a8b089f2daca1ec6fa64cd121bb8535eb4417a7583af943ad473315a433e14ddb7c4390708c0e1069a6fbd403eea24ceebde3830e9136454a7180e836cff0ffd7d34af318ac120e3d9fa463dfd3e61f92416765ca15cf83d74a46e285bd62cd146d1272cb508a576ff6f94a0c31d0e5b545dfa89a0c01de8494dc207324f966bdaf76a7cca7623423c330918ff178e38cd9ac8670cb60c75cd72bdfc59d24eb9e0246f3ba9ec06876fe798512d08f936b978e38699c76aab03399e4351632fb6070b955e8b0ece16c08d177540261cbd3baa3dc6d79215a878b1a922ab13dbcf7478a91245d72abd301edd28126890d7e5a85df6b58d1d3962742b7146422963e9422df299c57a79a4d36dd487a44f3b80681f7de40db5af85a723ebbf4c71264092ad8a582646535c3472ebe03e45bc7395c8bd6ad9785ed8295356a56b07240a9ecb02d1936704214a177719c5a9ab47bc866731056a63499186cef1289d3837668a2a6123580fccf905bc9866baf4204ce2a8efc3e8cc8806af650a749379f7d7e15b2010e724c6db3cf93101b5ad9a030532cd492c6663d54fe11b61c327241ab715a517d1ffcce79c70e523e5d971eb49fb2af0a974fad686f05a5183c52a3c84373a0f726922b75c816a1bc220e4e6ccf43610fea26b489098bf592381b976ff2d47323e5e45e58ea5b1d0ef308b51768e18e1f730207036284501e1c9c88cefa38c11da72186cf7f8079e9bbeae597b94ef07a0ab9a6c5427ab3a276723a1e92710c88d40da0c7f3a2c32f62c78580ee7a80b3b9c10470528a630b631d92655fabed365506ff3b07f41b16d37f4bd7630dd99b177b452b60143d18a59c4f5ec8deacd341d8b60b22f02a768dfc70dc11436d849617f9770cf2b8be98d385f5bbf759d28ac6409252010c712719c7f84a10fa815f6cf053a8e7689962ee9e14dcc4098082aa4b5a28642cc213beb42a0c512f215989f802ee52fdc73d01c9afaa89f54ea76566f7483f4a26f790e55af63474a90cd7342ff561ed16e80083b7ffb4495bd116ca0c830ac630ee299127ed788e9c19be87b59ed5822906fef4a0090e28e012ad52881d0f08432a4153ecd1092c82901dde51860aec6aa03700ca4c18c748983a1897b25f67dc546529a46306a62fc90dcbbd6fe64c88ba3d417102b4d029057a963acf14307d8999046ddb2e2e95cf2f970dbd3b261317c26aaa9dee045b27ea034f5e4451f58251cc0b647bcb84a6c6c86336ec19323afa4c4939b1a1eeba6210edc683c70474097334facd5d5b9dba185baacb1ac24ba4e4010a73d2035b7f0981dab672e0de472bffd4fbcbca16f608ddb11e5f48937eff590d932d7fb9269c1d74aa116aff5e5eb1b4467f7a201d7f4f8a306f220f09b715c4c2411d364f663e1bdfc891cbb450f072bcd0fa898d1c5e11c5a8318bb51c6ef5c0b4a8e12327a9099b25108c984e5b09abbe0cd40c7abf637976d03222b1d3d9de1ab956a745575c90c026ac693c9a153918ea31cbfe79967d7bab38f1adfc0470de14b4661a41c8ebd9fd8945089a2548f29b00cecc3a476c78a8ba9bda09a4798a06c716aba8b788e3f83dc2345c7c2c545db9a999b682fa3f6efe9ddad6cc10be38d9174c19428d0c82a8fd56c4542b14fc22b57867ff2017bc2395087c0cc96df72619d04a2282c6832dd42f190439f8d49447962c5086a4dd3d9eacf92bdac30ea4c84f2ef261330e0385ab0adc5c49b7198eb2111317ac98d17fadfb6bfedff932c89dfeec204ee97c6992c092660c5d6db147eaea46692121411c6b61b9ab90d5d2de9f09a718c8ebe9c3ecfe869241fb151846afb66abe7ad00c47e39cc6ca11fcc69cf48cacd46342e92662125c11c211e8f846b52ce3a47084c239072164bea4f7ccf72085b013335e6622a129b327c748953078b7ac89b5f124608277ff636e15a6eb0cddaa426fa5f2611786368bca9eb4633b97dbe7919963111cd799008275e8e2071bd97807f02c90213355c7da8cc022af8d01f2d211b8279ca757905c0de7fabf3fd563c9e613f7bb437b6e696059baf026b7c7fe0ca2acaa3806b0fe44bb47f3f833e79e8dbc24ecbffd0ed1d74d1cf7a524deef821a04a7ece22b9264bee0fd491b719c07d4f2ba032904008c8de329739e5d9b13643a1ec6cbd801f8c59013dabfea805942c1aba2101f315c9f162606199adf2763021f428214ef626ce9041e3abb997fb9f0d5fa988db6a64b03533561b5ff6c57f2d347c6548cba645eee57106d5e709a941572866dbeac0c03fa902a03efd4b7fc70b2367d6fe060250ac204931ac7bad42c2a9138d31e9f340be1d2b3f485af4494d4eea701453927404ebdfda7969c923e8c764390cea4e91d907ac04d05ccaac9ce871565971679698db61edf40c402cf7d1acced60e33f2c232cd407ee49883c24dee3b0246531eb51fa9bbaef54c89ff5a558b6c91d9e5ff39f8b0a7e5960d4b22f169b99d04dcd597b2785367965d5e2660bcd9cef55bf42c017f27efa569cf921aedc5bc5562ba7be19cad4ef07a2af2263eb367ae99959939735be189d39f0607a802759bee7a85dd7610c9a8acc012db106a1633e91c3c9ad9325fe631333fcdfcaf5485830abc00b2386997c00abb1e534d518965bd3d999db939f36332574c6c85a374277477a7ab6387968d427ccab5107c665fe64fcc5c9d388a9ae040fa7b3041398280a04c3892fda40c32bf9e49764fb4305c64ebc02184bdac5efd60e7ead1021538edabca3c32a4c96ec816c9c1246bdb20a71a52ac7f02e3aaef1455315a658dd422d5e8458f8b05ff9bb414fe6846bb104ab1fa09c43772c44b472a7903249b42623c24ceb313f76224bbc6675caf8d7cfe1524e9005925ceab1395f26dcbdaf9066ea2f6d4782346a28db5a29cbb3504fedc3baab1a15f0a930d6ad4817354f956fa900a45b43051d192cf81258b45819a4c496cd8d14fcbb721195881bb1ce51c1a1a603151fde6183ebcf48a01c69ceaf9a1c805777c0e8bc6f56d92d50cf2c501589962882f0baab0c353237d0bc1f2b035503db6348632ea7ac0ed374e2ccba9a09463ca054af99242e6204e2c3b68be7861b05af1b21ed38cb43ede50b310243757bc9aebda22b1f5e21ccca2ee233cc5258a34a800d120312f895b88bbf51b56a774cd1283c3a3f587c88bb6fcada2e3b7543a72d19cfa164d9118eac83b85baa821d986298aba359e0a425c91d589ffe25b86f00d38cc7a95d8b29ab8c39f9bb0faaf2c485ef5be4f6a99c7769b23dd2a38c0c17e76caf453bd927189ea2375926d5f4ee462f4a8bf5287764344e12d4c4ebc705107acb044cdff72127f3667aca5a67e9570f94e1788a844f00e978c73ad074bc3f661a5ace51242a31bb8db5f11791b4c8ad0ae59152b33822eacb4bb2eddc7648aa661122b69f106fd7e5ce6b1810c1f969cf07d40df3cd6cd55c55ae8802e90030954a27db3ee8f9ff91f9d78eecde0fab6c4ffce60d9c02ef49801171c33b5c458226c68d1a1294078199705b9f69910109438472332ddb6cf94d9e4400c9c192999126851998855d27096e94490127aa7f2975c04a4d7401fda031b58e0c967c4ce249833260591e4f1395e2d5ffb4c95a43e74403ff0c072e085b8efa3d7baf16da8ea902101e54bedbbdd76c1bba09407f8af26f718fa60647f690e4be1b16ac1e2118ea2e74dde52e21f1ba454aba01d0dd2c3d325f630d4dcb08417ba576474422093919bcdc49bf5f1d76c7e1bedf4d9326bd93027c18b0e51ea9e587198bf1cfaa01fc84025b03e023b34e6b1b9a932acd2ffee875a938d6f79dffb743e37ef7b8d7d24bf36b767329c3e891a6884e04de1a0117a2318d40cd21e1816c25356e8a68861e3570af7d5427669e92e2d41e1ec8ddae42c97ef726d2968f1dae3e78c674d63be2f7741a043335d7e516d6e30d68c843b62d324907ef8c5aff9f0da0035f4402fd0812e9039ee57e2135df5f3b7391f891d78f84d22c912b19219da8c1a02c03f404c8e3f17b07e3b55cc1f4298c7fb12b784d7823e5611c64dfaccc2cc19081eea011e290313825e6337d89287e80826f089ca028cc08415d9525448fc95636d1c062452b2e14819565570863074108c2c14145874ec31954b86286ed154533f1ca77fae93a7e528355b0ca01beec80ab2838c1949acec0456b156e1ea1d9c436423f1e0a0d8d829a435f505dffba74b3540e8ed908a3aad97f7caed14c99d352e5f3134c794494c935488a3852179d71e868191f35aff5d24f2b5b7b6a9ea9214cf544e6cd81b8433b5fa9565b20bb75f7dc3aaf7578aa2195eec22a94c589fd55526edc271070cb89e06b9a26250e872940d110ade8c1123658a422e14716edf6874e1b97a71f60486cdebd727af7fc865124d93c9da58407128a8c2199f614a09b1491daed76182e3e42cb299345b0540a7531df83ec8b35281b207adcd161b72504b56f2facc3aa5ccda024b29aa4745edb71559221e266e848de792fe9c69bf0abc364d0e6d3bc18e3c1ed228f121ae9527d1675224b136586bc3e047d9e8dc430c230c3aa507ab8377890bc8a5b3fe32b9a317269a5f5b96b601c4c8c688e3718ca71f6851e80097995832a7a47cc0c7825214e34a36df596de4b1876d150f6430560d9e7b327480c2deb2949e3b87e904e17adfba30f8ad3b47956ac6d246a8365748395c25d69a9793b416167abc152e9d7ff81ccc807bc4e79a6e361de4d47297d0634b50e90d2eaa12f800a33e172ae20774fda2bfa353a0d472f8a7304b3327afee79dc9ff8e333abf018b89454b5e0e0fd5dc94ece803bf3d61fa00d56e8c0e5a8b1d673166438403042d20cb80532694efbdb225cf524ceb1806506e04ee25d6c647724e5790a930165de090ca8f21b545bf9a2639704d7684f1042026c3492528af2834c2c2d389d667607f6572d57ead66f4b0f91823f61999b1570a55a5edc2cdf1c71a79e769ba3c6681ea99fa677150e04fd00cd4f0b148ab6931793becda24917be0d08ad945048bd2972f7179a49f9f3299110d87154d08b77d15020ce714910dd89ce42dbfc7f98e99c1ad3ec25888f47eaa14b8392e3b6f4aa4d4c08bbbe93bf275c9a55d200563308017ae3bbc427c12f62b1746f259140a1f2d7743a219ddec371c08e7c6e94b834fe650a04595b1f3803ab5f0529ed62b03b3f6b91572ce0ba69b18dcade0f7eb53ddb88ef78da2952eb25d3f2624a4e498db7ca0ec0a03b366cb55077d2e587d9ae5f93aac72814b63c0f1ffeec5688763f295390fc3f63f7c46f0610105c45cf401a72f6a351c41b7097c5f3dd561af61d21e427ee430b77844e6f9b51ec57d90176c2ba2c2c059078b6789aa2e9d6406a8ffc9e2b14a7b0b3778f73d5b68da0051cd2f02399538a6bdb7e763122658b6d9f98706a48402682016b722379fd6ce449c54018da5b80fab8b66cec2e79928e5fe88034ebcba24d077cd536f0843fcc3dd899e2a2f5007e879a30bf2a33a1e133c6f6f3bc9521ed36c5a49e6ee64134fea61149524622aadb2ca11c6c2435b2d7f208bf4020c0513a15d11e78d99d098edd522ebe34e08ddc9632afbfa570b2e1b36b8aeebc1a8f0f0be972c3e684c43d7689ad42e35160f2f4ec90f3653c80bdcba0d25c8e676b8fc0960da4757af8364405cb667fb7e50bffce233ec2ce42b313c288cd2cc29ed1a76bd3935fc782048e6e0bb427c54e3e75e63c305422f47c1ca61d5f01c85802e623257cae11d80268ea7b1c138478e58024e0533ae818e4da4709033f56cd1b33c5b1e28ef6cfca250ad25d26953dff80138e658495f414cc32d804ed365ae5926246f24572f508da25ef8eb77de5c3637e6f13fae9715fefe437b66c710b66c7e6d6e3406f2ce23e80b7806abe0ef87b85813774f41aa2761617590ecbfde8c7eaec1c2e197507e60fc966d2181d4035f6528aeb9699309761409cdc7d390d73a5c60170cabf5e7f010af78e3d7976dedac5c49e6c47e41aee78e6aa719285970e31c720f161cc2478ed2c47f51aef4cfda5aaea04110595406733e89701a69d9e8ed66d439a6428541c15746ef7e261c1e1c9b3ea55480541e52a402660eeff8341802bef6c3db37c7a8743050f819367b7809784b83df4c83c93c6c9822708f74144b2c71433a728f97a20c270c1e2e200e0934a6e9c67f3df11328178c64ef790fd06865e6df7ceb3581a15af6e31fb51292db34d68b28d71ef404b9f82a169b1c2de60be9f3e2631563b99316ec369da60c1ffa54e1912ac937f8064489979276a42b233c52235d0fe9b3b11f6bcb171d5118874d19f7728bbf877566d83a65a4112e288fe3a33595546df9fa5c60a049208a0a878ae39a1f1317e438d23b1e81f0e0de25078fcbb63c1ec38f14118091098c910d3f03111e28f108aa3f865141a7f6432dc784c7ec4b316f31ee239d438414ba2e088f1a3ebbef41f2ed84383eb30a083088ae5e93fa69e3fa14b22f9a4748fb74c55713e508593f8ee2d45f83d22166bc2ac16ecdac19cf959885809d65853b946b6d66175add272f9e886d674654eb0a26e628dd3f73712f77a8e456320a8b0e5b3691629e50b5c0fb21c40760c717b4297bb512fe6270a0da9ca0c0000d2191d8373a8fa9cbd7c356fb9aadce25caf47d02d106dbe08663a01431cd92586188f058cccc2f2ae151d2887695030fc576ad9fb0a382102657a9f470a171977c5f0e8565508ce5bbbee76d56303c8d8e02e763593d96ca872566455cad7b07f9d2106e2e498d1d2dfe449de36497c68d9b76a4bf4d167e6594d4f52173e4ff621ce686d12e2aaa51e6329c8b2ab76e066e63a1e51e1152c5d915008f0c6d1c0d10df077b809dc25fc0c13b5de70b15f5e8ac25f24e2c83831c48304414ea11748a817469c5465bbad8fc5b3ebe3f10fdb8124be4db7fa30d63b6efa4636e366fdc3a0e36d7f6c9cf63d115dade23f560d466ac06bc5b13da79c27ca76e61f9931bcc3b10e28b98e4d12cd4c646441bee0ddc54aad6c9f644af9ac4fe22c2c007aadd8b86649bb1c6bc90d0b3738bd8b7a9b4db9522e1669110b7728bd417ab7e689aa1c8b9f370b3738fdb57a95c9745353361ae35fb19b06de128361f81e593afdf0a8ef218ded9af46ef5e78e79e4c76b952a6d6a3b09c56033aa5acefbedf05272260f4ef6b62024d5c6c32a3658f12bb4eede631a1cccf1614c678f36aacfc7f5fdfe70dbe26a73d0cb02c4918aa710e4b650baf9d542f47ca0ef423282d30a29332f367be9454fb2daf88f97d1873f1245064b4878dc2b6c3cf40a98f63eb3c29bad508786137572e004c4e2d899a45a1e6df1e2a9dce031fcab192bbc91ac17a1a303f638a5700ccfe4072bbb2ed452b2eea7e925e0f12092abae8a0a6f082bb964062eb681c107cc0efc46d3b04081e83c97b0d7da16546c231a1f56c7fc1145c4c8a65c8d37f83163054119eae46eae129809a1ee3484ae8718ed684494427c4a272ce95c1b454e2b020a2c311e0df45c4e4d6b14f2271e479aa47e8494573f492ec289b628f57020ca346139a25bf3f80b9081e1d950d010b06e8702a0fd340e4c4f005df03e3a502748f16962ecddf825f0e845fe1e005b70aa2627228461f453afeb32a4473fa13df95ce709d6ea92f6f61f9805d28332c604d8efeb8dc00ce5b6ba2039a104cb76dc5c68ac4b8f34e686b675c668670cb4c0e37682f7799838535857d60e3cadeb445f0e65cf9d7498b6d9f63c6231b5bdf19c016cd162df749dc221f30b614625d855e8bb134469802be69fd055e8b31e291e3e20165abdc971490bf254de3f0e253d6f998d2e93e75262464931ae3f06bd57aa951cf718edddcbb6ee4c0298bd870472bea7c01117faccb6778e04f01c5ecef6b6ce0b83f520143ff95e64ddea55d8db4abd4d00df7b6fa59a0b1cf43ad5b9412da379d1cd7726645737fb076cfe21204f0731b8a19bf10bf9fd7cd90be609c1592cbc40b047bd0e341d6e12e4a6688fd7ee71357dd4746c797c34f2fc11b0286eadfcb7ce0b0a4d8e8b3e8a0b96b1cbb5a024313b498063341366066475a0a5545a1261fb9d766d037e802b0e371415c7e620e056ccd18d64e6842232051a65c4eb3d0b689daae7529a16c127d1608388a6f8d372f876b241389ff0d1adc0e2d779d7e17cda6a26a29429fb7e70c051b15efb14300d0ad1da58cdbd4b117d89848f8fc92d1da3c1cc16ee5bf0d5c12b6e7c1e373c7af4dab740ea259e17e144e4bc0e636e9fa7971e9ef9bd40a67e153c84d04e22a52cd596519fe2efbb8e46bfcc90aa67f66c8283989066b15fc4d494e16d64c525fc2247044c6d5e315e8867ab44388910fd4b7c6c38a4cb20480d80752932a30ba252260918131a352ca15c873a80b887285e95fd0f3ad73c0e35a4b2f8658564cc68366b5e70e744fca5c8eb32b8033737e708538ce8628d459faa307bf09dde924382c5618004dee9f5059d56decec096fed1691f43f19d943a24d052a1b17a7a816b5f403e6a353a71195662baf5bf120ce0bee11708c8e89e54cf86846c26e7a6d8c9489c48602bbb5d1614326a2664bd1eb57710ada4846b6828d7df5ba70259fe014501683da152c0bc6ddbf1854a7d402e81ee33d9ff4aec76c492b5953e7b7926ab2347c7479582095ddd7d4624facb0ec832628a283b42c3fd43c9e157a56b01812c81a1d140f0c5b4be99380e09d25143c768ab8a799fe89a83f0c7a1dcf8c8bf9e3fd4f5abfab5fe391f29d9eab87db767ba081095698bea40862760585da1eef0381c2dd0944fc0a5f6a0304e231aaf40e63f39de096eb56bf4d3a7b4344018d48f652c49c545f7069d7b7a263e6e89a9bcaa65950e78d51437c99431ab88fb7fc2ef501b86cdb575a9a900e868cfe7077404a1bca52a83717db642d64d83e497f57450fa170834429d86f5e39e594535c87270529d892d7ac99eb2e7809f005a2f85553345ca38d48dbf5c0412d844dfd3a922309e563227dcfdc4439177f1df6ceee4d635ab6c51245ee090f72aaca4960c222d409648a1e6c47e93e6d6b723325dd9b9f7e8178bf593f99e3b667ee471776d2555e343677883556a6652b5677e266b04e19912e0db6bebb400a985be8e5ad1fdc7ec7af7775c9370d0fcc8bc87766e1dc16b42e9d65427f32f8c329534e5f81c0d294f9b83fd68b23d7f024e445bdcb28bd676fbc4e77b98e20888fe43f2c4aa8d708fd49307c5df39d716b4c68c062d37232eb73c14743c2cae17062ba2b62c3b264469affba77e434c06b8c8ac11aed0a4f5ae2a1fb49a0ce3230ebcf73c3154e08af67d72caf2a5af1f1df4a0b9a074053d764f6c4f2f218a95711a7bb9776f3a0d516a5eb0f9d5348188d685f6fc790592c1875e20a620d387b898b57c8294a8caeca2eee7b5960c58211465165a80ee919cbc4eae91184e1b09635459a7da751853652de541656b454bc2ccc6f75b73ac954151427894f38aebbe349ae780011e371c5231eef09ff3c18fc21617e861c47acc1eaa6b5a214ed5f0a601b04d4404ac78ed2cfdfd3d16e52c2b79b50df102580b62431855ee48d40f4b5c8dca3b820d30afd2e4aa2c83a2f149cb27ab26742c2be5900a0d8921aad001ab13744c1406f4559cac983393932c1f41667baeb5b4501f81e1b674d942709c41e7b4c267d32ceb966aaa560d6d4a297810304f536f6878e730d7c4938d2c1a9d5d739bbec4e813bc1f9579e19d9e73142186ceec428c817a9c61ea7d3db23c80273ad710199f279895f68aff9852a63d2362bb99a099874d5f960e25f805e832e0c294a94d35ed9a630fb237b9a79dda7dee217555739008e8c95cc57484782ffb4e8d37e5bd3c8b6a94fbd8ab81b457f8fa030aed3ec5cb6ec48cfd5a36b247244a2cb9e01ba603842e7362db85143e12ea8793e6591b9f620f4af2da331c44f09b67c917b84f53161d98f2ded2d545a61af8f0fab063b795b2f69ac4dc410c6ce98d46f68deec78f110549114c3e500beb4d94e82644f3656abf0a2d9ca2b2085c672d88dd2b21f9ec0d13e5fd04f16d661d11d0695f34aee02b8fbea8faa17f1709485daa8aec21c2035dcbd21740c2b02986df9f50d65548e7787e0affc3552e86e58004aee3e6bb56632e95cf632bf43d45c03d17266aff3c2805057a5b56d812aa2d8edacc10c88224a743a662045ff2d22da3ab854b89baa16fb8d7dad5b12fd7e920f93fa5719c64fd526a9f07de22b37da19208499d03f24be0469ca1a604c457f8913fb5ffda6d69a5af63055d2eb1b9b5663c4ea5293e4bb5e5aac891364f2494bbcb92a8e92eebf8a431ec43a708af41170e769e0bedd0aa4fb03b78b36104093c9e181e6c5807ebc4bfca4a82c0e50f5bce503fd0e024bcc31c98c9f761c121f7838a7675116105771f7b0d0d27ec10d935f4cf92b4e5077fb8164bc226f3292328883f16b1e1b3f1ab67458a514403f7cc80e16ca2efa89b7876dc1accdf289c85d2ecd239bad146f51aaf5ebcf743d40c5fa71b689e0d42c6c84e796307003b3c37b514f65d7808b010f760b7344ec85e2042d4d8e3461943fe4ebe17aed76691f454e58cd0f97e29ea39233031f406348a81825d82c5c7dd8b2063bd2a92116c672f965dd5b5ac6e756c8fed44f812ca076c8b6afb1823ef11824261c4c3d2e5cd01a8df9d3fd8d2a6116c1e3eda6fd794dddfceb751171c01d1cb25e8a94f046a3445a56f84c33a997ced7fe775bf529b8a42ae5d28d07be9f8a89799dffef7ea25432bf70560fb424c7104a924824fe30f064a746978d590e1d7ce37eac938e8582920cb390e0629fe83fc6d85d07ffff4993eac04611dd9d3bf9d6fd593e137fb0ed63e08b5bea80d447df9ee37eab1a286a241801790c83a128780e2083cc06133dc89a71c454ab98882e2bdaed295f8c3f276ca778efd1711f6b5ffbd75b2f9e13ed218aedc864b7d997dedfb1e75e677078592b4ff48e9d9cdda7e37ca1dd0dcfea300a48ddcb6dfc2007ec9a5a91bcae495909ff94fa18a08b385853de2d83ecc96c4cca7209ad81f84811160e0021aa61c0794e8b8100196788d85a51e9e4aae680eab54a935ee9e79be027e4a5053df26d897f024b02fc82a2f34e257567c5d4381e9d4766bd381ae903a3f83c4cb29e59ef76bc9e4b3f7df1d2922b45b0c5ba6e7542bdc6cad58be4665f67a41df21bd39999e3af4ee9f4521b95d469d25e28bc8563b25640fffa49088ea5c6477beb34e367ffb6c76bf22f4d996622b8f1b837c49102d31fec4244f6d37a97f4b47ee77c0f5b86c344b31e6d324b0366bcb7f331e1ab9e57d3c04a0016480d40414230dca9285c22fa58152d45194f80e056e5f65e6133ba49fe29ba0ba4058b267b34d837b75d5418b1153956afc6b24bcb4a10b599dade39df2c1804c41cfa2c40038eac9d46fb777acdb44b6229f526bba1ba955027ad989dc76eb9df31bc4419dec7d77bdf3babc0dd12bcff1f98fc4b4ac00241fe62befe75998318488cab44ca2cc11f2518f5c1462002833e16aa0a9e8d3cad92d261bc1930c4101b29dd7bd25124000c73f30eb011fb10aba6b7481cf34390f00aeb8a930c6286d68aff60843f9f0851e0168f0f4f1fe3a7a1bb53bb0a359889a05ce2fcdc2ba651959dbd32c50f5950bb6bf4506f97a5ded5f036698e883e0cb37b81c198bbc46a24a6e195073c3a8d678900f8d36809c0a5d1fd9f531260142f56af29312c1c50841361d168c81e83d86cce47c6d09964016349135247e077041e59a6ecabbd25373de2ca8c8800d4ddd24d1a8b0d2d676a3dc4a8ccffba3048ce45d49832039e0916d87b807b98ad454ff0aa49d203165c59bd9847d10df13d1e8dd207ae3ab9a0800cf90cf8b19f56898df68a88017780ce8e82febf0c5b5a1577388304bc48c58eb0ba2367cbc138b426e385da4fa799321eaad9dee396c28f644ce1206299be8a7a7eb617b37f3f2b7e0a4120e289a66327391c8d5915d7011d080b56b08d0bc888ada5ec39afd3964e2f995bfe9760ae5305cdae50fa5a8d704cbfda8e3433e5d50ecfb8876255ecf0089033508c491cc09e6038111dbdd01e2589253f548c599faac1f51832c7f3b31525fec94b842ea180feca4960d0eac3ddde561e0be1f409139f88a20235810ccac10739c23b5217ba9425e6da6c383fc1e820ef18189c890d92a715a6307c5cdf1d4829d15854194e099bf6ea52ad4158d05341b04ed7040418cb1234d83cc3ceac91cef19af04eece9a79e9b5472633d1e0ff101a09c8dead26588c9c7e31eb8223b8c0a4a04fec1cb1b9dbcc8aaa4cbcd14f9c1cf828270372358fce1b414a4d0dccd6281bdd6e0cf1ea7e50805c087df1d810322de64d09d898ad98764da855c57e67f2f2dbb7862cb2167721a7b2f07870ea3b2f1c7bbce839f02cb419e8b7d23e39a5e753d66608a8537ed79c13bd39465b977a911fd447deb9f44a932a42c5d4c16c941779c980575165400354c457017f6f744d071a08691bdb1b502ea307131da0b4544b800b8e7bc711586443eda568d6b7e7304a1ddb853acfc410cc2237b0beb9540b29b03cdf5751d090ed6494af0724845322ce433301c3340155aa6b271372197b3198a48772f8e3af3463354e5b80c022b707f645c55239bef6fec617bc868b1a1a865a54b09c60fe64a729db49b55722b136fefc99b1f1e7e0a29b958d667a3d6f0b8b62ba890a13cad9c956393948ad255273eb9125784196de2ca742541c3fbd24c7a25024653ac2a788bd388e76b2ef5e7307f47895a5563fa87fb4f4d781ac6a696c1386d941573efea58e3dbfc6885df7261fe7a7c8f29aff918b3b003179ff303cc6d608f0ab8680ae6ef02bf55787f1a20b0420404edee2e593522dbfffb9db07d7fc061d00e1d6111c4b99ef995c0fee4472ba540ea199ee95f5b1be98e018357d0fd29130182ac206b6f4f1da02fdabe35c25c66237a606ca1db5744d42152c4a4ebacce115179c613efe8bcdc8471458747b4823943f8ca42dae1373718ea28c72e85411336b002f736446f31469b46151901d7dfb42736e0dff95cda8df40f3e653483651e3843030c645f25cd176f43af1c02161549e001b2f69ff39fa19e131b98ff7c710213aa663a14b9cd67e0636d63865466cbeb2f95adbaad84d5f2f83767e3e82213a60a521b46ad8165b785d924f79fd3999a2e4db3c5df6eec801832da539c8a22b0637a7edc5934b383a2e5a261c3f94be75214a5d4bdad5029d7bdd456f433cdae642516a99dcb82057685adc9c1f88080b37181b0d40b7cd80c452b5d1e9f93aef36c7eac5006cbccd7945667b103d2f087c0a051c0784a7324988c870444ddef5d3342eb195048d15c232a0d0a9e6f2379168755384ecffee75da45b11b30c3630a766a20c415b188c196ac8594cd75e5fb99f1e4e11a480398691cae19dcbc604d1254c0d0baea3c8c7bef3b81006560be3724729ca176cb551b0d31a7f692b3fc9306741d93d92b31fe1861f08cdd0e99ff75f26db5d4d6c86bba0f235089229e82c4c456421be6d3b2684037f6aa7cb8e625810a4e9de10aeb1a6868915316d1d599bd41aeda49dbedb1683a758cfb170ea3f86b9ef3abcd270c0611431d5af1be70fc77047e9668412b00cb0bb6a963920867cfffb2776d09585dcc0a61084dd20e101573eb3fd239646a93a75f4527f8ec2e8a4834624f82e56322f097c28c66e8ef801abaee254f3759f53a042cd4a9e9a9a4d4c4cde0fc47b3f505b4074b5ac7858b668d8d2f6e6de6a3a7de9e89d6dd30231fb5e5eba9d1c2e161d607f79f3e9942f22e9a9efa3db0b0a3cbe3f4f1943eea06f54ee246b84fd1b2487805ca4831e6e1182588769e6621fb4ecddd0a1e02284c1fe7c87318834f0f0be4f758680d085e44dc9235368b78633975a37e6b0c0584e4a7964880adc8bbe246b7d40fe903367c1f498a805ec3e4758d24759e0788533da737ffe8363299febb8da822029db96dddb16015e53a35bd3fcdf4c4307df01b14734906a5e1a9c825eba54f05cf1905c159010b9bf2d8c1096bf955912177f8e09d5c0be66415b952528c0831ad49c3d37f4eef215932b6b613211effd16b85b02890a1c3c551f884e457dbf0a85263b7a57da28aea883d9fdb675e1e3db97637cdeab319fe7791ca0db461af4602e190cf9add78b09a26ed9319866f60ce4a857f29478dbf7b6d88f0809f38441a85cdc7eda2c338b9b1d7c77a957677aa8ad54bdb48ddb83a245af3372fb48a05060c92a97c96a7ba3164963a06b9141314621c9c384323b6fe7321a53556373bd26d49469926d0508c6004b5d636c689514bd6a3096d354f3be22201ec0c2e0834e684bebc9624333fc81d61e1eaf3bf48958e264d085d4cf334b0f94723123f230ab62ddefeb44fb668ab10c0ad3b2ae4ddae71449d3ecc8f279569280f7f0466fac6c5751375bc17c45bbba2fccec161d8b98416547c366f4fda8173bc80ddc5ccc397572d25bd3652db00a21a069b3ef8ece5eea65c19ff64dcc061fdeb11708bfae981ef6befe08f206d114aca1eddff8d173ab7d999bd6e021f15d3daaed0a1633cfc0f9249350f8b13b0e2f18efe298bf32465d11b0c3a1c735896a45a384f7247e696d058cb35b5109559edf02c430ef03352564b38a57cb692a87a79517bc1bfb2779557360daa882026576d47f10cd382d5742133ae6b430f23a234a6eb1715b8cf1da9c142194af78ed8f8c67f8c2288c08dbce4d4968595fe8d6ae930d0dbe1a0fe6afd7df96b288e7ba30a5af0cf41bd7182a6ffd7de5db78567bb22131ff7972771e5ee099372403be81488534f4446859abc9bbb1439eda10f8b7a203f2b7e4e8e395884e2546165844075d72e7c5a7a5688b5a2331ec5fdd77d944eae5fb8931e6215337d13cd31e43500c72695be8b4f3e5b851051865630a6694648ec43be9338e926ead9f7077d53b793c2a93300a58aac1ae05daf8e1ef83f0babec99bfad0887e6368cc34d53b3da9020ed8c69d1cfc757704a44091c55d7863be472ee3e4d9c9d513444201d1a47fbd56d5d6d1a2def9569ac5a99c6bf34aa280ac4883a10112e61c70263339020ac548093c6324de4888bce150117d1a64aa79999b704be2c14c37eefd8a0e870f4f29f6930179047b0abe9b1d1024a21c4e5339176b45ceeede4a02043cc62595cc2100be091129c0b40239c7d72c2aceda711ff9c9e170bf8e5e7418080aba229cc1acc591b3b60e04c31023022eaf9a1f2bfcad52aa1603e3ac5a2c98bd16758d423c44180aa1fe1dbaa008776744696db375ed15bb9a9e148098f907a80aba0d003ae277ff8369c44ccc2886461d639b1f07550e54c1885e38a008d5c1cf9e69ee050cc2e477406468ed5bd57d4f1db3b7388f798eed388156e4454d1404a1cb29e3aac69c8b553fa4bd79e9e92ae95e4011b0a7280ba6bb1fa96f559700fe9bfe82282a8385d5438f4e138c0801a22c08f990f66925e17c1209dc1800369b82b68d18e75cc990c494cc5ede2ef6ba0af2b0aa5c76bd7ad5c117f60bbe452ec32e7a0bf200b47000021019442ab2f0133411b30f45ec1cf8930158f6d753d0b76f87ed16d4ecbaf625d503fcd545d07fd6bd0c36a52164ac08b223ab31c2fd38121625ff329f42f47dfacd1b1bff1c9322a7dd88b69044dfa98c4dd931d841294cc016f5f086913e34260ca5a00e3a9408d0b7708cf1cf44edcfea3b498143465c9cea1e61dd37f656a058ab9808773aa5e1395f486d589d50218f7f97ddd4383cd491b7a6983d2f222ac4d41b0bfbca41a89728813ddd4772d34835b5b0331f72a07834ba0c2c53e60f9ec88e6faf2e072bb3517d3b8e727ba592260a9341705ae37d98d986387e4465b3541d0875b5f69ff2045f15db99fc0ab6a0c0a67fa118717a2f99100310939d9fba7d2a8edf19802148f1d0ebf501c4b1dbe640b8984876d0a606c0aa9d29ba0711900a4e3cb8dfa784abf8aa97b53b9980180357768b853c55409f7c28af1f0780db26aaa2093acbe4f9b2a0fdbdc47e278e79129dc0553dc70bcb514288b6a08967bcac9886cff12daa51ab53b4ccec7665651dddd6447d76ce392e7ec9b3fa85726b47ccacb84527d05235df228932c4ae9bf97fe6ee90156f2bfa475e395242e012a44994354800be47ff97131566703b30d205a5b9385970256e918395c052c0a61ff5d01ca6892ab4aaf9d823e5aaa9ae0835678aaa649fcbf21d7caeac9a4aac1df25d7eb25cc4c5521cf08b8fe0260e500a3b1cf477aaab761bf3e405c32266bf279aef078faa9a790edfed4748840014be21e3380a1bcc3ad007538744d902b5c2883f85015e9d1999c1f99b12744b9abde3a3cb7dc951bc438d37a34b23e3e007f3ba49c30c9c700ff8f1e38ec22480d857a96ae3b0b8844f27a59fa7f717f6f42f881945e2ebdeb1659e5c1a720ed25cfc87ca382fd788b19bf1f2e6203156fbc853ec4b590b29ae7783ec383ee5d3b07e7d5a070575edccf99c29b68abe5ba600d5018fb44877a4a3a6c96baf6a159c22f19bab3c9cc4619eed8da910cd959d3191c38859bb49f69040efef7565e46859a32f9f38507824ada94bdb9ef459643fda3e751fe39e64986df349a628e1947e445c32eb1705edccf5f522a1857cbb2e01ba873843039281b232f068b3cb54cb3d6514217a3b014ff69bc1cbb8dcb62021292ad083a1ef8dd2f3cf293d9362be966ad4308bd37f744e2804e9b7e8678b72bd8456c6ec8116c5380ad85acd137cdca2cc966829b97570dd8b0b5d8eed8cfa386a5be9123dd5ab2784983466cf40ca436bbef7c7e0df455238665016485435cf825718e6056d95e6a5b2710b7cb681f96b11065c503a914fd79fa6e05881ac8ff1c2e34745894552d8a286e370b03179da8bb13f967020af68324927f86875241e73d8113c23f7038681e938affd38b33823cfd5d4af2d303e06e6c9a521ea9df864f2cb80a8513374d9d1afaa808adcbd13b9c876fa08def914d91bf184575de47375c0a0043ac5960e4063c7b11f34cbeea30653ad8ba0af319be03da2da2f17d19fa29ab9b1b08c9e1c16cc32aef63606d97f6d77b2243814e8136a7a2aadd64c4b100c30c30a0991a0e26f9b66ce21ad2f1edb7a05914b45d17d5212381d717237648ba8dd7b235a9a200596615fb6c2b2f042aa770ad8b89eaee52671665458fd9c481f51eb921329a4637a035a282ac442e919ccf430f32c4b0aed6f57bfde5b0805f1d5e0f19cf0469d5f2b61653bec2606db97d563d94ff4bd668f62e79c5b40f68bfe46618fd631fbbbb9aef5a9f90d455240b1acd050cdbb18d9d956f72beb7561f34a0ec8f140ec4675597e386d2f8835365e172673c2ef83a3c1a300eff723c99ea953cad894d53aed2e60b5d683b68c20d48c85c93d6b9bcd56c9aca0e51332b6a0eb7f4770da91e0d489e10ea7ba210628b2d9577cdf1a6dd3e3dc1c23a0164ad8df09a4511e59a3463a6f52486ec29322f11fbaa7a48ff5db43429f92c2bdee70d5eb69a80c4c2be8a17d49f16680f73f6574bfc5f78f6f88064d5053a6788bd3c3eddc274baf326649fd0dac4374f1002e6d392394c75bdfce05f9fa98aa086bf51d68950edd3a76b600108df570af5ddd9324227041dc6ff4202ab40c6004ee2e377c2b205f08a4bab576216fe87ed91ac7a78b775fea208bf0fed5948bc6ea14788e623bdf9700677914c9c2c4cff407d8787a636e2aedae9b3e032ad550d0ae05b7f38023ec54f45053e0d8fd7aeef7bb6b4fd497d86412a28825700fc2013d4b3b2d2fff581012c00050ae5e7bd55205c554973d998c4190d6c011796d3c089988d8141e0caf217f409ce3d1c8d61a49115c31aa8805cd1311200012a0500a8a068daf261e90b3ae01b6a7284dd2d6d848234618d6b5d2d7f6d9bb5dea41b351121213bc40bdc0b8c0ba87acda3ac896199dd1ec624be36d40d76ebd825c2dca0e5786af5b06f02ab81c7b6bc83b1393b7bc3bbac089e9aaa063768f3d86166833dbb7907aed7e2589fcf84e5f481aa18e1eb4ec0849589965bae69b06ea145830790efd3be7cef1076925aabdd4ef0059a800953544a015763af08ea5d0d4d57f9d430bb9e5adb1ddff12fbbf1038e563dc0090e00dead41abef1b5b0f1d12022e23c205923318e109246730c213d9101896f7e61d38da58b7bc035e17a5f45950c7bc75ad55d9cd35b26a20455396d96869d01c76354de975d149e7ece7f82c3b4a3aaf79796d43f52ba487f49dddf801d3cb4b2d1c31f458144b48f1766aad425db7ee708c8fa686fd5b7d60f82198f47af87ef889116c5f5a12041c831d923238c1b201f270663731dbd1704ab7cfa537bc7da552e9345dba3d4de76078de6a3887a09f375ba7370a93c351fc788de93681d54831ad9b77e4a1097cefe0cb5ae701cd21791204c3e3b0eca3994dbc385786e37ad90df89addd4935eb31b787bdd60178e1562c709315b38ac4b4dbb795ec2d2cd70dc72a903182783c093008510b850062648a1a786663714cf5f1b0e69f47ad997dd201d1ecedc2761f1389610f1f030cbf1f6e6f84a03eb230e7cbdf9c21247fe0338de5ccf5e4b5bf481afa374c45f8f37932e7f91a1091e5d0b82479faf6e029a4392c5107c7d6636cf361c9479e760b019b321aefdca6ec4dbdb0da583f478d23bb3b294c76d741cd2b35797dd700ee61a7d5e54ca5137a5935e3ae9a467a347f1c33920ea353d65bbb183690aa56374d24727cd64435c1767745f3060c7094d306067271362c709190e89ba893db008b0cb6eb45513d85d3b185eef0cd39949f05000b6d7999887859d83215911f068c1d75d6d0253ec5e97de2c3fb31c34d83919593ee760f2a887766187c489110c2ffdd18454576f680e89908f31d000d7ee20e03e8b9379356fec90085501cbcc04d681f4076d6653bc0212a120fd1c2c59d7766a5e0b3b244247b0bca6d7108ee275e93bc0e8fa269334bd79ca64c68b32759f4e9d328141bb89ae0db5ed7b33421805154170702eb5c618314a29a594bea226d1ce182311ce160e9e935661ede2d98dc17501431c4ec686e9f035ae06cae117e054d6152083e10c0c03d082618b93e564da86f61e0e0f9d4a1e5efae7e489469f5f13fc9a3c27bd82322d04572e6e3e692b2b7aba996a2d6ed64ea7a7dcee59adba67d543adb5566ef63d6cf7dab6bd85a00cfc812b28037f7e28d5b4d56ab58256d28889b95b8b6fdbe91242a12121a1088566667c78a6d9a986b313ce00702311e8c31d5d075f52e290cae1a554fbc92466674e36987436f0d886c361222222a22bec7041f0f9582959b5101e1e18042244a8f0b199b55b64d229b8bd269dba8930b6872710283383f2e96e97dfaa1c34182d4f27415a952713dc3e5a95ad2098a755d00ab86ffde8e9a46fad5ac5e4a9a21a5eaadf8f69706b7030fd6f91c94bf567d0e8a7a02079e39083e9c741b322e6c45ad1e7e904f7b1133947e48888ae7059dc2213249dea3fa0a782335b8453427145c931624889132a3922265c1194910023e8b221321c1147105e76a3abc88290254c705561c18f1ff00e38c6cbe33da9d8ca5be86d66b392e17060681594312c9c198e766db9585fd962cbb633331b2c46166fcd43ab526c0c0204bbc15ea3c9d4294ab7a7021e0faf1c07563c682c37b56ae5c8cfcf8c1187788b898592dd906428b53052a488194c7230c121e564e3904ef55f4f128d455ec678bc09ae6437ac8bd2615dc6b062dcdc2ad910f296d5c262190e78f9d9f34d38a5f5eaa825ad673dcb7acf072bde5e53a74659bcb50aadfe4497e1884c3a1571248a43f42ccf7188be9284c801d4251b930405619844b131089a21cb64591c8631443cc4a4b4365bb9c4ac2d4a20202989e0d30963f4c93438557c64b2c934b8a156a1bc271357c33a41c984c8c1b415447816b99c451e6791c35b240ec141cb336806b58abe3f939062c9daad6641a327a29886851225dd12b3362587b81a8ec9d01b82300ed964e08a622dcbf3e31007e3c26097742a4587a4c54223a51708928e0222fd05e92e809e1ade0c260e860d2ed58f446abdca2562ba299148b55b0d8a69884371280e495189429120e06ce1c0cea8d9a9d5a11736c6218b72383a4c8a4317ce71c805ce71a8c9731287aa7d9e76d6381487e68c4356cf6d1b1a402ae7e5869a115a4aa53c8cf313ce29257defe60fd45a6f92812bb4915aef58e31e61a43262d68b94d26a82109a5e7d9713529de9548d113e296384efc9f7e468a653cf1a76f6ae87144464b7d9a5346b8d747b2246cf46f6f690629e7453f0d98f6e8f93a59f37efb0d9632c2a6acd3b0fd67766b321a053562c8126658c52c2cb8ad91de9c3a8b26ee3fc2ecd45401a348723267ee659efc51a6396179d326258c5b0d13b128380d18720ac4741d57a9ab5c29905378459948f9bd6a99f4ed8b2e0e549dbc9c406ad61b0d0964934ce2cf85508591e76d1196d8c27ebe46a5810b2bc27a564e9da970ceafb19b42e02c2ce0a1ab000f765b0bb820e40b0cb6c2a905962da6187dd71cfa089761a42458102fe65f0ba4ef5a975266a63b4ac2725c52e3a2d4bbed8aa1881e098dd7081adbfcc653a5cc82849f034fada96768cef0ca65caeebbaacebba9ab29c4e56502124a355ada256c6f4d2a1698b81281bdddccd3931325266d0eab38bcb5d3785104288d9c057d236fa0e2843a9f6e4add2f67e349e13518c423b4d6bf7f9c09726c3142fd57f3cd90967d9e9e3811706b9a70407d3cfc78916c4d5782757031e291ac2f98d01f71fa552522967c5a494544a4a29a531d26ad2b4ebba324ae3ad59b1686516c39e8f35aa14675b7e4dc0fd28c33d1f4a63a47656cc8a36466a5d8ed2f72a26a394d467d4a72631da8ed6e4d050873434680ebf67ad8a2c500183e72ded59efa2d2b2e6b4e69c5aab64b4e6dfe0ea19b4db566d285068cedd3977238ce67a73384975b3643b15ef4c35c68777e8eb1fb59d3b7c84dd129ec203c4c38dc201b8f7783b0f3e2de4e81728046c1140114820a662a29315b0af76661070351a7c819f2fd6377d76e3614adfe5e8d6438af726b09afaeaaab52eb3512202f6f5f6a8b78f80aba9578ab7836d06011428a0c6de137c81b779070a1450535f8fc17aa3617a9aae994dad36081a2861c35196c028b3a18f593f6e42ece854bc56055a7640cf9901277104cc4408db5ddfd12a382fc4dc5e1a70e4e814fcbc178e1fa280e7ddbcef45fae2a4d3a2b604da9c77084b071d025f318965bd675d4f5ef1bdf9eab358a6c3fac4550911adca301cc2c21183afdb1268d785b7e68e563d1c9f43881d241fd5391a739756c12b304482071f1c85c0da1246291b75516bbe6ed83a60efd0d730a8ee07936b244e8070c32763d3aad09e8d798725d0a47cb1077c0f65bd1861940f46e89ceb547f61ce39a7a4d69c11c668fbbd47239ea639219451526bce394d6198a639e9a7a4d69c73f6a443a8fd3e6168b543f153050c9d0bfca142ad19e17561d7b582dfeb8b17b5668c11c608e194adc1e799863d900a385b18638c3e708cb17fde7b60a8011226046113a3365848c137316cce3963b43293a777f50ad87559d2706b8cc24eb16004c35337c2ae0a2cec00d3c36d84217c74b69d98f56aeb65d58935be367735a6d8c468137ae6352ff95aac9a50863254818625381eba2ad0d0050c1f71541c235da1208b2c22764fb0c118a04007222cb31b1187058519a8b006ec5c2a36c1049562f3b228cd4c572901299a4906a536b4c1426973350f67c73621b6da8d2bcc11a349d35c7e39e79d965a4138187a9653b638e680148b568c378ad25d3681ae29dd1d1b11fdd24f31e1ecc8a06d42ec6803f4ebcb82b8d80004d7c0f92cab5a5686cdcbb26a8e186766135d7d167da49466d8bc78f4c0d4a292526a552b6810bb2830f98957d82b7268879bfd8357b82c8a127699d9e410022b6daf46dba2283de5fe78cd25945db756a191b61c428c81e4b21b35eb0cc7c3d92b96d592104ab86af6daea4f2cbd66591cf56834aa23fb7280afc32dc332b389029320f87a25451289e4e4282b8161fc8e4edd1c42d01c96655d97854d7a5da4cb932e2f8d74e588f1ea548e6849694d3b2a597a5da5cb01e5a099b50940707640a61ca4fe196440724ae7542fca485531dec167a3e3fb5297c4c32e7ede48297c6f273e5845855bff8010c60837fae406e98ba675eab91c3999aca6f08a98ac99cdc83e2db3816d03ef8c66a38e7cefc5930b86105a27e541cb3138de8b8407cdb2b65f2084f03d61e78c3831ebcc3b5f113caf655d4aec0da453b2093c65036927de8c66c19b01fb473b313787a53c84706b2072523a1f1157072101ca6e6f3eb626fb8addec82b1cba294ce39e79c319ab0770ad3c1524a29a58c903ec3cc666a2c4eb4ec823ba55ddb122d58891d0c2f4a07bc3a0fcf63d98d3c9cd7e5c6104e49218410dac88610ea744755848d755a07840d2184536271ce39e7c4b09ce382e1b13eb659fb8e9c25e63d4103fb75c53b61431c5511367cf2a46dd077120fbb274f3094f29aac4e630863ec4b080773bdc2cd7441795daf177417941042784179bdaf9c73992eed65296d8c86e11374c64e659d88e5e183f219420853f3e6eb079be4bd03b4f55993ec14b476802fac67938d125e1a3873dc110c49751875066dac129b97654dad6a4d6e2668b2d95e9366b21147dccea92e6035537e38eb407c499c5dd48dd489d83ec78f3eba39e29ce382f0e8f095544937bbdd46f51a8d7e8dee96ddcf4db51eab17ed203a4529a431e2d8704a79032f939087ee089e91d2471ae3928818bea6c371cba79c9986514ecbba516602a372a000c393708e9180931fdc4f6659a49dcab2d1352745f8c1c106126e8665c12ed8d91e687d4a35ed8467cccd0c36992236f56824b9d91e2de5d921b1b44536bf32af326d5a6eda4e6fb1ad6c3985b3ab6c3906673789fc9c19fcb7cc53cb35177474f3c3a325339ad3b1993158deec342db35b1d6877f4785f2f9cf89b72f3ce0f9cf2b945d336832702f07cf1377472c1f391b6b8d974d451b627e460ba7ff44bbf22f8e20eb978f834e35e911c68f63faebf1f3cb6479b772172d990802ffd568eb435915764f52d0eb420411a7b543c9f2473be873b0d746a3e6e3ddcfcb020446266837247308cc34c47132e40c1138af0a8b4398cc211738e2bc230ce72215ae611d117681957734c133373c40cde6ece0c3ec9703562bcdff788198cd372edf92ef8fa0cd6aeb5e4cce0f996d782057ef761bb007c7944ae533136e7605c36075f606c47b8223cba38f132b361b94e8348b822962d5a21da3c0d346d4b5838efac30cee8adf07c7d2b3cbaf52c936fd5aa1757b938b91aa5f79fcfa3e2fd2001655e5cde7f3fa04cea00b8eea04c008866f00db49c3383536ebae9a6ab6c42546b032d53d43b099eb758607bf7b2bd48cdaf6cf32aa66d1ec5099e01d81c7c01c0e6c3bccb36af11e179f3e8d9e3883a24d92db9a885e72311dee26582dfe1a663e137d4a9d773e7768b202bb4e0439a772e6d80bead8186840e13efbd4b9f982bc00215d880fb9647cb8ec8c281961d5113d9a03ee7a69bd339e7a69b6d44a756fdfc922c49f294086da149abf27bf29ae0225312dc3ff50abbf7de732c9d6a6c09da0b1fcec70cacb6823faa7301aa10fe6817703522b5eeecdd1f107c41f029818fe8357972784250a67485e6f06468bc973e7d79a0710287c4892433c0ce75e1998087070965783c1870369e11948e8bd34f49120ffb143d25b89f9167e3257933fa3eb97d92d4c35ba110dc2fc9cbe8bbf17551177e78547f8d4a9b96d59e42bbb0edba1540b9769b404de0d5687f35dafd616b50fe23d6f8d07e025a533a697b352f2322d6a0dc13948054a3bd6b5c6d0263cf0151ff321b9256abc0d79fd0841bf784d73e706626262695faaff6b9dae7baab02d7c3bb1d2760ec10b3bf6c853dd606874021b304da03dabae66542f0704f30bd15371936f027ebac8aea5cf20bfa69a848a808c8fdb8196a62d9e1649888713310657b3d0d638cefbd0cc7133284661a6954b3cb3f57a3d4f384f8d4cdb5cbdf0d594ea7ede14724081d4d1af692461a6d37f21139e260fad9b3220a478ed01f68ae536d4fd3393a0723afdb227e990dbd4e9f106b88a45694718489094a8b6811bdfba1c1f2d78d1816a12406fe74ea18344243752e48e8b44c8552a85fe0e7dfb3a665cd5967ccc914cbf75e86e361b1e4aabf6ece699eb7a4534298116d3e3f2cf21b7ab04890209cdfd07b58b46ad2bb0b26712288da239a872ec14431189a40261ceaab712e5368efc78bf681fb56b3220b425e103968f0e688f3fb912dee1f16065a6e1fede347cbbc81f8c0c210ec47bc815a853c140980f67025f9d0dc314d6f374d84f500eb1407ce2791f6a4123bd85d01b87f8805dc5660c107eece3399cd7b56c999c133e5f27c3668f9772fefa2de1f0f26f0fb8943d4828270dfc422851593b80096a77ed635699deaa8234d9dead47b226af1f35d94c574a0652da59dd62000cd597105216cadb88212dcefef8500859d1559188924cb9252c648d3ddf2bdf8704768450c1f5bbaeef85aba27e5bc2d812663bca5e35d6b3275ea6df4c1e0d806804d31be68d9694ba0bdcb1be13c5294c4845d148a80851fb477876d09b45a1da6adca7df8badfb3a9ce999e2dee1fdec14bb7c636a369d0e01d8cd8662488b3d15badf07b6f25350a459eb0820a1284514a88825142e8a8355d6fb1a13c0dec6c88f83ecc74c4ec2f6bc0756da75ce376cf5d188698e384e2bb281df03a2d54cbca4c76a5397993bcdd87b971ecc6f103eebbee0b4d0e765f545ace4d2bbaa7e31a02be3bfe650d80d98d950d011ffb7ec2965730010ba29473ca39a373d3c9e85c7f4e17bbe5f62c7e94c637e3b9eef75ed3760f2563c78e31c66e391bc656cd487b4e89926e4a79c137b56074cecdd66edc1b7d6f3fc21bdfe5a4746aec9ee1cbe4e74b05117324478a860deb7d083c1bf412bba04c2a95aaf2653704802b1d344a04847f5b0ae6197833bcbcac4bebaf88eb5ac0ba49dca217bd2eb42895ef0a91a3534174aa8928c202312919294a29a59452d2503d525444b143128628c0c3274fde8c57802341d83dc1063ece95b0b3c2481016f5063ec9c34e1454602705a2038c01f76f3c350481431a709f810adc1f030ec47087332cc16111042f8801b71594e0aeb802922b382bf888c10e363c1cd06a045807fee19777a61003ee03a8404a6b946f728063861560b6805370501d400c0e89156e162f4c50b2020f488f48c315ae8ad508b04e9b70dfdc8124a5195426316694917002ee888db207823ce004f7e3124b60405d830684d41b7073015b65c07d59836c944140072b0be8036384304218238430c60861ec338c32964fe0fe7504b70ddcd7edc04759f0a30926d9aceee4071bbf2630253d23bdc78fae5b273ddb9c8321dd4c294ebc4b3c0c9fe11bc36390e2d1e7064f8f7586e3dd399879baa1746087237a923b82fda4c007c60e4ffabb39e2077b098aaf5f97de4c9f53035bd65d765d1e1043d296e12dec4a77644502ec320738fcae7332b2d13c5c3918f99aafbf1868d7cd3b0fc7174f9dd546cf4202840d6d6cf860a71e0cea7bced9c0060f3b2b6cd082878721f10a2748362b6cb0daaa700739cc392724c2ce0a215cc0a63b778515428e6053a79e7b33dc9f8dbeb36208931a9f8dc602c033c3c0796564c9e7d8950eda74710e06f6595a5460df6c3dba19f4cecdc8fd48fb26716cfd8079b82779c0f38d235f6c5dbec22478bc2738892526a6d7693caf43310f05607aadde227ef3c2eca631842e9d829f83e6f2ae78c10f2c8d509dcbc9450a4b0a5f6ca4de8c7e6edc2b495d923e134146b42abba21e17d040abde8ea7fac1bd1495917a3660dc8c46f264e0a659c60c1946c8c0dd1a80cf4e480684524a2865c89011e9b30c4a21a494c227c30a69ab86b3610f1ceb5115bb68ecf9624738449c114aa8845ad5f0e8b356d60150fd1a64cfee68abba1f51743e4abb2b76c539e97c718e32b043f4447675d0b65349b46c4738db76307a0607901d92254ab00ec453471b31b2831d3220898309d600d413448601e782c88e80e7c59957625e0fb4e0f83805bc73913d7a87761482c40bdd5ab712dcdd5d4516a8c041ad697003bd3791aa1ab468861f6eb079d9807977e47b5360d7e91bb4f91d6ed0ac1c595a16462df66959b5d65b9685d9f7ea76bd1edb5033d2cbaab45a35d3719d3ea294c8116f9d010ed75bd6c5a9d33e07c3f5d5ca7458ee0c4678028910ee0c4678a20ab7d73a0dc486c070bd1646afd3b76b3b87b84cf8391769b5a066139640c36e8f7a139bf376cb6ddd1edb503d1f98ecc14e84abb55a1134ab75aa2f1bf3a67d37cf9c7e9e1d507641d8cd4e8913b2cffed55fd77545967af3cbea6d8643fb8bb3b4cab970d80551ab5cbcbe6636356563f9dc68294565e50ecab478537bb386dd7c7abd393561f7b83027184a647269e0853a867271acdb19dbf06ca01c335dd675b23fdd4894c262e55e272429f7ba3dd775dd7c94f97c3de7d088c1d82f8ed5008bbd66d4d5696c1dbba86bf10e92ee602647cf5a95535b1f8c9b53a87bba2deecaada72b2af7ba58b00c07765dd747d765769d74ad44154a658f655916ec1e2e32894e70a6f691496c42517e45a2d8061a8964b8b09862cbbc3beb3198c6abd328c79edfd569202c7e92f820c1b0873d0663f514e515655e9dc6955a1385462629316edade3524e04b3fe7946309ba52a89b531fdda02bbb320cbb2e0af5f7f9d349a3d7270d8c3de7d0c0d961e8748e2c9bcc7060c7c96e7e5abd8c19979caacfc75d6f8671730c9e2c4f4e8c0201b66d7836a6e302ae775b88a934ea6fcea181ed67b5cf5eafd2619f7d3359fb6b8b1895bd9e5a55cf4203d78a005cadaad5ea6ac8fa18d3aad1eb25e06a9c5eef621b26f06c8cde8c7e7d47a2393b5e940e78ec38e0b1efbc1eb85ed95d564fd3a6bb83eba6d338bbb10d6fc674f1b7bdd02eb0998e5dcd737d88646bad774fd96c8f66e117d4415a507e41da0bfa99250783abc93e6d66cfbe5edb85619b367f6df42db6fcf01210a7dca67c3e25b3a18d535a983a356d8fe652b69922d4024ff0a55b6437b25b8bd95aab0c3c31ccfead6437deafb7a0afcfb6235c30cefb75738bd3d7b75517561ee182b15797e16831b3bb96d92e473d15e5baeca56dd97d9bc5957ea37f1bd5b4235c708e0b36754a3e223dda115e0f56d55715f6ea4cad6959ad371fe3eb5d164fdba3b504d5f75bb65a6bf66b239d6e594766f5a2d87889e8542bc9e15c49bba42de25a35231b7cad19f62cbfa191b517bbf95a3644c51729c3a15d5eab43ad8aaf1f6536b5d69b2366adbd48d7e7bc0e4bb1a44b5f16cf066629f686f0757ddadbebe2908eddde62d7eb7551f4f6d66bad37df5cafbf6eebb13b8f028566ba6549190eec758459d34db198b52919666bf39028a9d6faeaaa8cb07b308161b709ced663768355ebf64fe8d9b86e1f93cb035e96fee63784ad938e51d2c59ee3c5eae7b167233d1cc36cc4a4add6d3792fa137c33aca96bdb48deeb26def61bba7819a765f914e5957bd2e6a7fcd9bb37836ac67a4be40b18196edc5b97460b7f557bda879d2ad7a8b922e0a14d9efe620e9669d38b26e331cd771eca5afd910165fd7cd4aece0faec2f3b96d96472621058fa8addc2b079535b5e6f9f9156bd2c9e8d876bbd4e7b43b86566c3e3d98b423da0e5f713f8b7df6b827ffb0eae5bd670b5dbb639f852e937ebd8b683af4d27627a5f166f863537b9b5ac60741053785052ec878a67e4d93019e51d1e8c33b21fdd4ce539a6a094482f9e59121003159369bc9bebb1571728d45b7c4096c36bf0a48d6543bdb4c4bc88f113bc06c6e14f315c905c1bc0c5b6b5000a4aa2a45f8e1ba236d87bf42fe28b5edaa998173d9d54172f21841042d847cc601c991f35ef73a66bb6ee1c5dd8f349559f535986dd489b832fa3ed5178b375eb46bf6f1370a736144275396e084bebf4aefbefbd07f48e704318e71dde0c373784c3f5cd366d5dd7d66b9feadc3510638c313ee8fa29498224dac4089524c1ae8bbc927c48f10ec49d8b26814ef01a29b23bfc8bf91c24dc903b0ebc4818e186dc95483ad548bc2dc7082036cd881cf72cb10cb24f68b9993413a056f5fb0b249b042232572c3dbc3f854c16b44ac65dbcf8b4c313dc45120fb8a70f98e7f903779e41704f9e3904779658e07e009e2593d4b3247a79964d04f02c8b708b67e904f76d0fe4906e39a4e5909643e4901f5c9d730e4227dcc5da9677b0d3342ddbf253b9cb1c49a6002887f019c12baf085e89ed4485dbad1fd269ba7fb4ca09a8f5db54543e7ab3c08906d2aad155defd33d2b6f7c39da6f3ce9343620f14d23c0fc320d8b1c0659d86b0b31b8f24e1cd2a77a6a3dc7549023948ba3a10939ef2fa0a6ff5d44abaf51514283495ab13b1d35036142834d3cd2917653e62e7e24379d681af88c4cfdd3acebcc4ce2acd971b0886edc49b010fb7d12b85f0f46e6c479c308e7b0a4a7c1440bcf4ce70c4bb9b08b069fde2ee522e53ee4ed3a52d0a201e8198b75036dba9f9026c5abf9c523e37021c6533dddae4dd264797db667fe59c53ff6895c3a3b713cf86c3a337104cd396075a763794ad2b640924911891d384c8904482fb6a69b9ac772ba58fae72138efbbcdbb23b77150994a4074012496ff25917c5801deca6123b385e0fb46077512477d235e2f396750c92fe9668c1f2efeee61c888a28799c78eb325a276dd9ba2d3d96ee2efce580e71cf07009050a6dde5feee2941e2f8ecaddb79c135e396195dbc34c4776a37263caddcd23932905e51aca35144d739b360f2ded0ea2684fd1ee1af03e6f4eb90ebc913d4757b55b9f52ef3657533608dd530e1d74cef2e0dd95bf8bbac9fe9efddd8c618923df32b3b7646f794bcb96e546caded27219b77c32025b9a09febdbb2edd4c6f5d3b109beb17ed6695476bc35eb79c7322656f69b9599ec4801d477adcb43f683dbe5b7fb7aebb761bc32dabfc74d2276a1ee7a2f468efae3cae90eeca96556ece09cbdb72db826dd9086c8f693927bcc4b3b21b159cbd566b5f96dd6e138ba9a4536e9b4239f821819f0e133df02bbd96b689c510ed274f25b89574aa3f148f740a3a2863fd45eb3eab47069e5b964b70117ce9533a8b7a66519192563d2c844a25205e2a412036b9a45ffa755db1efe69cf0fb4aa643005bf8c116de77f0e3e954aaa5908b8b123ca30e742e2705d4a9bedd8126a455590e71ed438ccb09f7107f6036cc071f0e7397ddf0e130a76998fb706cf321e618869db62ae5d8b556cdf80fc78e9da555f4d8695ad3344dd36c0fefe1a6bfcdf4942d3f9c62b79c03dbd248a5fe9d5a1505106ff9fbaf77738e0b4e398caf58968dd6108872b78d64e928b7329b518ec5a5d116b135d4aa9556a1c826594328816894b694cbca761979e6f494cd61ab9dde83cb4f97990d0c1517ec72954c078bcf1bca805d5e9f85b1459ce27269cc663b857dc6a6750afb0f9ba953d8517463e914f6d3f361cbc731b2d573c12c21314e8cb73cc65b60b61897217fc82127d4bb00d8f2c300b8db9ce5a2d3368904c899524af074b70de0295b3e95a00402ca03b84e7b2f0670ba8a0036dba3bd6739c4f60cb13d1a0c000ce2417af2e409a641310e48bef7de7bd14598ad1bd6ff2ab7252c8c63fda59b7756b8f4b759bf5b7ef8de6dee488a439269e097bd09c4e33d79f2e4c97b4ee1f7e254994cf8ddecbe1d886d52d1f3081094994cfaa5df733201326d0faf00a008beb8f98309265d88a853b647b38a583d16112036a9645a3d5309864d2572489155d4aad7c333c5307a388cc993b2cd2073fab40aeaf4cbbbcb9d0f4874eadd7a0f005b9eab4cb1cbcd938a3c85e03e8c593483d099cd769df6b096e382b08beb3762ec5bd6b1de4af2b40a0601de9740a00c10ad02ba122843f4867ee2cfd08f8b3f5b299a4e03e71212e3986e98de627a565f3ab52f9da555a3976e5b4602c197ae281bfc7b47cc6089f2b884c438d65bc5437334b4ed31ef02d8fd695b76574762f794755de2169f72b9e51cd90de194a3d8944ed3285bca8d9d2add6ea74e953eda583a55b2fd52facd265cfaca71e99a4e0369da14a3658bd1586c460ca16ece0c865046e6fd4885abd1e22da1cccc55a7418017a7ab641894723f4e4934823bbf00cf33180818b484b5919e23cf6095bfcc8606aa6ca79b3383df4f40b84fb77fe0eef1f94182fb3332aa57e4e00b8dad009b832f406c38d8e6e0eb5d86374ed397405289249a3fe6115703c80c3279a28c0c106fd50df00258568f454302114002c1208805e941495aa5a455dafbc7eac97a200c6ad58aeb9b5d12bca2b2a5801c1292eb977769001a05006273d98d9be1aff823815c121d0bbf5fef52cbca6c5a4208df7b2f063b087b09eef711dcfd63a1129c248921dceebde9ce8c9e40fde7a455f236d22aead5bb8fa853fd8a5e51ab56fde3350900007c70e9e16e6f89018305f5c2c5e9b5785fd91c8c2905a5a4914699837130efd74637ea60de99287245f739e954afda00fd7ca2af08664aa35326cc847337bfc7635668f9bde85d427908af965d11133388242354161476294db91a3cfa381c4e3916ccddc5e5ba8b0bcb75fae6b13668d6e771e6edd48bd80689541f52c09422429cb34ece1744d6f7de23e2601adedac46d95c8dd1ff0e552a0c2926ae1569bd4221f68ef7d480f6e9d2258548b3ad53ddebb45b5492d7a25ea54ff55b7d5a2da249379354dd4aaa28a7aab378212094aab37a32ffc2eeae6ddfda26e72c49727c7e221b8b3782be6a3d55a89dee06a7469a569a7139056bd5722f812a465ac0fecb5b4aaa5552dadb05b897cec4a095a9e4d2a1194714b7cb46541ab80bcb4228d321b575af9c06dad2a760ee6513810bd19ced570efd337381beff00d1b84c39b319a440ec641194a74a4652e1f4d8964b8b8e04c8b10657aad3a557f7e80829408419958f31c76198442506888a868056562cd0e842520259e524f8988abf1301fad7db45c895c8d11381bae34821209de0c5b448b3f1128280a41d40eec3b3545f109a7a2455c6a155d8d478d2004f7ab7d0ff5fefa1ef6a41eda776a48d7aedd1e8e7435927cf87d4712107035396e0f9aa06741f6a288c03e3a76513b7ca41c3b8a88fabf9ad195e288777757839277878fd1b38f6e0f297664b7c7abc93efa4e4d763f2bcc6eab941d29174504ca538e72537850fe6a502ee5812fdb4e4ddd88281d7ba9b49ac0abc1fe6ab04bb3cce6dd11ad56b87f56ab677dec8f15b24396c81665abec470624e3c98632a2114f56345a59a09eccc7f6404ba42bd1e887909220a0cc1ecaa0dcb1d20f28837217f580d2b12b02ec25eca58ba0dedef52b911012160a2dbb7aec0f059a46c4d5b081312257834e9e9eebe08bbbbb59db6a51252239d27b5c4b64a786a66bddb08d07e57a30ec472d822f6daf832f3b353b35a5213d2520ce95784a41302c68f1d40737cf95832fb4872757a20aa402e181323b35b016a93eae86bc181ab44c79268659697506ed3d0a090d113569d58539f1517fb81a0f9b839677f0752db3b968c4d81cb4f93c57a3cc46d28b723649d0fa637f8eeca844f0a52b503dd2a9b9c28a603e50063501ec15bbabe91fa02025aec6db7ad88b8004d8ed454de08ac0f6c06eefa4d90eec4e22f0a56e523c27345b44cbd86a85fdc080603c580f4624284889d01051d1d573f55c457c7e806c112d5322dca714db766aaeebba78ac6985fb8f044f7397ce39e79c73ce39e79c73ce39e79c73ce39e79cc35627ecced53cae44455066a7e6e707b440a04cace95b9ef956cf06caca5d4ac4c1dc4083374f221e28137df209ced55ca54aabd2caa7b48a3e3eb754895e934e35512dca0fa382e6707442832b288351ea6e1ea500f72b5169d5aa9c32045cd4aa5c5a957c54a256392bbad0a45622abc426b141d89f111f28f363940227537fdce805b8474fe0fe2807b8ff3648548970ff854f25c280f25c5940f0a5673ed51f3757b8e710ee2553c8c1b405f46e05924bab150a506985fb0191f94d29825d0f4d603bb46d0c8d6ee4ddf938e79e914ef950fceef3814c60faaac40e76786eefd03dbe5b44a7dccb022661e3aec4c3f01551c122ef59f3e689732c8658124e261618ae46544293b69b0ba269a557c4bd08771b5704ca68fdd245dc2541d34e3176cafe4d6074d25ff62e11a36b276d3d7e20aa21bd487d25954ed36f2b02656011580416e954f7786a285243baf69476223560ce1d0a218431c26c3e6617fbdc64bd76b0ed8d3e6e2ecb01ef6e0601110ce19ce6c1ee4122b85d13f98b98dbc8c3021786303968daf381f32ba2c9402354bc9083f67a8ab491d7f386d82609dd745914cb271ba313dfac5dbecc9b73b02c22c774a2ea5cb27330605c16162c095a66c1ed52af251d0442085f16b0ba1ef76501374851dbe92e834b709694dedcf22c63ad3bf0453ca10761105a691ada40b99d4e47fd099d4ea89fde1d04a38ae8942441f92d8a7a5996c70d4aa1222ca550132ca5909442520ac917d12a19ef5bd229599fdf9298eb8d32055f5a2503e3f974d9e2ae656852b0c5cd6e09e966aa035958685858ee8bebe2e658ec62e52cb774ba3a10bb58f9751cfa885d5c9c1559aa2f41892149fea297585e2d2efa96648f1b8aa647529a7ad895cbed0813c6815f796df9d35a1cc6d6020f3308c078cb6bb418e662b32bc7565a6c51c488bf36bad97e31dd58797c7d2d31c6e6430a172554dceccc31158142fd2d9142c7d3feb4d2ca8bcb1627b19c65e5a8bb38dd453cfd16e34fee8a39c8a011377978c52b46aac40ece39183ee7607a8a22ebe1122d780e411fb14789c22e037630858f2bc7564e2f0a141ae600eca240a1d19b33c4cab18b737acc3165ea3ac5728aa7975cb828b538a9c5492d488f142dd2650bd24bb74897b0449287f205e9be78e9c5e5f6e2e687e5cae3cdb5547a2c3d5e4546a962a5d0503a303dbca73ff86c65e5266a5ab9e95b5e39cd564c38075f10c63b48bad9c5afd7c3687194bbd8728b4f4a4d37ddbc72fa48af43136df194c7538a4fcf267c8a1767e5a72db7b83926bc724db71a969bc32653cec1b6e4b41c5336a230084f9b6d198e2d26239199c0b407a6d46e3232d2fc45baee74cb6cb0c6acec4666373153e272297a9add441df02626219f840ec5979b41af73c2d4ea5cd94d5bb92d91ddf40acb24487714fb3597682c219617a61f901b753094fea2d673891fd94c06c6adbd0d9a38f2f598cc6ee4b19ba3bcbb54f0e9a0f1f62045156348829d14b0ab620c2bc02418479da62b8ceab2185f1cc626c57b75d9751931d3aa774aa3533331ee22f11e93738576bdf8db6668f4cb7b22bebbdc42b07bc492a0651ae3301ee3f6f881a826c661fc6677739a676e2ca736e1405d5350a73abf18ee46d192c4d5fcb45cd72f316872c4f467fa25e668ee678a213885dd8f12aaf0338511ec7ea6f00183dd8f126e80635c0d2713e3c323042ba03842169ad4f45fa6c3c2ef4228a5fc51c20c228c2c41389977cd7b118e08a75a51b5b0cf24fcf228c3cf05333f80c82ff80d3d17f8650cbf8a1f029ccaf4771aa73afdbde4aa536129282feea9532cce1480d3c9c5cd3bd8c5dfe67045bd3e7a63a7506742107e71fa7769a750375c765dd7461f371cfa1718f597ddd0bf78a51bcb91c0516379f1b85d2f2eeae61c8bafbfbb17cebd783e96b521e1cdd8dcafed1979335ebbf7ebd4e26161cb3adddeafcd7a8bbb95bbd8540e6562de87628686f2738c1b52d9e2ab89feb439fcdef567c2b96eaec75fa763fedd8479b85328682db667e4f9506182e9a36c1c7030dd06dc6ec0ee0d360e8f48bfb4079c902ec820a805cf04b88f07dc46e4f7f37ee812faf8aeeb3a0e87afef60979940e9d1aa184309b0e576f0db7420b66414304ed32618f705c1c7052349d0f2cb69624410b41c1343c3a1dac5144db6e0e451ca392dcbb22c1999e8d106e86374d072ec88fb39a1b9ec7dd552a8fd5ebbf7b0be20a5969b535a91c2abb15709a0c1eab08bd6d17dae2d3790625057eb032d37911d68b989146995abe1b0c3451c0943226fe7b6dc18557230fd871ba350a620c23a8d21bc8d3126684f8b47345313d90210dcef20b411dcaff047cb9a0e120ea6e39b48d6b47821910da2e57824531a832218926091297ef6da18ad5d3951f3fa89dc5c9c314aad0852562a27bd35e53bb3a19f52c64844299d5b84cfd1cf8dd28dd25a8f9dbed6691fcd6c281318fb9c731ec3b026f07c43d59a2f0bec69f418a5f4439da2563db6557b2dfcdca276db2ad2ca9c73cebb3b7a1d6d94081ae93b37b4165f798f56616f7115534a8ba3b478e9d9ebaa97d2538cbdd60da71e3be561e1ec1846696e6c27cd6cddc12ebbb94e8af8b15553357a18c7a46498ee6874b3a68d1e5b461bfd61d8ed19717d96d6c475635a112dc7e06c3f376a95d8c1f3b7b6566c27769abe2c0bdb1c9e578c449d9af559978785a30d5c792a4ba0963cd94b3ea3f8ba59a328ecd875facb3abd46e4ecc0d93de7047b4e279dc63702d8c20fb8703a1778786610a455cd038409b8459e4e358d36ae53f7520cd539e79c737de7ded3b2bbbb7b06e71c1867f4fade6e60337874177cb586db647adf7c9d9e4219d375f025a3b647b37f0f6206ca980e4f93438856a1a858d82f6926525ba72d672667e6c152c7251506aca5a8c40cb6e5452d999a11090080021314002020100c078462b168402c5574457c14000ea0a854704e1787598e29648831840800080000000044100409009663365d0df3fc26e86c2f4b93298a4c0b525111287476f7f0e09710190119e6dada482cdd5ca35d2432560d9fddc3a4e0906a835192122fc34a814dcade8aa881f181b68a4bc45080914c3c0be38b42ffd91429172ca9d6918d005ea107fe08c5bd1bb6dda02d67a2e31c5b5586105e6ef7d4b080b2556b8c6f3a7bee0a110fe7705bf1804736b14ca32905bb2fc4d3877af8aa227dcb8bdbc709e0dfbc70bcf0fe82c96a10994d1c682e370a7a8098096d468806508a8f984bb623fd84f90876c98814b602411f1573ec8d6281607768696d19ef53b0db6c0013f324686a0d3812b34f99d9c12610e24bb766742055d487eb521a21341eeafb1fe60d84ee60d6d8c61c2019b4b24cf117a89df91a635020faadcfddf57fcc959a82ede000eebdd5a1c58b7899a601fdd13f18368efbd32fca36c9f388c10973c3cd414f8dc8a4216daca190929f99eeaa38e3afa15ac98868df0d4878201d4d4350f0042878215cb9cfba9ba69b46c099cafed60c9daf4c6db8ab4282f58a864b17e49cd67ff5071234491dd49881aea896fd34676d0211a2912b39761ef7403f8dd06f9e687bc4258c6ebcaf6ab1d32c73d78de6f5e5565d76b4a6d9d2154cca1e3632dc5d32e2a0bc664324e9ed1ede5b0817a76caf14087c428c70a0857ac8026482f472a7a35ac777e51db6493718fa79138243addf9a75f10aa13e794c1c5584d2dd426a781bbd6807c541a62a203246b3dd486c39b78c9a0a412d76e818c2772268778db9604467fce6628cb308a524d2f640ca652987b51e90bd37d761dc2f3ca760ca81955f93b6ff3c1fa69d79f7d14054ec0869998687ef6ecbebdf709e258c1e5939a0694055f92a3e2bc698496b624097c1af6ad4929b233ddfa1c55c2f7ab7d2435c56814b0b04c26b00a4bd36cd09c59511aba5a0c8f8115134f1fc0630411fe69d60d0a40e3580f7aaa4e00f5fce1c4d3207aa9346dc522e09de1d58007200d2e840009aea1c477580c3667a16d51563a87102a7ff0bac4779d696488f87089cc9c959fd39d0c71b3253fd164505f98908620cb37a820f1d67b1830d3c6697ec33fa008a1b0301e8f26aa7ebd5795f6d09476de74fae2c6ec262fd1a6394b67180b5c694264a5f68bbb0548d3cd8dd7cb8f53ca01da149939eed14262b167aada46352a035724829de0307656985e958ab1f81b80d3d2881b7d7d497e872b442b98d10039a44320b9433c337000cf11b5e7e51d8917b48082227b6e59d241d40d1d24ee21c9e948f738eb0730bae7c5d28d3c2b1bd6bb452ff267a6f65a193f081f77cdae30e1b34ceeedc6cd9135613e2b70469700bf50286d8e538667ba4120c8589f8b24388072ea54871d8d41b2cc553c53161970e5ea462c5084d025dc4509b7a342ce86791d9df233caabba84d0be7d019274c1be9382cbf47b477cbb48112f26bd669131d5111e3845e7f8b098b7eb8c3df0897c63bcc19fa6cf84770714706267e946f319b53090a14c21c7db0a52f7b2dfdccee138b3bd66509d449464cd5096487b4a5d6c92d6d15a7f8bc1a2980e89cbe8fe8d0c450f60829fa6af7fbf914c6b2e648863f73dfe56acb2b62e1714a3e8f93c23786afb8f2a32bccb58d16d94a4a2664cd0ef2cfbfcb8b8599bd223b5c21381c1a3b34505c325a6036b9c88d2ba96c45de77a81097c21c9b16399189efbfa1b270b296a4300d977e758cd18d0b6c275ad40510dfb282f446f0cc71e4af9c5d015fecdffe40a82441f4b6470c4ab3230103719220fe7702fcd82c136b8f81da6cc00dac78da241ec49bedaa086a37dcb29b351da91970483316dd54bdf9dfe326f1b4797d2d43838e2b68b470c1c0c2005ed6b3897c91811f106baccb68a96b0579d1020e69d6dccb84c1a8198650a99ce62986e8c4abc4f2d1492aef204a0680615ed243ecbba4671178ad80a4d406803008485af70049dbd6678e31528504a1cc83ab4bf0c15f10fef0195cdc65b0796eda95f4467e613631f6dee8242f39c42936311c83e94456bd00c1cb3a40d0863517ca1c503b27af2ceb5817027e0643bd0eb3ce9ccde0026a2abcfa8883acad64ac2490e035305132899e74e8e0fd356cb973258ec6a9d20969beb71cb12bbf9a8b45e46336aa1dff05349f519cbb004abf6041071543dacdfb52a2bff221f791d2f7fac1ab4f6dc9e800eccb79e6ee870944e4770281d654f1d916bf8045bc022931c3fbea188388df68030a7a03600979dd122217479da23680a365955d2a5dcc319d5a6155e94d3a7081943e60f5657449c8858d9522d6563713ae5b53608ffafefe15cd800480d005d1729901651ba316fc648ae187353f3b1674c8d0a16cf9b3a9da150b19d3d4e132572f0248609b026657825a4723eff097dbdc25061315a6db496a69ee832d716361fb55d267e7d1388367599ca4c6094c941acf3259072ed99267bd4c453236a9e68923796cd8b500b96788e1ffb8022bdda9d4d9377b200d6e192ae20f2688fae41c3af9e60173d1e25ac918a4a9af39c00395fb5363514bc4cdee2b03346a50fde35addc046b924e447fb03a97bc685561a2ed2ceaae49a84a97bffe8b5fda8369406b85d0e1c571d144cfb9027622c0cf31180e3400686210ec013300e22514c1deff2d6164b1655d2dab6141bf45a918f8d70017c3b8b23ec00bb545b050212d509f191aaf38175a2c28cc0116f51329c1a9e088c05f38f13d6f116368dfdf0807b62df3feed77c724244c1e6c999db4b4ea321172fbbd1408a2243b6e2d8c1a858475cda2b3c406cddd460a2e54a357c2130ea821e30a5a3022623342fc831ec75928ce929c3751b06ce70f83790bde707eb7c563754afd56ed373276d68e6663dba01147e5e10cf683b28cd5569bddb4a3daa31ab65d713d927acef74a482d9ae19a59ce143c9a930b153e0c4e7dbc01444037cbedfa538e7dca60f5d8c87df4b32fbe45ef3a68daaa932fb3094740181dba5e0e4cd6ef4adb10d93914df91d1bf3e9b0e8b49e572483703300518454c8cd5dad9f806e801e08c7fcc10e8878844d245fd49c0e2550cb5b8378faf6961c7488e12bad123b82062f5d813690eb736af4b84507c2c80d5b67f9a77fe0664d2f954c1aba3e4072cf7c3ef13a6212c429763c627ff69aaae934218d970b3e6117f3495483eae37749a683299e2b4e5188c753d4bdcbd9963d6049e066e5e93ded8578fcbc7f2eaaf0f22ddc9a91bf156f28d5cc6f4eeac00475f6027399975289adc0b98aab67dcfb30ca701ea6a0ceb5058f350760cffec6bde40130993e76dd4913e78c291c8c30fc2ec58cd989623aba26bc9f244f3435e210ffbf27e1b92f8c115d9f20d3fb402d42118646663ddac2d2b9b3c0021ca81992110b849931cd21b465e3d70089cb6365be296c971f4f1164031155c83d7c2109d27905af911b8da4293e8e69bd423488c1c7596544ac083834f7e916a4006497bde534634b30d8bbd07e51f43e951f5e0519fcc2bf37aaa94b10afa1f720d954493847b4fa172e5a3f5f1865ec9eb6cdb06e97b05db15bae3598fcc32d0423e110f65addab6a4d0f395b08a99993fa190d17910c74ff9134d58c014c19132a9692d8531442f75014c9eb89a6a7a0ebd31442c45753cc07f6b6fe4a93d8f374e51a98200da6d872d90b8488f06aef8a830eb2095662ab72650309d3680bd5861772ba2d9e725b6b7fab249a0fa84eb266b06a359522560ac2ebb0628ee354222fa19f12caa6693c99b40d1a1fc05d8e2d0c77ca8c05e23f99f857dd188fbdc240dd781b2c8cd3d04507d2d7d248e33b9ac6d9babfc01d3f70b90ee80ee10242d4e630d5716fc6f07a8d50765d2f85856162b4bfcd1fc83a8a4b5a77b1e3586b6f24130707cc5ce3a7a87fdb4ba8acbeebf5dbe8deee4b923e61cf585f893ebc301270487b905ad10ea7bbbf5fd87d5b16ea15431e87a4e93c3a3b20af2037f95ce45f08e33df681def54b788c6bc15260d260785c77d5b69b3ad2807f9b99375542291605e89d474c1357e0be673f39d5570b9717f8d8a3b35306af6164831b477180066a12b0d2cb71670cd286ad90d36b9bfdfe158a15c06bd161bd6e0000e35116821c370b8b2ebb637a834a755fa17afd114d9a49cc03461a7bb7cb002505e73279fea9009762a3b8ae204e634da38c5d5dee4081503a6e0d3b49b6fddf4132d31d0c20ce5e3c1179504c978e63799a07a8906aa7a9a12c6fd6ee2a75978c3e72382ed4fecdd0819b90ae33553d57529e697185b64a6caef50c5683647750bf503b10ca03596739f70d907839ee08b9a15ad600e7262e0c504b6acbc39c88f58aa688f9f6769fc5e195c5dc5cdb87bb5448250dc60b2c62e85b455616e17c1843bf0906422cafecb07a619a94f9b5141d675302356c71eb269f4704df3a6331eb8f3f70c84800650b2fad6b4d5e60bc090894ec953ed8a218b332c5c7cb48322fb84c9a0139f58082cc20f6a48bfe131c9b00210631a6dba13920042bf9ec059729500107b57e870dd69b0d433489400225ff989809c8fd59cd9b7c86b5bf52f3b32eb09db75bb6033dad56dc4a23021163e93c7ccd9a35248297e30a1e18328b0dffe207504292bede9be61402620b19b0594b020c6b957e27d2c09955c088e680c0d098279183130f3ffdd73877875e11537af950ea9a26057ab68c5902b085fbe866a4e138fd324104657493684db582fdc1aba0ba3880c74ad068b298775b1744dccc6181fa8b79a655fa805b1816f6da7998d45cd90d9013115b379442b5f3735a226ac0aad869c2f939bcbd3170095299dfca48c0001ba7701c1ac425a2864c1e117bbaf875c67f7f219f73f11349cf13b378ae61709b0d692953df4c96662095d756377f40fcebf253d3fcae27cb1bbd6e1747b55edd1a6df258b0fc8ec253167c1e253b1405ade813c9ed1e00899636686f392fc846ec7099e06ac263ff85b470bbb6567342f97433e1cde75364f7c015d5615a784bf2a1ed07488f04142b175d0fa385b6598aa234222b937f9c34d3ebe3c4ee0161b68753cae160b826cd8c2f197bf351cce0cc4d4a6636a319c268b401faae9fcc4072616dedc5781a384e13ef0be9194f0e87547b03f442086019cac1ea0b0a8d5a54cb33190f89c2e55b52d888b63c9fdebbcb0e432a588283e5e9e608acec7d7f4163d0690a68e59984b4b754a34ff874d83538a58783bc207ca1a1604f485340becb6d74687ea42e257d527058639ca8aee0ba65f6615622bbbcfaf972e2eff43a8fa39903d0ea67b9154bf335efe794eca4018c4e3dee7bb5db42933ae25d09ac49452035f9bd54c4d353fd649be8fff9a93400f51804c575f5f25252741698ab395b188b6752af642c7a9f7e681671fc000d60f0069ebc4afc60ba29d4f3a24efd3470f57bfa86cbdc8b49e981711e2d22ecb43c8aa99fe193ea4755f942448ad454b5fa399694a37cf4f348aa7eae18db56a32f54af7fbc8874a6fb7f9e9154b1ad8ea7ca6b270c4af953096d2933d170a178897101ada94b1a756591285948bb3b8487024bdde633d6c187fd1dc406193a552d492d13f23f278d4b05aadf11221bd64209e893ab28b8e3af402ff269dd9512b4161be558332c184748e83a52268a95992f879ab022e6d333e461424166284c1a8b92fdabf481b58b4663366cdb65102f326d4beb7dd4bf38019682053a8a0f918d201b7b9289266346efc5e016a2a619004573a8d0ff5cb6064a950d07343c2d2736b54fa763f2f3ce6354f676a6dd17454a3035b38eacec878a707cbb0b14908670011018e187a53bb4d4bc026bf4890b98615759b7759224d292678af8535095815ffc12c283dd042871156d7b64577f92f02368c23d8a43d42f4b8b090bfe0cdc293342d2e115fb289c7ee2a0c30b4b8fbe2457000899acdc378a4b79da3576be4577621ea52df34bfaa204de2b665436078e9e2f915fd7cb256978aa92db6c2224c87b07422bf48828875569615e441efe04bc4de11eb896061109ce785a8ffccf68399ba75759defb9a9808f3cc8d40409076191ca2c651a5e65fa8cb79fa8a8279c26693c06107f879fc8af4b86bfc6345f6af51cc39cb1aa70b0d5b8fd49adbbc51cf7c910c46ca4cfe124e45edac74b2b8b9e63e89f2afdc1e0c191c0b7d5302a730dc980b83929088043e0b1e788ffd1d03ff5223085472f35349b4145b3c1bf17c6e804c93291d01ae264658b60f97f34eb9523927ef04dea89f84f7715575cbe9a1f265a06cfc3dad70b896f58e94abec7afda5650d300985d1c016455b5693ff14b712558062b809e0df8e138acb36a18b07617cc4038894a759a0ce8e6f5cbcb71335001c70f43153711587970a80aac9e49eab077d849851a83b183e972ac0c364813b9e7b24f638efbe3d23dd977194f765cdf42736bbfb08be74ca7ae4f1e34a9b38f48ecc4272146bc1fc6aa81c7120265ff833110005ba7ebebfe134cedcc60d2451b7e7d0271bbd2088a1d9b690c6026d1632413c850949d7281acfee900cf6e998fd8e39490706c9592cc0979af72077e3a1f4f600fde528cd1fd5ffbebe0ea140e412fd87a2677c814d470a6a1341730cc729f50738797c45dd3f8d767a303f6a3866da195254e06e4dd232ffad3b4f358e1cfdd55be45d25a58deeb07a6cce503a5cdc7dbb53370efdd2b2573a92f1965a675b695618636156f26531588c6e64e27c2acd069d4fcb93592634c2ddb191cf60c41dea703cd9b6402b2a53b91325f27e56f8ee20ea17d05982cd9056de50eea4a22d05b57ccbeb85cb4fbf4110db4753fff240165a5f358b4be85e745ecad1dbb427b4e8b844d3c418d13b44ddca06b1f199970aad9fdbf7ffdfa2d79b8c3a338ae406b1ef8e99320f7b1030a37339bb767bfb56b01e948f99261ceb42befacad6ec15513afeab79595442f3fe8ed56e7e30ccf288fa2dd9e653a3bc7617164a35f214870756b8eb2d3434a9fcb6abc8d0b85c5afd982169d1127a18d0df713ad2d99d955ec6cfee76757699b398bc25260431d8f3cb300c136e3ea43dfa594b8c6bf43641631e04399cb69a0a41bc01d18338ac4d1cdd73179b2b022c7d4d2471b558a6098490ab8653d96faa4a1e875e1f4f42beb4da2b6fe0e8c5da7f22a1c86095583642e3948641a6aa86cb24b846cad4ddbdd8135e309168b87b21e73509263265d972ed340d5886523ff8f39937a4d2ae31950f5bb4fe9667ab245195f482c01a8885f367edc8f78a8c6a08a4e62549ef01f3357812a0e762cf1a9b2f80104bd9e6e25c3ca6b0640db81eb2663be210b0b5e3718f490be03d92a1f19ebed7923aecf2c776dcc1685167e94089f9a228bb100fd94367a977be51c5f3d0062880005ecc90291343a6975f480c34717a1e2d1da9793904a04611b2f45c25bf2fbedbcc41d9c37c750d39c134121dbe7b77f6a4827489656d87722e98ddea20616104cf1f540bc19f410ea079e327acf66ec4a1accfde08337fd3a200e3d6bd29d275ac8a745d0a16e9aa5f3a1963c3051298684fd60295247f58a39feb8e714656ed05ed08428a95ddd3ea74af465864384fc4e3ac78e62c63a1b4162f177c9270efd73e17aa7a50cde1ea25f5372fca73e1f2c0dcf371363ece88c719f1382b1e70c6294e900580470f7ad82309ca6d811946e8b85d3b1d1c03cc8a8a40af0507be88085660731292c3f51dcc3dafbb1d51a97bb847dde2faca3e3c63da96123da99dbadb32a7620d9ee9fec3e46c4aa10c15a8806f56972d62cd2f91920fb2ace20ea27f51245436a370fb72f2635819611a7779d73dbf4baf5ed68869e55222bb47b744ac6c4d9e34002c2320a6a2b88852a8474c969b8112678b0edff073ca65828784295093e72e44ebeedfd2f082e9d2e4557c6ec938815ee960d725fc6352a45f53eec3c6d57f55a4c12eaf52af4e23d18e7ec5159e5a36a2acb6cf1a507e848ee69d55959641390ceaa80ceef697546f230ee535f9e1860ad93b45335dac3495ff3f9ca8934c8542876bbb5122486ce73d0da679d1ef3b68e72f4d71c3fd4ed0d1304507a684da1b1087a62884508066994b4c8fe2948c139d69487c26a466b984b4fe2574da7041b6ca79cd3a21b5c8c75253f5963fc70c0d9c395ad3f8096105387487788964a64b0663ba1c2c18b4e6972e4fe5edb27d0086bfe2d5e9b934cb1ff7b45112750bb54647ad53fdb3ecc7174f73f90200982e72a568c82c06ad80d881f6f249b9739e1bc8797af28200c4445d82052d01753f7767a2fe5373e410e6203ef14809c614f70861b05d65a98159052eb74153c825d2eb029c40c9bfc404dece1c34768e4f7c5c60ea22ca170fccfc3c8aacd0bff1344fd98bbeec3e65e2fb59dd4304cb446e6d448a9c9b77622e03b7de3375fe0cd519cc081e5277afb36e86884a2315b10b33cff648d9ec85b1fba6f4a23b0cea80ff3322ddb08d71c0b50676f80ec6eb0c923e3b30db30e439ea261ddcf824f54c5101c09af326d638154d701d00a60527efffdeb2756ad0529866141707c0833b1c791f24ee93f9c0e7242a88f920bd18a29ff1cf582c10d7639d37fd2de4563e40bbb39bfd1a4483eca52ddaea71cd45be33b6319d362c6d99daa62807da434b84d1a2847519edb899903c4a102ba31a01a980671b8059897e6e81b01c5b46252b678d8edba0d376d1bfb0fb5b6cf3c362673a2d563a26f251da757cd04f2f04664b5b2d61866390d12bdba0a5e596dd1a8974e7729c326f17008bead2ea1a1d5b4db0c8a294c36c0e2309749ede1c53b0cd9133ff0cca8a3b73a5f280706cb648064477d51b467eb7f9f467af5914680c60aadd5dd4cbeeb1db51c5831b63518e9a848f07de18758624e0c43197fd084c1c3ddaaae38aa98a3fd7c6ce60e8300b24761d2d8216c54e2580c2f0ce4044a7d171e22127f96237f695d0086ec2382972bbb616bb42577f5c41cb643ccaef74b00dd0cee38062f0b1957c92f7b2db8193a3d7b94c028e431ee827ef98a464128ea3f7d4bbcfae00de165b70ea1ebfa9a55c6840ca2da80e7e5b352735e0e40ee580d9a8cc985ef238fc217557f059154504abe78fc82c00d811035aecacc1d0ea9f92ffb9f2aeb997a19473d3105705f7bb87f6c9885aa5c1b22c0b9745c91c9f4911f50189061c7143a8c84e355eef3515f3df1e431bac05ee15e022e6d50e29c6f4ffdf425221250f9a0f7f6f963f376c40b00e13616df96872bacf5702784193eff17444d76bc8b4cc08ee7fe7e3fabc5fcad10d5f83c21d7d6bffe707b9e68ff0441f0ede3782b5d6810ad3ae349730f514e6f2cc18f10be2f9d06d2ffec9c4680c1f4c743cecc94651b510adacbd13bea816c0fca4f4c38daead362af2a363bb5932f6d4309cf71e12b3825a793baf57057b9bb771dc4e82a3509d6efce576ef71821f5ef6bf8797f7e201621ce62f9f5b49d935c24c2fc6bf09deb6198a945069aa7198e530c7f87f08496a3be6a83205e9f1107ff44fd343e44bf0b726e9c2c70f1dabc20acbdfe5f5644f575425af2b0b79cb22049db900f326ed256e6ff093a37c0bd8f8e7f7cff9d6a4ae0748d65a341c8426ffff8d019d02e762b9c4299c352bd25a3ad578c7a809c66d076fbb045cf8fae91a775268f298eff73d1d73e911954e8ec67418314eb1156c1213a5b0ae061e369d643bb19791952d74b13dcc7e4e2f2d2a0f1e636ea02151bb88490ac16982291a0fc007f084200d5b0b36b9af75c2e1b9c86877ae0c9fd0ce9614a0f3e3de343e42f935aebf0522e84533374eddd8c61ad888ac20ec26dfc4dccdc7e91cd2471a67a8f932eb1528a71c819e1eec3c588afcc0197d887b950f75bf08d16743d99e062b505d0f88d6ed2ad1fb9aee5374d4b9944a8d2cc453c04d71ac311c1b936f3c1b8c4213b9734bb5a23b8d2ec7ee7f9f752498188c3596d8a33b74ecd6b1933fb8094c8ae6323170695eea9b8490db1c080f7600828103415b8234fddcf22a85b160b9dee4b90978a314e095f799276cb7125ceee508afeda9d81a7d38c288effde3ff9e17f86d1c68f09cb88afd37eb122165d11eaa6c61d386d3b077c2f78eba48c0e3969944082b3f52991054b2f8af9c855e9e306c46658f7ac16e723e6bfe0fd7eb6954b386fc68170106ee41c49332012bb7615fde84938dcf0af4d3e98a51e01a3631a0a5076c685bf78b760379ad918c92fdf80a27a30d9eecc8091b8de62f113680df5b3a085402e51818bc7d9146c04d3e790d0a5bed42e36581be69276ef9e3c5e6b3e0b2376b031d1b6441f8c58ceb99e4a4cdd5bd387fd04a6f112cff9daad0a5a06e067f598f1cf0c9d105f64e87b3508c65ad2d2bdfaa18b28a375cc06dbb7d2e844dacd7264f738933ffcc4f8ed13613e0b957087f02a0ef1a656be427264602302917474d4f117f355d64c6a1b74570c58475acb3e98450af420474e6dc6da38607b974187945b6576a1d96712a0586b3284d4b31b338ad4ba1b9411b97d17c489e7a8161a1cde68e0b0bcc5cb94f2a79f74fdc92dc9c8e97d04699b80dd05a507114ea26954975e3a835efcf8539ea36058451959266f165f9db4754601055dbd21972ab616248e020644be60a09c71d2dc321f3c4abdc807ea86b544c3566ee639d16173c00e055495a6b2dc415c14dc5b255d8f41583c14a4e30748443782e54ba13cbcfc150e8cded42c5c82d668d180b9256e71e79db5aba5170e9d810054cec8dba55ab35cab631387257f996964d7cc356f411bad5dd37b0e8651db9133802815a18b5d9e14f23be35d8b7ad594991d8ecbe9f76fb4294bd582f340feff94b19c1d34f90b3abf92807d86aa80b32e8293340c4c69a611a196c7562de66cacae3b759725b3fcade8cfa40a88de0cc7636aa69de7a7584bc3e85c81b022668317b9d4d5055a8698caf43b3548725effb2504dd9b454b4fe80f91c5ebf4129c9949f6ea9242bdb4c303a77b22fd38ec66885f77285b8ba60bd09eb91a2571118f476228ee2a2dd9618ac0b00f96d2dd1024a0d259a8c4430c5968f193d705a7e50af490e7484e6db187c09b61bc8d8869f33ca6e9933babe4c720db90b7dadf27fec5095061c0705b5ae4b00c4b1751e8dbcdc1f01070ab7902ae818e5046c5bc8a1736210380904401bc0fba82b46f9845635ec726cdd5068f02f5449af023d95c73c57c82737758363490e042e89431bc4e6270865454582dbdd4d78968b87cedfaedf4d5d2bfcfac1026f98a976e8b447f499b51f7cd7319b30eb326c5c355c3925da23e6490c9bd642ba9bfb4b60d965edd75398d2de1c16cc5e6b7812cb615c208b1a2c440b83e128833074b7528d063a1207edb6f77e7a9312710e1c7d73dc7e5875878d940072b8c59ccdca88f04cb14bc7048250c4991d519a7f56223fca912e6bc678e9c34d60c9681adbb5ec65d469725c3842331ce1fe32e57678271f93da8c56648184e39b068d30fa0d391c0b980b92aaf00d249364493c7998381ca6936c25c2b00320422cc01e210caf9801414601a4f338bfec0a2101e78080ca64299bf3c41e9de314d1a7811136b21a87e4f56d9f0c48ab43bb391a1696dc6fc3295912fc792bb0c09b033f0dea524e1415a99171389a10dbe67d44097bb85f7533c2e068b5abf44aaf5797647982d027f8c2c5ab8f03b07843177bd5c6854b94845ba6bd3494ec7c98df1735a5ad52d1199b278eef4daffef8f0781bc562c784f5f380898b801c2fc1b5379319adaa4eb7d72ffd6ccf77ef817a27d00821f0fc96ed008c52a06f685aee090bb0f2259a9d4982ac08a0d44d11568fc8aadc642e222388be7704490f56bdf05ee305c9f606fa1cf8fb87407f6d7b69d9e8db52ba449923eed30836406f1c31b68921f88aa957c6801de9afd4fa4167b79669a816e9d80792905d2031ab6edf949227f6152fea4eb42b0d6b1babaf8854f58215e246e592fbc6318a981662bb58b8bde1fcaf2baed7f66fbc9b0a2b1bfd86d3e01d90e1fd95f211dfe4c210b335279e6007ab61b2b7309a69a5f3f744da9ad2252cf58dec697e89014dc25e48ea128700089c1470048ce087f801877bf831e3a1418c3e7d119cadf0c9f63bbe4ab789962ce55ad91f9bd5a409a8f7f2a952a64c77b3fae6bc0cc0f77530e92a433ea12a8162d668aba0044675c4c03e792867770482f8cdeab00a4d58bbb1541b1e52ecb42dc715aa5fb148115dd77c3de9056de4d66394f5f41e69fada73d3905b72a810d79081135532e3f932e9b811a643fe54d9cafb237c46b348733c19332b6ac08d9e1951f8ad916c1cef237b3491fd9947449abaa5d15d7a1c559f6f3695b276d929d107b37584887a30b84e095c4edc9086c36a591dec03fd186377bdcc6f29575d1f4bb27c0819fa20722f43d22eac0f6ea18d7b1fb60deee0e75325fffc2e0f4412d2b06ca7f6e3cd185c500eabf479707e3e5824b19d99374feaeeede0d5925123d3c08f6f1ddb4e37940b85316c9c81557e9187eff6ad4fc2fe555b2174ce9f496ac8b16c9bae94b6fc917232132d16decf7745c4e1bf6991d590dea1d3e93aca58a9f8c95b7beca69f60970d3d11857ae510d938b6808beab49ca32f4ce40dafd562b66eb3b0c6fd72adeb524a0713b2bae42ca68a219941e30bc13819e167987e1218779f7c5eedfc747922100584ec2a1d05675e1610d29c1d4b8ebc736b5140adcee869c8c98f84baa7db2042aa046df97b6349851ecd61b5abc9210b344c650aa1ee2eb3bb8bf478cff69da77c804181463df5879d3ef1272e6a3f3a003190abce938226f822ec7de7f63b633051c7aa8c99552f4625856864bc3e846638c43762ed9f84ce4be10c167995e20943ff379b15c9ce162fffd009025370e36b6c7c68568b40ccf45122370c5388393a1f0fdfded462d3e1c8cc414140b72a901794236490fba880e0c937a1c6e77f1fb632609baacc9a87b8900981ab98eca16a10e5e2f5d6d078281bbb3f5047f5bc564490421253584567720e54efeada06df030fdff822d9fc14a3d5d81ba04a3df9bd36b37e96d7798e8b2343cde3073633c9cd431078ac26e018a7ca797c75441f247ba18d497088705e6d4696276b50bad25438a3ced0f9a7a4910ab12d5be7c1b6abb9d2c39709cd5c41c542c26da50a1505ba994b8ba01ba21f1ee64e8bc41b54d4817e015d6ae5a76ce902ecffb98211cb63b3b1011a11039fbb82b2c03bfbaa51dd8725caa818bd9d4d9cb6b4d19198785c3aedaa98d7780efa8e404424e44297f549e72d96f7839832e1a4402dc90a00d6457354781edb3aaa0517bcd0e0310c53d510ddc5816c30fa9f581a3ac9ede0a1403e2e693f4a7e08d7ca899449311d87821227598276804f2736788c03631af5bb0629ac1a070e4006109199419cf329da2a40cba7a7483051719bf1237e2ecc6afc5e20e73b55a7328897209ede00cbc79027e15abadda2269b805990276959cdf453fc230606db00ee5c9451d21ba83b2671243b8d2788a9c1238e495a9f6873634618b3c36796b86f8dd052faed41c10a2d064280b76051b7fcebd896fa072b4966eb9a1e28306f027c1627c861b906f5caec3d7dd2c93f5a44836fa164ab0d1b75d026e039af332e36635e61e35c5e8edc061e5a9a93e7d13abcf3fa5eaf330e4d31695b77c8ad2e225578d282fc5e07411d09f014d81c50f521a81842040e02665190699aedcb37d29b0a21fa8707325e6100f65b56ab72cea59b59ed1539bbb00c3696c9d48639f58228ed50f94241b19f3967396810e5f6177b4ba1d5fc8f7d2599dfea3c6a8fc265521e16dda40b1db3c1a0c827ed2e21481139ed459fa3c989096824f083cacf31b83bbfb3aeebad1e7a24334a790c79066dc62cb4e1db3cc134d4dc6f46a768b28a283de61ee7b6d162211e3a64fbab8e4b600de123525320a66a0f2fc5ff100397134d5a5a02b256156721d7ab98c33ec3b9331611ed3379c37c10fc9eec428e541746088806dc9df1b03d751d5c75756b3e7f6289a76f2f3332ac57c1a8ea36090db58845635516e1daad2ea545e30800878522303dd880294379e5d3797d49a313371aa1615794e906f6b05219e3f3a07261450355e13d3f22c64af29ce12bd86ddc25b17068cf83a7956ea538a80e0e162c2e6b0593750fec4e9d22ef02f33c10e009240a5d2820fd09dd743773208592f9c475252e036bf7d0965864b0b111553270cb8d9f13e4d9a641d8d37219a3b8f3a9ce623a8a02b80a0985e26f76426d58b022ff22c32b305d59702f91f96efebe304af43a093d26202a5912c1f42d94c6d25dc7d8e856a4eef965024de64aab6267059dfe186194c442e88bbc1383d53de3211a2eaf6450278db920b91de588090356364d96b2143eab48bbf0c78348f22d164b252222a4f113e17110ebb5c58c688cf0942a8488fc31db04aa23d28a002351beb0e2bdb56c34af427e6bbd725182ef8acd52262efe0caf0951a3185495858a3b066313e33341778dc5fddde8ff4e2c3f7b4aa34f64fb1d396d45ca34f5b8f2116e6e8528e0ccdceda7cad9c04a2194f22cf0e6ed1c310a69b45a73001c0cbced440a9c84fa524bfa18f58a24562128bf1280e5348724396662268ce896d383c854df9cee982bf7598cd3efb77dba68b4bfb9a7a1f866d8907ed07c9315d82c3719699fdee81d45c4bd1710e5a7f0fdbde2380772a910f3db141d45088d83b4797deae9eda20354e21b6100af7844efb79a857333d82a21b0a1bed48a2007438cd23685d56e06a193c4a3144cb431769002f6c2b63a31f3ba7d090ebedd6d73a55f2f2c7ff66fe71ca29b83ff4cba94641334c482b5cc894f92d9e693183c214c62177ab34517f5663744d4a240df3f103387ec837fe8f1ef261094a828333e66c7a51ca546034b4d17925b14b5da856509788cc79cd8891a600a6cba624d0ea7908a481a14063cce6f11ebc17d32d40514154d8a2a29c08bf191eb92c352bae5a529220cfc7accabdbdeb27fa06477d16acaf1b91a268c69a2c0e6c5036da692f81378c97a69115dd3c4783f3183532e17d9aacb52abd428a9bb60113b194169086f79863b08d160eeeda111c2acb9f87e3d27543c381e5157aa559aeb4913d0c4339246baf78413c8a4872c39fab1c206c34aaa5e5580b02066f53929cc0238592f606ffb41a1202ab931758b66e01f6ce1aeed113cf4ef8550f6b89a43e63b20b9ac5737dc4130ad9ce151b6bfcd64e4a609321f6b957ecabe76473cbab4e55924960df114224d43c1f473ac5e44e575f7aff175ae406ba754f3c1a7ef96716c8aeabde4fd22ef6b96997c25ff55ee3761c2bd2591eceab373fc2528f45a9de811b3074eef3ed3f51846e7c31bd81e47e509565b997a4371f9531703ca7d322a48190be678553c234ca8360bccc595ad7199d1456e1ecc0bad4f2b9b490debb5ce55575d9c0632ed608f272ef4cef72f26de3b2ea19abfbdc9463df2afe939bdb8450de0b4a36bb8e8c88cd33acdca0dd1f3514298c48d5185b222623f13d04a2232194817efffa3ba0d9ea346c2b373cf8a6d8f78f2ce25f47daeec655bf783d7bece00b57d25bad6381a7112296e66bbcea485a18d1b0fd80efb4b3772796c449fb020e433cdea93d61aef3e97e85bf2134338380e6280ee8ed2a17eef154d8f8815d9265722f23064121143466a06784d18041a55836cbcb3ff2945d12c85098f557de54f751edb7c156bd882605c4f1d696920f684593071370c925492d95eb9e12be8c04aef57d33804b73f3295a12bb94e6494d4a9c6ac471db0b0d2361eebe1b00c52c73799a3cc094b731ec7c1604bf891a378f64fbba521b00f392005adec691765ea78f44cb2db10ac6ec67fa056ddb8d9c75960f06b3ea69405f61fe4a59ff0f9974621922b048d0f89a4d3b0f8a33bbbe82a9445968be6513680edf4a3eaf6c2e005fd5ed6f558c22abbfe038e5fb50edd2d0b601af926c925ba564b730c7c184319c0e30aa190e832dd74f720491d33cf6c3395fd4335208b56e4113db4e5e0575c14c9ea749ad7e19931da27f558f0ef108eed232e12fbdbe292ab0d83bde5e12c269c00acaacbade83afbf7a84704bfa9ad8d4ca51ea29e78f660de22a15d061100cdc8abaef8fe41e9c6d1c74b8b748d72200c724d3693c9d4e1d88394adc9d0a14e42c9c37d859f5efcab5c5ce8f0f5bdd733c470dce15f7bb226045c04a3d3bc9584ba3262595cdc603ba4ac3d952fade45bdd8c43c97238514cca28849da073373620d74f8a6fdbe8a22e944e08555c5421b6c789d094d41811eb84447ebd170d6b5388afefc18cdc6ba0bc54d28e17473e5a5fd583a04cb1b265f8984d50288e064ad69c0e1f05e8b7bc95949696b942e861d80b3ddc8e2b28b825a4d6fedf1d2cbdef27656a0189447ffb657fb82cc606290645aabb7d19764195cfc99b611e616c72f8b0172390e0772957c50f3939ddb9a523f630c501a435a2409e52616dd6b8fdae06d41bd433f3e3359b3240154f3a970d46040599ba8c5ba1ed4fef2347855a3eb570c8b8ee1acc047ebc179586213e04ea4c1895acee6c6402464d0d90b1dc9138608f4ce37d7bad73ad7ae94c49267e2c06bb05f596d683cb1ce91fbf976bcd1355270262ea5ca7debbbf20be235fd371035d49a2007889aede2b47e62af2ce44bb814e33f1992ea82325ff317aee521fa9c38ca3315c76f7bad4f46d5676f670dc25ee109151747191af6b4ed62a25daaae7c089d425066e4a917d73bdb03751367c57675b2df46cb3da11fbf68f311d16cbf87790c0859b1b946d8036c18b40806f5d3264f6e510b57b362bea0c12051c881dc5ce32eed73410c4afb8281d470c0be33de351cddaf3b9d8a85064c11d40fa60fdc63741b4cdec1c8cc11590e341a9e571efd4f889900baed207deceb9f50415c7bb6927dca46f450adbca0bfb5dd532288059dec1ba606efee9341b5ed0009d9a4c69fd68d33ce35f42a10a97c28485290b34444341dfbcb6c784643ebefb2748118245eeb45d6707cb467528008e83c9aa5e4db5beb8e3dcdc4ef780c3ff62b7e93dc2fd7b5dd74e98b527ceca58a99e4a45b41ea7f04346b23367d846d870b6495a4b0e92900497a598a83425d42c1c33e0e14be3aaf88312ef44e04040c59e8319464a357cab4f4c4cffd134c993bd50cbebac1de7f79325380ed86314002806afe8779ffd33f4d079a956e3c2ef5a1b2eaf6c9254b1732712d7c8f706738b3c3e8974f85d1f32f6295d7c137d5bef853f8866b8d91e457b4005e2340ef25b0b5ddd91f7745c1a5099f105746b69e44edebab5edf17151a59720be6c693e1a2e11def734798e8993602ba79c8ba221187bc27737bbf7e691ddd28192a4536a56e50f8c231e780f712f48917e3cc11b1b8e55ea8bc34292637a860b8d7810463212f58eeb6cfe443f3f3a508905403bd606898db3c3b070ab9bd040ce5a47421a5692646068c7880ed561a294c3f7b258e79054d9d49b3350b7995d90a9188853c4dfe04e81dafe66d40f5c8bc9f52dabb604ed7cbe0855d9ba0d539d9acd0bffbe195623e2bd416e2892ee875eab1f7853d133037f28661d77c883ba2611ded25fcf7e712e4bd8db651a37c87b8cd45059b12a4339c8d355dec71ef2bf742f7e4a13e658aeff3da3e1548063e13eba6b2294c44188bd6e7bcf79556f31b2b81b94083e271cf587965b8f51d3a48b6609e1da4a892d2826e4987c3c0300305f5223dcc1b5b1723669e9c82711060a8c5405ce6f7e3a89e78feeb1bc4ed18b79aaf2ca77fb9c203e9185ddd7e4c1fe127789e28704778f4c004e6e5ce514d6953ec041ce200acc7f1f17d489458566942d6b89102ee86a90365c82a1d811f84384425be5e0582da39a4ced0769ae373a4ac2f78d84b06f9f4e074eaa8bb94ad62f24008e78a0ca9962588c514082c448bcbd3d0cfd5b72c00fc47e31d1032636c41144b74d59b03db504c1aec308b66034b20d05512a4873e0478486ab32638327969b8c3d833ba50105f4ab11fb5dd2df626d6a6db40cb0b13f0aef5496918471e9ae689243a64162c394afb8832321b80ee87dee275cae845f2839b48c127d7acc0ba4787c528d0956b9bf51bf0cc445ee20fdb589b47c596380f9bfe46b1d9b09bd2f17c3f25ebb0fda6a1801a74d7dc2c70057d4bb2af7afe1e19521e9a296fd95cfd663d0cd06f5bd314d4a77d66259387ef1daac75f0aa7fcffc5c629638bb6ac1f814d113be66e88e4c18fd4005eee6f99c43a861b492799a7ff7ba6e0c800f334042e5db97b40afd1f2176291c7560a1368321961e32b01bd623a06d1af6ec2ddc45845abd6750d62e9fdb3d56390930760352eeb78d7b337781339026fe732aad8c40c9c6150b1e923e1c2afc3184cd76fe88fd1fdaa3b83d49dbe491948f8bfd99c8cbe0ff5103ed4cfc71feaa978adee5f688b9881b930da5ae55fd122efe49d6c675247ba97b9ded5ea3363cd5fd582203969decfafb5582def59bd5bbdb26bb759cbaa8d597fd95434881aedf42668c0e3d5303bfa5ad4c86c37763a40c804a85b6c06537376c2b89b0139593829fe0de6333d73829e44d136456c3e46101a4a7892deeb94e9d12fa4a11371081ae8639aa0ce73256e309c7ddc8510591bbe5c67e46b674d45c91bcefc7d6f487785a7bd1e9e5f91d34c189bd913b16c5c3367e8997082b2dc34079f09db7dca7f2e1a1e7c026eecfe1290afa5f76a6d724f7ec0dbcdddee021960ec92f0f1c28ec386a22bd40cb9f0f794f2fce3b2904bcf82a5085c0fd2dce8af68c4517dfff85e183f0a836282ac6d2226fd5d9f65e9a401c478b182d307e07361072463bf030e207e9d66ea40ab3791e94a028daa33a64c412c8a30b395d39dafd475816a36f6d678c181a2fa677682d4e2c4b7e108c119d914408b1b8182d25adecc5f58a3a95377bee2661fad490941694d97a1db37635ad5db4de2ff8cf146a541672b895ea9a55c3db24e05035300f0f1141844d095e0330e3a6daaa312d86efc6b43a73b2d79c4a082195052325f075081e4194e069fbf90c02423fb3f872256dcacc68e85293e1e2929f934d815afa56be060961c485c7805c737140e6a3b0ce7d5b7befa3d91674a237dc08879f508ef175b4691d0531a8e4b41bdaea9f7cc4233b333759ac2bf8f7b5d555e4091214e21cc92a0e48edff4271eb42683d656773f4c55eda7a4a15851865493a1ff0ec1c8b41c08615661ca31410ebcf7fdd006ac270bcbcf8540b6a0dc0755976efe0539dee42cf3618411c1925dcfa4e1bef459d41d312b20ae6acc1ca4241a2e87045e0e3f086aed35c1314267d6c1e917b1e63700f41e3257861a5e42ae479cff385b55fe777a99b12e38ad870976a5b3160e9ca908b15d0a27f4ae20517d3860b5d6693ac25bca5131375c005fdefac5fed26684181a36e26f582a56021b52bb78b02619ddc48bb337af8840496380b25655de64b20d811d04ef510128ebecb4aa6d83f6cfecb9d7fa7cc720cc41f516ea2388ecd79000e00bdccd015964b5df11c5cf7c23a07953a3e8f8673e62190c392342b0c86ee780e2ebb7fcc28d3876d9976207bf91de0f86c05315899597b98adb9fd49eb25d1df212bce5b9e6ae6ec82e948979eb039982bfa19231fa10b67ffa352eee4449eebc30da6f42aa11c883dd37768978d3956ee0d8d07e8c9df8977446d626fffc03de8a338e8af76a75807dd769c5d51328a3df6a853ef6419315cc8871534143332b84de8a660890a30323c65f1ff6d2503804d3eb7c01ee42d23e3cc13f50c0bc4f4d40e6220913c57075ab0389b743f60131a5682c592407cd915bd51bc2371352bf466b2518df26a783da2dbe3c27437b1d564482b3306b4464b868de0a3608b0bf401ac35536160743a185e08f4445317f817529696d95e131c2055cdaa7b6141b6bc77ebd2fcfdf74c2981e4c1cb22762ff7a5b6ff82472d9664e38cb011dc5ab9fe64b6da096396796f201a357311fec13f3831f8903ca21abefa8519910ab7d95bd7402811faf12f93650e849eb331e73d24098bbd26f6523f46a7a1dde41d72bf8d70dbd717ce6af0dd22b0b5d6d32b2e9c314684db62504a315cc02e2901693b44ffc62352939ae804e1721d84efa0ea4781713c82f7b17e7c095e79e8d40541ed8698d4af4271bbeb9e976294b74930d93603e1644ac158709a31fd9c57577d500c8931f8702e90016f55f03cb658a1e6165e4c6fd56f7701e487eb6db89aeabe32369119cd417d20f595f11eb519bb8787df5e17f9ac35d87f1971c7e5ba7978a3c44ab7fe6fc30a5d76c3f848906b7cebcb81628b60d61b0cd8daa7dd62e6e9d359a6d8110ff2c3f302a802c7b93448390192f948f7fb708c15e7f0d2afa6190438b2a40250e51169f9bd64c27faf2097325384192039e39977eb6d52f321273b4a0c04a2d5c0de20b295c5266c75767a76f9e16901319b9aca99fec6ebf7635e06f53f4d46d4eed6e9a2d783deb29c7bec98f8a7d9e77a08bc4fcd54c9f21fde92f3830d69a1c59a0159dc4b6a874341067f2f46b8423f26d23ca18fa209031435ffe5d64352c03f4b1acbc059da22eafd525ee5a1628f56da99a9c83a81aa22f84a865592961c9c38691fb9f112dbd9b354fbd478863010ee74c035db5de0b55800503a99f4d8ac8648f80c03196475537779c7314cf0cc806f40c61e877c6497f9f493b46ca7379ec55c99e89636a3e7265260b0b77395ee04ccdef5d8768b995d84bf492d84bf492d84bf492d848f492d848acb779a210f64dac53c83b956795eb2627c5f7b926e81b750a0aa59f687f424cf5317cd09c1c15dfc7bf6302acb7e7f9400736c78d75d0dc875c2d28b65deff7e1cf7e2e60b8716e719073e7ee6307bad9fe0235c9a58e4f45eff935a07463f98bfa15929ffa97c50c90867f9841307927e911e3941a5ef4af36b34ce06b3a429932202652cfadfef370832ca7b5d62255b25051032dc8c07523664c000a3a43cad7906a61e4dab3047018146a3fddd07f3ade063e94b1654518eb7fdf8f758435a5610cb29a4df61973077d3eb2801a19fc35042d1666565239f18c4e195d73680ff018974ba8ace4fa395d134c7f9cb747b7544773f60213b3f7e3d152c470986840a5eb6dec37e60a244d4014729984c2491abdaf4e509c0a590112d3a69bca963464f1a9efb5eb393fc237396c71f996b569f36b4e5a485a8047809d95a8d93355dcd9b43f7b4ccf6f342d1759d448c7a839566aafc9849f5c8b80abb99271511686e068713fdc213b424d4c5680bdc0089ebd3d298e4933b2d6b92ed7bf6663e3078291c2accf64700a7f3f1e3d2bdc4ab023601694158ef7eb5b63f785388b7b3f542edf819e8a50f9e0ef555c02fc0cab3d01ab4ed0e70eab052935a9b60be4040b0435595a6a9697b99b0057c2e5e0c11da07a856fbe234b2b6c11485f2395440b28a3690caa76309938c9c2eb033915d7bfeca99018705617a87986fd526d4da02157d6f54b140ed9332822206fc6832e5cfa4d0bb2548d311da595a2fb124b1e80ff13a037679d910cb3270a038205d4634f9ec8878601dd081c248a74aa2df7a33145ab280480040b653fa4f48814541487848985eed16aca7b1c61c2f708e0490b51db5f09604f444660d999fa6bf2c0845eeb017cde83eb3663bec8d40debfab26b52f5fb6ca73876b22b8dc5bdbe33bf6507c6d24d1c232150b65da69766666d5b74d38778e6db8ecf34e7b7a492512c0d4fb27af182a31d30ef65419c7108da66bd42b7ee0b000cb4e2b234e4ba9e66d1be861c72e80cd9719153652b6f5614c7186603b3d61a65b6929d94ac62b036d003824d0fed1274e4e474d8719a01859d574f4887c8f7600270564ca427d348ac5bfdbf1b937fa1a7216e6f16e1dd841db708c9c58f2d98ef77955ca41bf2eb09af6a7ae0a718730b9dfd9bc2d03937820d7b1f6ad20fe21f008e5ed9956246aca42d78383ee067cbdd8a6070284a7d59fb9b22add40f231b61fa9152dc194d6c2ba679034744b7abcce04da180a1caf806c049fa0c91dea5290e9b2ac0254bd321835f9b9c1db77302359df4d07274b9528f49527c56ec42e9d8474a7d20be53d0cae02c7b3287a159043e4554287201bdf5439ba0252ed0cdc7fe3ecc965a6d88825b452887529315cd1cbc9e246d752b655e8877d4b90e2a1f4450f8859d03852e10d8a5a34ac55bf4b365b155af279610488665df3f8486c9d79a74cefcd7dd45105cb045dcb7f5b06e3caeb06761610a548849da953c6ff47a52001234d6c04c170a1b5ff962b06713aeb68c26d0cf0c275e3d803d35e495d2b48c34d5b802cebc3859686480e824ef4398f490998843312f96ccd858078cae8e3e859acc013e3941172fe615761c4fb26b32a34dc78da5f13d2146f5a4e073e588f7732e21f0820aa05358816a82b100fb489182b3bb450e0b2390eb8c2fe9cccf8992b1fd89a5098ad5961f11f16b5298f81bac6e0bf2719dedac559f0ff9668b225b521a58f40a934c853912bff562671b9cb70d6d4fd0bf1077616662cea21760ede8168b2019ab77b1b6d123cdcb4217db9a0193b7a08e84a190c572423217976515e015eecc9fa2bc358d9e3954480aab0a750c9749c51374cd042d66a6c6360b9dd698204781a820293b80bd95306441669429c4c976333b581b7c8a00d836cf54d1299fe225c6cc4a5c349ef2d4196aac44f2a319f2cd0494b6b025596c53e28ba0de365dd7448a5efd290b7045d8007e4f865ace65edd791235f68372fe5f203988cba25671109b9d5fbcfcab853d420b80a692e176856d63ed69b596c2aa0ded7f60c60cc6fb5e2ed2f8a5ee8962f8014004640555a2710f7d2318c35f392590bea7972065e0674371ac81822a95e7d224caf90cf80712ccffcceb0b5b7206833cb452b2c118d533e5ee797cd8e1d1440c62b133e55df344c1e24daadce881d3735934f35c0a99a050aa5199c05b991cf2c5ac37511848eb804d085f4623ac256dbe3c977d5391404d61d99ad07f41c31e3fbbac1452e4da2e5904971e5bc590754af7e8797d2263749d4245c803660a9725bf201a657e184e0325e24d787af6d933ba16e917174bc1312b07df86d5acf037d3f4e918169696f958f6ca0abb49dad5c2b95a628e3fbcf99235b4b06470ed890fe8b9be5051214775580b0cee6a5757ed9169a9aa72ab51c6885355a267df8d682bec822a9cad1eca6405e313590469d4fb0efdcd710c2e2bd6e13906c7b4c60b019f9191175d6e0c41be7e509b45db0c325bcfa02b38405b8c6659df26e7ceeef533a6c20f56a0dd3035545c4a754b371072ba1124f3030dbd5e169748dd6e5e6afb846edd73c315108a3575d3b79235ccc0dc32fd4527a39278d240b653eca55e6e4a281a7bddec8fbb0a591b595b5300c097d8b73b9b8781ba602f79dd182eacded257f46f3c526c85c071ff82908d6b886b7fa33d5f5b558c8d46971753df8bd67204cf6069adee61045cd2dcb37a0518b2abfbc26e0c2e6d1c7fa7bd27d1a21c4e1bc0aa9d887d7cf5b0ad93b07da229baab0892b879b5ceacbec0f1509480a68193edf79e67410d675bd5321b0e2d50896ad342246ccf37946dabb08398dfa6ff3e0343295e02d11a57252fde13bd7de745a4bd042cbd8ecc1b9195b7ae69a1533b077ff168353289b8aafca1232d0a336791b6b5912aeff631f882bf12ada3d35847e710ca88282748a74b9bde6048381fdef95d6f44a2444c6bfd1f04bf3d8790b4982df8d21bd0d0c30939741d4ec9d65f30ef0d9260a3018b33e1076819ecadac4baed20cedc687337d1ad3538dd7bb23f9fa15d31f24f633c7ddd52787e200bb8a995395ff0f2cfa18e698b9573c79d15a0204fdc458098191827d9511f8f25cc3d708e6d0647dd56ea6b4925316999eb14f0d1e6eb8a3d6c4d307dd0b9a34b5fd80a76dad83f9ac1d9808554a7524da645f516653f6e1c366e281a3945db89e839ad3e70a2f21f6928674c54eae507dc3560296cd1782da0135f615320d5d3a411295df7f7bf95e2fe2d8f55ead3c30e0fd4b9b9e4c60e08c41eac92d11a9a08457d79bd9863b8bbdfd034139615da54908198d3e10f98260af71137aaaef18bfdefd516d92142d82310bc66c4214fd6adbc571814ab266e1b699d50f0d141d59554833b3f872e82bb43aabb62c628f38f8fdb114809eaa617b1b786952e11f172590539d65e0124000d7e7290d3c6188003c3bce0567f98038181d77cc0121639fc4a3fada0a526f0913f47880454819d288a803297120118148a6334981340291cc12d9400a5a0e2dec3ff3dd25cc2c58ec23394c665396a0bbbc215f36903618b2102cd8abd8a61d5cd4b9ee6c96dee3570602afe76d4540536d41394cb76f32e311c82cebdf2f6e95d5defacc1c48af11d3e8e8367733ca3c3c944ed5816194955422a5d039e17cb9a2e621f008041d19e977a96a2e945e98d34c06d96674b084a95a3b1697e19891145cc707a503ebb07b0604b8e53716e273e8526741b5312b62fd1f712d180b0b21328b281ec4a2b08b299ad6244f076a9d1e7a108d02b2b1948fdc0442a859f726c24da3dd460bb6c8d3277223391a0f86a6dfa4afa10630a557ebbb19cf6174c3e45c9a213d2167d067d22fd7b76f6218293235cdde9c3c1255cc1396dad77c70900e0f6dbaa90a327a8eef3503a32a2302b527b37d68fe614ed12f0d0ee681889bdcc1217169f45d8e5266371a701fc9e9f0fb0f84d0f93e5b8deb29122f265945ce3e4e801bfd51a4b1e3aa8cf19d24608f8d09ba87aab74fd59b85a275ee75ca9424c174a9ecd8b9fa3062edbca534a3c7dfb2508a7c39c0ab10cc5f2614fa5ca3805dff6b4c766db20a404a8e9405185d02011f9a501459745fec4c96151c6231f5652c352a0a337439506d0f0bace90610125484a2fcc1290997b9c147195696d75f97f23043cb6059f3b36c7b3e752ff1f1dea1a4d19e3e3d5da0f23679080d521e28fc1639958f5bec45570c1baaeedd680719eea38569428d650022c85e57c6c4b4786790e43bbbe97548d7b6c599c3a71f126b453cded3aa82134294f135a00c4a99de61fb64421df643af9051b2c334b5f28c0346473910edad57cd616c20009cb503402641757d6c3ff986d543627d28aebac39765be80f7e362112666d1467950fe39c9e63625625f89e1a041e64d14bc3e1200b301bb63195b824586479246eee5b59771cef697b6647eb7be67530d9c8aea2843d631e393c6e613b0b385fa47a2f2beaf42cc706d94c4a626ad2608727c502c613ab9605f78bb4dbdd89964d709d737c45283049ee05a32b16d3214fa4825796c10a337d82525195ee339e0d1de2441d0e487406825b1b68172eaf181a6ed5afb013dac04c94721151d6757c4b3e2695fdea8e0edd6d17111963659702429c8212bc9400195e4ab0a68184b3ee24702992dd23a8961f61330a76e74e507636386bcd7d1c64966d7eb14d87d696c0a51ed0b5171942acabea58a44c0ea3b77080c7149a935e85ded3603d3797c41c7337d924a0136525ee74e52806effdced88487d08ab833699e57f2e93d732f004bd2795837cb0aced3f83b7323f04a7911e597e3df9cfb8b5976782cd08a929d0aa5da7e904680d7a603a7ac81131082ab618346fc36899485edf1b2c56f220b1974f0f951d030c42dc7e4667f53c09adb87998b716ec11097a564c29c19213bce4dd6b52dbd13ade55c92e90cce1b82167b0cd457aa10e42b24e8c1d2f27eb8239853aab35f7dfc1afaf15eed69b8e6ecc8afacd2c269833448409e31c9675d8e853f1212a777049c0f604dfe123ea262e99f307093e2440359bd7bdb01eca668ef59a15936db434837dbfff486dc8898c64d42e9e433f5b4714d71dc3f12fd1bee4a383af7ebec36b1e1a9795dbcbacf4da51055a3a0cc56429f0702431757b53a3c7dd9f743221d3b40cc10db15b09c56bd091c0bf1dbc7892074577e677ca306e807d643311287a37add243dc55e72579f19a89068466c9cea14f4450b61a5de8c9cd534c43adba4c1f5ad461999269e7921b65b9245fdea9746e6e24eec7dfa61d0454850b5097bcacb607878fa29c90d137a788adf254a5ca8ce08e41e09efd339c36a96e6f2d29ddcc5f0af6003e27288370667b8946db1bca8e2875a88cfb52c32b6f132dc4499d42f90aa305b46f7f9844f3dfa9e938a904970076170e49f33c3aa53200dac6f5e33f1d0423e12a564e8b0361a70eaf1918b28286878209c1d460b16a03e12ada1a469b42bb28f3f9c8a98d3bcdbed60b0c92df40fa6689df77567368713bb59412dfd80355007a05607281c868119e132b15eb80c3420b4721d805aa7d055b17c19cee3191483f8b45492b0c9d84ed0da4bd332a6a651fefec40913bf7a027c69837943e9239c73ba60f2f320927651d1d7adc747b5f504031492fa6fb1b048c5f09b69254039a552b4a9200780421066a07131f744d31be84d49b61536efa39e8dc50555d22d45e374ecbc079694a25416246fb7c8847c8b472b1ca664f1e81e8064a4c4be1f9083d5175eab7080c3b6c0c47068a5b5bde56c867fe1950cfecfa0051c96ae432ec54e8493b3652575f6ee583381a2d61c99e923909ce1ab09654704d2857f6fba897082c04c73715b5cb963b111077da2426882c4382e1b938a34048ceb34ede7f186218405fa51614cbd51aca50766ca9c77a70b4c684b10bb4122a94c3dbbb2b8c2c07da889796185adcbed25d3d7aa1a562b78abaa1638396560cbdf0494a096954208a0d82d2f6ce7c0b3c8fbe03258e424ab8c7f7af882b4401118902654323b77696549feab12d5a7521d02318056c4005733f4d6fb654e80283e7f7cd5deaccd8170492cde9ae1591d24bc1df940bc84efedb1c9efe43343ed45ac7e82c7781fb1d4dd4d32fb744c2aa2400d240f77eb7c90adf48f53a8f0921ed129192dcffbc82300b6b89ecf281975b4ef728a1fe147eb2d61b88bddbe2b2dbcab5f2436282535507c512153ae7732c9819cdbde61932d6f4bdb2d999cb351beeb83ec99e3bef2c1670a478960a604011e9c805ef8890777741755a3c119e9427b1233ab1b3148caecb9e93bf4ac807739b46e6677f98ca82e986b2a8b742268cc8e3dac739c1f0e5dcc8c13b6d6c4ad1c53b9a9926a9069d9e5d4182c5206c96ca07fad86781daf7dad8a3eee02232d149dc32b6f179895748288f049917816571df3dade30295abd77fb57514859ccb051a4486853b0b498967875d9c02de950043b441f960a6c4d3b7a294afb19df2ba21745f04f68605fe63fa69705f753af0f4caf0b4c294420052887a3070b41a50df70b789ab8b1cbff182796deccbf1cd02fa758207a0ee9c8eb87f003821eff9321a34c7b69dec7412580194a2c3bcf32249bf4d6b20542934465707af7726bb12684a9ac8d703b1166aaa8c1e807be8eff872e423e164fbb5934eeb8d788fd831e1cd89bb512dcc7e983bbcac49e4353b0490f6417d4aa60f9037cfb4ec8e0c05100486fc6f5eb9ecc38e0cd9befb77761ede17a1866274fc9991823faf89895694b25387a186a5819dfffcd6932faa847d6639d215303cced0c70c0177024360492ade5cd76ddaa36a2e26be95e632a96f4d02b5d49b181b3282907c9b81b438e4d6f19d10e113943e93674741946fce781fb3ae8388ad8065e7d0f424479c10ba31c5f306e18c74187094006e9bf9bd78d33dc95a145aaa38717cfe1dc8fa70ff56316100f12fbae89a25569fbc58d5afa79a4f1b7360d27480fcee2af6a36814f4b3fc2d46fa562034993b48046776120ccff62727b7cfda92db4cc6d938131638d14ee6ba0cdd478df1bdae619aaf0c1522bc3aa8aebd9ec11000fbe4199a609eeb54de8a1f47f8cde409c67d0fb50f5bb1144bee7650835f1325614696fa9a5cb474407b50cac8376fa8303a75ea4d7be4e79b44b84a41cbb648ae013d8328ebe0365a5e8f139f2854e50b6cbccf6bbdabd85d12edc6fcf27a5cfb4082918fcfbca418a2e3b4026c52b59443b280491493f13dbab8976b90a946a4168f6a80a845f4a22f4f2f6d5c1dc678438aa9b713d086e873d9ddba1dd4c5c4624d9b32d9ae94a85c0e315b5c02667a6494267c23b6ca30ad4e45d5738fa3776f0ea20f3a6b73646ecb7f0e7fe99e6d413971dd452fac1f7201a1a0fb0663ed23bf9bdd522e50cdbb2b2a403a9d53a74f48edb74d9e5e15d6a90c6f23c423ae31181089dc50bb8aaa23c1023b6ca288457d7bad385419b8aef10788b082de539e6ee19824c0718afd402f3264d882581412daf8d8d97f202463b57ce740331955895c200b3d48c08aa4d218e7911487fb9d59a15d4582c0982fe2239e0ebd3bc847f1cdb9b0a1e79b48395d65073ed7721a23688fd95047799fc9f25cd35092a279f75a7f0aab6ab7a2ec14a080081f502ad769f09e39fd132da16c859d826b9a5a63593467ee11a8c31bc74de0c42241dd0b4920192b8ae77b4ef40e966764010d2e5b1a5517218275111b68a25bcb8ca41aa56f7556c6b1335e20b733532280af281f6b40d1ae108241622ebb1ab5c737ecc1c106651cc932497a63277419917a214b991428a7610e0d504238a925404808688218e7c573a9797084063575cb050135947c4411a24d80510ae25b50db530d611c903d95db03511d47fe5fdf08629cfc0cabb8ffe23e58f6a1d3e5e81523752dda6d72b7c5c7a512c316e7f2e9b107c12e3e8dc29f71ad296013a2bc727c6619f80b0fe56aea2f093cf402882baa329eb31f083fa58596191c51cbde4eb715258b4f6087bf31c24b1e4a1c8733ef6d18d85812fe8a994e819a0f2654eb896ec511c107a86ec45bfc27be13ed61485b3040d4eeeeeb7ce647e7366f40cf3a0f5648e3c9aa9a72cf66162dd736a5a3981686b340bfdb5b5587a3fa3b7eaa892bc6070c106eb6de638a88c92cedad0a73da0c5e2452699791c168b2eba0dd872c5625ae780c43f01a2ff856467d0be8d80cc2018c3be1d9501c6eaf0b6f48c584287c75d96cebcd0e1268c41c5eede4db9190f97ee9566258e79127bee5f31d00367fe37c17d86f701e46a79289cd9f4033b47b9a66599a523761421f38b537b9bcbe48d4fb3d789fa4cff092927b2b1aa3ac0257d10992c2622c2047362e9da7ce9068192cb3adb474e0ba33cf488ef4cdcee317aa5dfe35450c49fbad999b4a84ed93968240b1025040a2a6d0917889d48b52adb06efa01ad69fd05093b97812c7084abb40d4b8afa2c65d94c3d4bc5cc58bedd9d9e4a5e66643c918289ef40b5200fa37da9c860e39a9b97643e4c34486b620783707a165e6355b76c5a4fe047cbaa99ae6ef93d055cddc6072857bea723efc5f1de787f20286397976288eaae37eb5860ec76b34f573de1c2c956a9cc8a420c76abb149fc78e5c39aac83227b4214ebcbb12c2dfe022e3f0a526a5a4224a41e5a47b8288af6104aa0a4f58dd9de8607c34e651d544f7acfaf24f5054e63fc1b902424b2884960b2b9bd81ab5782b2324f658e306a807ad4a06f930cefe235ba5c9e1b8d1d7731c6194d1c608e0fa3e5cb87bd311421a2492498178fc52a55e644f1746c184a905f46c38d816522731a9f138a0128ae928ba76e5c2417826d7979903635cc03c0e82b6e9f58c782d6e08c4b95ac2ffe88f4eb60623cc18f44ceceb38ac4baae78a608c8ef3bfa9937f37dd51e53d3a8317c4415699316162b3c212fccf3839925d53189dfe989e4439981a3dba8fd632aa69f2f85212a8dfe68dc7394bda1a8f38104a6d344df6e686d6bc9014c32e28556f1b55ddace83f2710bc1354b39dcd39973197d662a64dce8f9b36967083b648b6a6a11dd68f253f1f59202cca7d825d68c6364699653628500966a961ef2ac372d3c350b6600d20c517e86aa6674be6f621a0f60d91daa6d056e29649a35bf7127fb159fe4919dbe7c0177f5d40672188e49840bd2e9c4921ea2c0b021c74d3faf8224708060c99a0402f871e47ab52650630abf92dd5f47d0bf0a958b29ba96d31383d5527d92a976ceaec04183ae100fde0e4ee41fcd612ba7120387f3f1a1d869d5f020fed8590eb7fd8b53dff1996539b212405062d703db6cebf85ebc1930125cd8be87bd56fce4651ba44fd474e4090d6e788bf77eeb9754486c91ee18883b1a9f16c29a1e421351ea36f3059d1793084babece78d9bf60e44c27081a9339b506eb5c7d7bb9a8c761aea4d6f2608fd41ba923f3ac53415afbcdce8cd3d7f6d740428c6ed19e914873de8df8ac6ae75849eb985f7e80ae140647685362a71360d28aa54b517ecb225df77cd0b1f4cb6b9e954dda67fbc8c3d5803caef9c176ed8208aa5570f0d7b8c008ac7a8575984ac744e1046773c382f4cfbf0cf5aa1ae94af33ee40c1ef8b8330edcc105207fda6dfa89bd596a9c03dc89da2c9307014c51dd8bcab1ded92ff32c7bd8c3b4abb76c325fb115c1eab050359925281e26ade9b4a3bf6b21a272ca70eeac4f364bf28154ffc78f7b94fe067557ba66c48c2cedb648982abb536cef536776c865e3da0056ce324503307b21fce119a76580b5f3bc8c88266b927d949f62252dfb1298994c20af0800f52ce390b6495cdd2f37ee15bc3adb0f8a3ac0813f6ef0576ce369a87c371123f98b44688524a8edfd4a9bc9c3a1c49430a0254457d8743f26d81ebd0180094911fe0993532d01e0a234cfdc1512be8441e8089f2b0dd16abcc0dc7831167b3441207d476974bdd65cfa19942e21211c7ca29abd8b2dc4ab286ae667b00d4f56aca78550938171e48365cef3a119b5f8ec9a287cd47c836da0eb6b35af910a0ffbfd2d63aff9d5bfaa86a53318586e2f7fe309ac6247d1e33e7ad435046acc59fceecbfe9cf2866937be0076d998015048b32c5d7419fe101a77f82579d26cc1fac2aaa1fb8c3d2ae3a89fdadf4a88b38f1715b2f0c62f32a16dc1bb1419e86b69939dff1fb57213e9e52befe1ff06d7d35df40b6bf78d147869079acf08997ea026aaec4105872df9463eacb03d9ab6f7b18fcca2546b4dcf9a0fc53d06e49628fb02eed5a4b8c89854b96e193c74b7f51609bfceba6e5ba9b831803fb09be9e0403dc63e2f13d1424e0b90a06db76bc721049bb448a285a7024c91a0de095184143b07f7519f64be3dd243e5a636a05332df286e60b9b69a205194e26f8ca2822d6969446592bda41afef14e8af04ef22476efb8409c57e74e75d56e8fcead8f6832b876025577742d8dfbb740f87213692c7cd6a99b40eaf4cdfabca2cf3653325a4bb55af714b35b3d68bf3d448e0ea2d19743145e308b0a45e981805c39034990c7fa26ee467dbc951dc4981f46d02d91984126d891841124b5dd0962490d5847d1c4200a6181e1b434af14b2f51d98fdab1439622dd676027c7cbeecb49249929d64a6c1c50577630cff319a847dcc35d55c610fa82831a7da06e09386f5c2c7161164672cd4a929ed6b1eb1a34f97d8a75804b46a1d517e135f569051c9f0a8ce3082c529be5dd041ec842b9a82d0c8ede48d0cd65b85500548545e08dda3f9a16343fa72bbc7bcb04d78bfc2edfce4ed5d204d0d2215f12f73db64ad3fb4cb26768d116804fdc2f7e17bdba472d028fb29b58606e6bf4c94df88d122c524129bb9668b9920377286ed9d70ee6fc08d997f6f4685bf436761b941e51cc35889541df3f78072a3e0602b8fc41368db531825da0573d143618ff25c3402dd4858c688043c91b019b63d3f8a086e64c45f68bad39d835814523eb2af888e8a685aad0149a05b7f751915bc59d55027850032418fc7cd31c2d0ad56d855c688dd4bb96a6f7591424881d5744b719b06f941d4be911d10141b58fe083846f19177acf22605075ee7eb1ca9fbb3615a1d09dc33a7dc7337da30e174a90b63e8f4cc9ba4ae5c80e2833b3d3c067f360ecefcf7d49e0fc20b9dec9925c362b5cc79e62249dbc440e3e78d1ed7aa3e8ad005ef106bb149566d925e048cba59575519cac986d6d06d363bd76ced55132859ad8d9fe9495ddca23e82d5413f661672c31ed6796193b064603b9b62dd0cec2fcd97cfecd2114e75466e39ea76923b4243fa0d2af52b4acbc2116e60c5e9a85ea751eb5c31e700249473bb518b0146a23f28915fd88818010b5efbe3971b48838da57f1f664a1173b0b0a16c564aeb34ba3f39a4d3a261206c0a947d31c595ddaf7f350cd7fec2823e2646261a0b87ba9157e1a42d5c6be6fe450a31eaab2cbeadeae8a06bb687624ab20e2f4a24950e4fc3877123154abcf02a6c6261467d6d950e67e1282c097ce163814911760702f3b80fd0432f376732eec649c7d2304f4460f6fd386201c95829e55b209e946dc39d5c38021f489c2022fbe4fa50f3cef6a0125eb7faaa5691134745afdfed270b9c071731e870c614b14b890f4ce99c2de5d7e8e6240cfb45f01720156236806f6118f03a4b7807dfd6b4e694f049f21e1d84b1029dd44090f81a5db49d05d75dd3a2b596fa51b20b4d0ca7b30c88e4871898dd36889f9510c7e2640cba248263d738f684bcab6d21b1964a9612d8170aa32f822b6685a8bda67f3d0809ef7413830b530c420820522ecc5288548c454043af28c106a600ab44e5a91cab842c8928f74b9e076a5df20ad35c965e740b5e8fd870d790556b5344db71087ceff63e12ba0c0496c699b4101af65a54abeacb9ba6c0eaae4be50137f5dec61599d460adc9b910fa9e882e7e3ffc9df74ad0e1edc353c4233dae0f421f170f0c586d93e073590a3d3c20c05609632fc120ba0d98ca1ba2b9165ef7e47af3cdec4cf29750ec145200284cbfbfc5985d69ff5ab18ace2fe65fe91a13fceba554a4fdfc645156f4fad1f180e5f80c1b365d960d0dba31dc07ad8d7d6e401a21e9fa5c9912918e375c9f30d08c0c251675b2d0d77408d422c9e302603e40f1370243fef2fc1f6959d1cc6a99100349d8fe5b0c799a3203f851ef6b2c90fb769ed3008e15ee59faa9d964e4629698c5b80a0106393641c145b1ea0f218544e243d6abd2cfbd82f0407107b48e4206452f034ae58ae2f73157016b4142202144235f1756155e848df3057080803672d945f3e05ec15f8aab9f73d647e9120dac1b0352109de7c18190a91d66c560c479f9928a2a9cffdc9712c4ac84b895b1a2797193e9179d28ea89d5fbb4fba5275c186e1a9d4185c6eaea3385548c6fa7242b4be4aa4b5421f36cf90ddd5762e7e7940626ecaa4aa008b83731a3820e959731e1bddf49a63e4b9e4148754e1b8f2a757eff4441c7dc51af76ac4a13a03d2abf1ce27bed48272c593d20128a512ca7322357f98b1cec9d963e6d2151f20048408845298c88b55c8663a6e4d376f46156d452a2827b13f677988d5cf593f00e330e65e141ed66f2516d5cd7ab63ad35a0c419b6d48debcf3451c2f90ab1202e39ce2f8f9b8ba0d49858f7c72b0bae38837cce58b99c327d99c62842421d66bd6cd942c3690e73dbae58525ca0c37819223e4739e187e03c00290fb9315da0463df0cecb50a61ef4b293910e861d814d3d889513a562a9c0e3662dfbedfcf6cf3021128fbe9e657ebd43b6e2048cd52baf761b514e58f5c9c3e6627b42709eac17901962a0596aa3f7ef8e63ef3bbc204afba00283d26fd7fd88e4e635186b11c1365dee91bfcb3ebe722e6dc8883a8064d31a423fa04348a30fac4ee4c87dd690ce88a7c744a7bdc253708fea111c1b21bd2c6e0a370136d4a69a07570d04592a33a526cd03c5d01915398479d84e47a8fb6b5ce8ac346996b3b9c8674e831e24ff61d2e13971638ce62423f716779fffc68669547d0ca8c3b12e72941cdfc4000f48d56027a11dc782fbd7e9e309a2cfcbadfb5333110f884546c61df9c26c48c48eb582778857e0730898861d76aab7247093e7a7e2f118417800b7b6e179e155e5fc462527c3790b09e1aa058b70151fd05c539e70670947746b0c286154ce127b20f82e904839b9d1e6397b67be85b414593cdf9b6ad79769e7bd453d1799c53903214fc0024293126925b3c77bb35e2199a949a842f783dbd24903d974d8c00bf02e1d1d1521cee6f0d65db2502ca7713d7af5c493d50d1c7e15fdff6c1f25c0eb0c97b3dedc8882044c13e192b306a1c0e89ae4b0075b8eaa302aa518bb7bc63ed40023403504989f7598900f601d0520d22b476faa4979f437ed42feb7da7dfb3132eb8a7080c80258fe6e2ee5ce4416225be911694f50e65da425f19be0bec7ac2bdb07239eed8d6c5f847a4687bf29c4e67ad39a9e1a30dce81b7dd8a52b3c304fd4b69a23e0950493d2ec56a28f368dec68c396e001401e8afc4851a8f9c43770e288fc76893ff2bf137f728fcc19e8f57103985f1d765482d6afdaa97c63bf89bff9a420474a416340785fca3c295b37cfe8b0dbba8476ea57a8bbd13950e36cf26449eef708d5f4cea2c5434a3c95841c80c06fa2eb155116993402e89063b461cb38ad33b034769d07330566e96f479f07313dfc45649a43775808fe4589555b7c1113fb3a52ee3ab183a3853f527fbfb25c5fe7241c0a5718ffd57b7faae23f34b68fbaa83a563c7b390bcf75526a5990d5f256740d1f240f67d869bbf4c49d3d32f6185bd78884fae8abf111d71ed58af80931e20024b631ff4c2d1bc3693e64ecd5a61040a4ce0e08b17861485de11ec9397f8987136c1b1c9a2053c770e0e89fb80e19a2805f755cea478a8f615e6d87b8bfafe4253ef1d97a98484675d7f91c486d552b043d9d18366bf4f6c1997e62fc5f16adc81442c4761b02e4b30c6b1a666bac95e9a7dbe5949ed1f7940db7c0106aa84aaf4d9aa468a66283c21d5ef27b8d7b820ff31fa9ecd8e695ef190d157ef300f33570d5d1e28b40daa01f0849000247e17740798eaf31919152bd3f803000ada6dd0143568922be58f80dbeee172849feab3df037cf5b488fff4fdbd933e8abe8da699f095994b181cb241fc7f7def26464e30fb108f1527b28724416d77c4f4810462d20d8ea0c29932587213fe11dfdb7cdaaf19da88c01d09ea0126596717a5efe957ef12a8845996e6b8a898d8307f0b8bce1f291288ece59cdf52008f3069e09165739ad038b1c3ed507eee9b6c928773e25c6a360b88afd920b8b5ea04c551affd028725f32e6a604850aa90329f2d822c5c099cdf123d5732187b418d6ce0601eaf52ebfcc684e892885ed62bd34b88011894276cd1cb207c33df81b3b8c63ae13447d76a4b74ccbbecabf12f6da73e2757f3e2600765f93c9b562c893a53030c6aae8e9d3bec1e62574827a57d60d04d44867db3852bc66ffc93f4078787c6bc0af3408240b80211018776677daae53a5f7192b4e792e82c6926b9e8b723496f0182e31d7ccf1e56045a137ea5aadfbee77f1c5e2d7b974411c6f1fc060c6d17cc39d8d296d5886cb977b9717ffdc993fe7c24f7e1c1e04b7651a545c1b4afc78b1123669154941ae24da6bc2dbd12a353244f70e9e2fa45af36d4ef97a10efad73843ecfcf40c1c892e945715d377fe7f5c1783100f8ef012cbef55f9168677efef8433130598e41c00c0c1f472091bd71a7570a7767e3b245c7e9bd1f52006642a7f058fbcf99e3c523db1be688ec3c2ebfa16e999b849d398cfd6ae250548a0fee954aae48101a04a1b823add2e5c5421f0e07c0baa97e4682621332ad1395ed72ddb8e5ba17bde88ba353678d784585811a1886a20629d49281ba72cf90a0d382db743ad24d6a06d283c169b0459b8269427c0b4366af36864640073bf2d59764e36dfd39f9b3ddb5f08561340e7a0fff5840c02b918f548204a98de6318d1f75e8fe8aec5de407e8f76b3e9282dce77e219c560e0582cba190471db4e0461f9afc5b583d5f5c44f5d31a374f344e9a506f3a3f5b2684758759860ec39643c21f9ad1e695909cba72b9b932cc0a3bf4f3c195b62745fc7fa3fb582121acf82476818306fb10e7605847c08239d9ab6d24d757c9f1c47cc42d66f57da04db576e44ba3aea60c2247511497c7a62af952109efd6d8d1b0a9d4be5b40bb1af4c05732fd57c8a59d5511a7b0f9f39a744f42f6c661d64412c71ef19935d551c3459d4d88443baa8cbc65c69afe304c8196e39dac86225bc3ce416ca9e4e6a83fd7c10e15d57ab23436cdb6de419e657805b926afcc1a576ffe2bcb1fa58b796151967a4122f5cd0e475f734b477638171f3a0fa4296f67bac4532ed78107f254a596091a9c1448d2a3e3073e20c8277aba458b0825f744008f012a72aa27fa04d2d1e42d7462399ea57804dfe9301da2e8a78199f258e8d2e4660421427b692258e722fac55f3b27f5699a9ec411d32a9b29555c5d842bcd72536fd6cbbadb08163559d0433a70cd18e14139340eb5423c7ba016ff226d02c1a4a320bbf6d034206cf77b7fb7b71240d1afe322ceaf914e083f188d86e0e2682c0b2200223a4c5a26a5f713b8d79f56eddd0574538020952011779441c4e1598e1e574444a4b29ed7dca550ce71e9944718c48f3186101f121374e0e06967bdc7717b2a86928dbff6b77bff241529ac16b544a43314669b15289097a1c7a0a30770a54b5c25adddc45450dc087d27a239646df5c0b29f71b02d4978c216d11f8001f09680e0281de9da50969bb0b3afa25e879308fc45533a33f9913b9d39ce0916214da0f3747c67ae153b2f415361559364f67fb9f0a36ab447205b251434327c46ac4fc5e53dd82094196b6546655206fc8f9581593a8dc8f355a5a5484d95c8201a84ad5d00380a76a6a8d02fefacce3516bffbeffb7ffad2f17f864ca3778ea9ecaa9986f2662438bd1f3b0c78bb1f19090404c21f3201a4d8710244bb3939c9231afc2f13c5a70fe82bc9fcf60a2c5a27df8348ab568e36c7075eca641d04712009ae9a86a5b93e6a13980ae409a21db5d5cfa9168b6d49daea6c2b00550bca8f98e9e5efca561d878ce327313928abdf94d0848b428c997f29c0f1ff20d4248e94adfdc20b7f10ba99e86f52e33230b1fae5d9152b66a98da780d28d4fed4cf6f9c070e71450d363da69f83a31323b80c1a0fa2b3b2d24f0b0f7ed85697b3d848c10d1506e7e373a378d2960543a13a0bf5bd0b90dc6b17dc819f7b8447322ee15b0a20dcef26f458b0d81f2a49f601679faac50e81f0c69a6967393a586be60c130fd8c89e1c286902e9202f438bbb71e6213efae3fc32a4395e69f0009f3744c8fe1b8786467a857a1b5f1727473a66f0ea07fdcf205b746bde724e449d1890ec4cea6fc2650cf80e043f971d72991755d717950c86340fe781a25dbce3ca0510f457abac7661fc74a21f94662c7c6809f67fbee9977ad5e97e86e41917c52cecc3e7b35e79b1d9654f45383ee5f655cb38379f8508cc14b2e9ba41b600d8960b83ec83f46890195fb2704e3a7c19b0ef1a52ad6922c7610a5cc5f54c5370291bdf30443759bcac0336a251abb99d33e391b6ed395e50f64031055e5665ff72d79dc4dbcf603718ca5e49e475f48289aa9963355d5242a00ca7abefc07baa7878f36c7a45c26ff7133e9d5b0aaa803d1452a759713883bcee064390395c5413930e316b43a3bf8b348a85e460b6d968d8120f64e48626a0ce657b828d66f7744050617b2d8f6d169ce8f64b0b54704516a0e9c830a8a301b89188a677674d4bf7d41d8ca9b8ea31fc9b8ace6822de09bb11932b5e09a90a211f2d774d32cfe586535eaf0015804219041c5977cc6ced024591015cb82fdc518999e453997d82508e94d585c3381b1ea9c768853faacaafc2f2ac44a158f60db7a7f9098f4da04736d25e98898524959dcdaf7b0a06a63134edf8d9eb3d9172c9c4d8038764c75abf3f54204291e6bf20fd6c2ef32f2265695853c02a509b100de3be7eb6effa11d17bc0efa0ece5378af5dacfe9b35bfccce6e2d66657143566be7f509ca354d5092840cffbaa5a4ca43fd50dcb8040fbfe4e9bf9ce9ef390c20e7c83f9adcd3874c364b800adfab2f29cb9c3e11c1bb8f377e6431d9f16c8f3db770e1315ccfb7a59fb9ced74dc1592e70135b6d62d43803abe4996aa6631cc49b189c53d48453c39166fad44e1aa71d12b13cddec2b28842580ca0440b3c687754c8295dbf4c805df9ac01663a1a258bad3bd2466faa15e97c6c844fd908882b4154912113613126b6947a884683178782256d162aa0547efe9d7c62ff2deca93bff13f3e640b49332e15297067c084a30056d24952c7b7c0f8b109b839acb4f9248a04685c25e0d880027eea4aab422788dcc1c1e252f4d6f91646c04913721a5b0204654a84b08cdda38cc1b575c7c5f083ce37f5803255f1af7824fda7e670fbd3673006120e99b8f0a7768c843676f78b80b7d9adb02a55ca0615f246931a8db5bc6be28cc5be9d81fb2341864157537fbd07114e3d4e775a0848867ad1050b80511173ff0e94b05171bb295d469b48f6aecb328d690e09eb58fc8128e1b8a242f76604ae1fe219ac08e84c4c1f8f8f5a37ad318834ba765b4f7d9b2c7b2d0e87a78b8233873a0a29e1056c11a930acf38e8842687009a38ca7d87b780fd62aa0d2f566591133e70f8e4742bb57ce56ad4baca9b62d990c25a766d671c7096fb18e9ce72885838aecef99ef82403e9a8cda29d5b78dd227e2a1dfaa53eb9757734dba0832b7bcb07acbd7d2836fb62ea0ef7c66510e0a92c879d8b3d1c4b70c5a167e3097298a79d62892ad816e71e952264e4168c7d02f290b12f45d556cb83d50364489e17a31749bcf974ceba0f4920080594d705d9784e1c0426dad359a660a0743f8ee9e9501c1bf0c2391c8981147bd7b19063293f51d66504624179b13c2ebd2180ec375178ce74814c005221fcb47737dc2f56b21b10107822a3c4cba5bb8993a2ee98dfb5b3b1c1b9a636bc65794532552747c9dfa498323c0e719662118d54997758c6bf1fb5a35ce20cd5c3db8d137942b89892545b8ea99c4a0538f09ad433452bc8c83ac781ff4cd943e956f26a7f51f15e88381e1b1563040047f512950559e251af7c11c689d37c8bbb2ce1008136853307543b68a1502905a08114072a0bbaf83c6eb0a6e7cbcb416a9f52d2bbbfe3936c3dc8538744858cd617c043b2b48a215bc0c13195e8207f6b197497ae6d54ee1482c648a452e0ef193917fd5d41ab6386fb946db75b0dad65a87a8edd1359d0097f3cf05116fe748bf7b1ec3079c43b0349d719a8fae5adf75dd98a1c19df3a58cad53a9f275c34e4fea902877ffcf0511e7e6389f6f92de36ffc452147344d677d2daf478ffb5776c6bb81fb4115604d64820654eb0f4810f84f03681e1ecbbfbbde1ecf0adfa89ba4a145ffe910c0632bdd681659d675c7758953fab3aa10c2c06dc70d85aa7c181726d6da199d6c70aadfa110c26ea52d43b6d109d25ebc85aa90444d5b56a0389e5f05031f2213d6b5803ecf8b8f42ac3cf4852e8e88112c86442f69253bbeb4a16f0fcbb97ceabca5c5bee7c0da9c7b07c14db46375bc19e8e9d5d1e9a51d211c341d39c600e7a55b9d5a39b7e92d604a95d31fcb3c58f6ada41fdcec7421b7eb46b9f1890592082402cf232cb8a28b47eeb28d84e0d24d3aa5ffc1f337accda14156c92923348db93cb39984a99bcbd99d048a2bb03f2736c30bdb26e00baa42c25c786341be3e6448b99ad802e2f3abba964645c2a83e9953cfb76e2e41513fc5d187c0f91ad12a02d9b812d2ae95372a166b5725eb0530c1c4920a3e0898c735c936d3f204c6f8d4ad1c0081ac4c2c06b5e4d3a774cfb46eee4f96325d55b1c085671b189f143f5bdf4a975c0783544b46729da88421efdf5219aec0a2a223c8e7666db09a2b048099b7e76660a74e325522e9b0addabb1bfac68b0bbe4e2ba4b30a32b4ef699ee46917090b8e1e6ae784ffbdd0cef9704ba018844026ac782f654b9464a1d5375b3f3f5b29d91beeedf670655169e586ab8247058b28c009ef960bee6428226af9962d0118050842d34b52bab42440f9e7a851afe3b97adf15ca65444549cdfb3e05f3cc4a44b50bfb2774e4cd6a472e87b26785e0211b3346f688a34d62a506318e34bdb6876cabf8fdc898bf514523e52c9c457d2e5408d5fad8f9e09c00515bfebeafc456fd531e9519a2cd75c2236bae5549eae6f424f25a832524b0b0d7d90ce54a59c29166f4bd2416456b95aa7274421e98cccd2a5a7c64857254e083643d2fae85be2853b4c8c0686c636bd62aa507ceb88200dd612d4b992f2bc754633343181787b6af6a5dad45f08d774bae2ee8a34406bc1d00cfa5d0d5ac20beb744b9e02953c4fd6a776135bb1e36cd4cf63080414f2837b7a11a3600f4f671f1b71142148dd93af3740e05836f98ae762ecbfcdc540f6c179e2bf36014f8eb97593153eb49e24c8b4252c9e119776ebd54a550eda59478b0fb32b0a8a564ee44d7a9018413054e0884af0c59305c554d07998456b5b2a53f4db32336d2498a52e23cabd94a1f42231d5b1136f5463286d086263f6e9e031a470b0bce01008080e60e63280ba027b2dfb3e771170c4e07f957b908ee56ead8ac35d94deedd6d6f29a54c5206ce0cee0cab0c6fc3d6c0094a50f23182d139ad35a9697123853a7eba1c9211c05328d62ae9d567d19c44524a29392939c96dcec96c13b9289390b2d6cce5573d931ee328d346354f2046e1a18ecfb6975b8c2a4b051c64810b1f6edb6fce6d91e3388ee338aec618738c31d618b31c0db667517828bfd65aeb68e4218d85fdb17a6cb04a23678855e6d0bf08f931d6208e08fbcc251566a86788dbb66d2e6daa6f6fb3ed8871025128f3b0e63089af54f86ce4f1cb7cf4d17cf45ac3247ee49c966d70c428723ecb138851e4e8f8510eebc728723efaf65aabb65a6badb2d61aa56613fd86b7d1682cca1801f34064c07c48bd50e33b92d0217df02823cbe41b0ebcccc8388ec98c0fde301f9e8426340e7023e33ec884305d38bab1d530e630898f41442a7ccd4062bc2e11764ff5d517e3f553008c29a048c3cccae317eaf86883230afdc8b398640a423e87fc519e4014fa2c03d14012318a9c9998ccf2550ee3c973b38bf9561e2f0362143933abac23c6f14cf9e0325f9cf1e163d11a8400064abc200833371c789959398ec94ccc77c3819ef1c1710230b372990f2700333ee878a266e53a0eb0721f5cc7133532ee834cd6f1448d0fbeca3a7cc8ad9587d115c5d0812200d9c21368200525f80e132409020b5800c52b0760988933312b9fd19a8cf9565ff685437c8efa59cd68acc12ea2444b70f590c66a448f59a642521fdad36053244e107d488b5ad523a4553426e91255340d8102155387af2ea38c08ebf99aa58d0c132fce1086d169831ec85d24f3a1f449348b649e938919fb36c34b9fb1564996aa0273155b52081232c9cc9143a4916f568fdc89a9688c12554124ed37e7acdd95d3e58d4e44b62122661c73d67b636608e0a877084b4494998856c58ca387ae09815685918f5109961f62e48731be5b7e88336b31627ef820e09045dee9b14f4e273df3e9e4e4145d2e7f8a9c7c4a3cd3b79fcd88dc7e5a258dc4f9f5d70f0a9e774211256c46e8d95af1250d3bd87c10656c43248cde8600e17bebc1b76f42a28c74b9b54831afd5a397dc866c445c51467a74af3c5e795a157a49f8f688a48a1e5be59195a75946af2823a768f1b42a4f6cebe9d984782e6d8845c4ba894210f6e8258ff5762412be1bc7759987d171d43cda444d7ad641a523c132b93479c9a5c9a5c96da4cbac83158b2a0b092026a5528c31462959d175b062d12759a52f077d12a954922cec79885d478392853d8c45384b96b7156948b44a4699b57f93cb277d29e3e89ed33cd307e4c4312e954aa592e765dc60770dc6942c91450de2f44d832cae5973c51a034662080e6311123a8080c358f43fc09c0f70287df08d626625565895d40aa3340f7d15da80b6d7f6da801a6ce9aa43b617a59b2bca2093ca5c63bfb93697cb18e070736d2ffa02da5c2bfc9abeb95a25299597004d9d56afb204f8ce5c2387af3e0d2ae91516d2a996aeaa43738571a690b012611c1f9cea83717c706c836d411bd0e6a25f11c544db121cc3b13967f6b3fa6c2ebab9e60ab7706b73e1d6e6c2adcd855b9b0bb762980817551f0d2b196ad0f549b011dc1ac53e80c3ca93439491d4b95a2ba59356dcc22d2ce4db298f1d333b6682308e2a524c558069f6629e9206a337e4015cab8f37e4b3da805cb4095c3ddc5cde50adde900c6a159542ac4294e10d094599e965e10ad1438f0adfeec182e013dfd13a9fd15094893e5166ba7c5c3ebee3de909491ca210b3c4136233c3eeee34a43cb05e413859d2146e8077c7c867c585aad922e1f23bdc246702b89cb1565c6ad10e3e056cc1e0bc785d3aaf0c4e3c45262a9986072f2879c22721e5b42adc70c58d6573889be7d0e49a951e9f372c6a69206690fed5c4cabfc1fa6c11953757e56e0e31372d0e93208963e5de659f404fc82c1404c07c3e4a904063bb223020e61785e720e23a5c718302b8c8039c3517b0bb0632e31c23e8ef8ea5ce2a8391f1d08425af1a59d9b3d9367a63829eff5397b5e1e60f2704da36c4a02c2f98c9e0ad899af6f9f44bd6a1fdd3e967c77a8fed9999256c5e6cedc993b3020cc4e0b35c8fa25334eaf433778fad011b2ce75b78d347bba04b6b2e6340f4f8c27c6137307a5bbc78ca3e6bc447285256c1ac82571076910c967aff8527dbcd449019e3e3b29023c637c99b1fb9849d9c71cdaa04be09939161af046061cb68fefb07ba8532dd4301f42ad0abb8aafa27db42ae6266a50253310ae53ddb1ac5f7c8ad8e0033ca4c085a228869f2ea0c00920546801113a1665e22482c3b9338db4ca05711593b0337df0edad89039839034a2ba595d21823de34eb588b3f58ba14bc30c4c40e1a52a0822fd81c206108ae9c1e2030d13e79224c4cb5b30861a2cd2ba4873073c7e7e74eecdbe78f1964f2cc9d28d3819d6f2fc4547b93616e6036f1edd4a779e28bd6b14eb56b1ac6279e993bc6024cb59e51bb8e6c0971ceb7731e508cae18bd979c010e6b4ff5a9426a8f8f2a4a23ec4b0702cf077dd7f05c7395e56dfbb1fd7547d6078a32f2df735d1bf3b79f32f4f45721bdf26233d5b121db0b557dfa96c48bcd1587d3a9edd5aa9f2843b99cd8f704f5aa1ae95411e2aae22086f5871f62c478c1527b6864ae2dccc22c5b63f45e1e902b0938f45cacb9f2843ae56a958c32d1853c264415662282edc2372c565d6d493ad5db8f75cc9ad613f2b010651ca16d21cac02c4f4994a94a74e1dbd3c2b77b4351467afd31fab44e27bed49e116ceb8932b5f6d800c3e14927be34c89232be08a35b7c885934d79d9f1f2db721df3b7faa3d211681cf4f92564d29867c7b155287c8b0f6c88f30597b4e7c86f4ca03f25c46e4f6d3dbcf94337b2ff05cdf4596091c62d6cfb76356ab428c6f54d2714e690a2dadff117f8694d11bf8aed5658dd1ba57263eca34101d5d49143dad383599d7ace30036eb00c2fb2620cedce010e61b87c4f93803d32a9be851e85be863061d8802c43408e3021c3d8471010e636bd5d2a9dec1714aff561dab00a2050a374ac0a1cb3ce30538848189315bc399760040740fed5d0b03d13940b44afee8655c59f7192d6746452f7dff8c963eb0cdda634b4983e0168b2f40cca09e8e8782efa15685168a701b523223c9b70f20fe300388b9caa2cb9a850d58289cf02c9ec6b6010e6b508749c84b638587829a85cd0b506c439b922d165f58bccc9e0e117018333d46e6981ad4aa30e6db6aa1d2d9d17a6bccac81df524ab192f3374f44b72e7d6016273c38e03327f307f804677aac8f433bd9f9ee04e7434fe844eb4e82787cfb098f1e2fd42bebf264e74d50956c23a826d886947c3be781601b812734f4ed271f7820f8f1e1490f219c131e9d6aa7279ff8d2de42385aa801cb82c5db8f762aba8c94aee454245cad32e6053904fb094318660f38cd634b38773a9fb69e45193963c74c288bb40f06157de43b666234af5ea3d5ea58cb9a26b364c90d641989996ab7b90273a6b562ba1112d6ad3642050f22aaf830c6242c066bd5945902b56a116e3a7766571fb07c288030460ccbebdb1dd4648422ac2d880d60e8093b4a88be59d3a5e3a79ff64da73e630b5593fbd5432c9c9c1e9ff99ab039142bc29181841cb466688c16c599d8433a3b3dd4ddcdf3438340c00e82c398981f762a07348fd1a90eff8161eaa797e22a5c19fd7419575a9c91a8034828be3493902da5a0ad2a68eb8df00c0511f2031c6acf93c30327beb8a4a03fad19f167c6785a3b1a8f8bc286e0b5c455fbd4f05f1aef6dda299ea9d849d9de7de7f125ca1c963e6c9fdd93d2fc2da718b4d810f1657ae6337a663eedcf9c39c39929daaa3fea8ec599990ab12f6d47c84bc6e6095cd4e5841ea02056e783db676bfa7c0be9c96a74227cc74c787afbbd503094cc280458a75e63a96759474c6c7b00984d0a1c332d22437c698f3371a6661a9b29a734e6518616519d99eaa1996a9f2cc061bbbe7f1a8491453dc021289b4e41a93b81f5ed2d2f624beea00ee0c2a3c7a7efe2f3e8d25de44d8b3718dd65ba745749baade0501dcbad816483c7027b45297da66cd2c2a69cb464279591b6e23287ee2de07450e23498e33a3e5ae55a4cc53cf10d95b2a545fe9c850d35b0a056ac9c545250624c08549c98984ad823dd8edb465a66aba6f94e4b6f3e0238bd92488efb4e3ad52ebf9b27aa731467ce7d15a8cec51a7df33cf2930fe353d421c32e077d2a5180437cbac1f8e4d4d2de18336ae61a46d4cf41258ece5929f860054172724c833d9dc095ce0ad3343c591a16124fea36d2e5749a3b5309b0f40264dacaacc04d6badb59964bd3dfa8c7685b5b656cf1aa454a35a379f10622290bc7adc3c4167cc102022c2321143b47c3b045a159d46745a6db59536a055d2e54d94a72ccb43c454fb89c608cb9d0e60777e409ea81e7dda44a7339600cf8cee8d9869ee42ed63660d7dd194628a1339135bc2cec9b43929a59466d99454764f99d1cceb6985c44c49392497c8a2cc6551ab32ef5b83872c482f6df06ae85eb27028d30a130f472db428a3b9cc5eaabcb42f535ed697282f9dae4815798394a537bdf4186538978ea30c8bcb1c665e59011bac537d53f2520ee97bbec312e961bf1a2239446e88b06cb83544584158d260fc1a22339a45e61a229785436be4db6dac55611723b245524664e1e4e85c9e797b4838ad1af21365e28c79423c1f8f9443f2f124d89384bebd86884eab66163e3e00be1a223aad21a85bd31a62a455130b1f1fe6ab2962a4c78a5b03458f9f56cda18f0fe3ab29f2a3b3726ba0d0e969d5bcc2c77ff96a8af4b04eb7060a964faba6153e3ef8d514f1810247e5d64081f3a355b30a1ffffb6a8afc204ab9352d229e564da18f9fbf9a223cad22945bd32a6ab56a52e1e3dbf0d51469419173726ba0c8d969d59cc2c767f1d514d969c54c6e4d2b36a455530a1fff7e354586f08832120a1e425a35611fbffb6a8a0881e2a6746ba0b809d2aa59c5c7277d3545822cc1b7a6b584c89256cd287c7ceeab21b22489776b5a498ae4b46a42e1e36f5f4d911c5894912d988f56cda08fbfe2ab29e243c9bd352d2545705a35a9f8f82b5f4d119c57776b5a2f22b156cd277cfcd3574324d68a32b245a4a855d3091fdff4d51029fad96e4deba7488f56cdd7c757f96a8af4101add9a961011a256cd293efee8ab21426444bb352d23ac56cd267cfc94afa608cb1565644dcb75d3aac9848f8ff2d514b9018a32b205a4d3aa09f4f14dbe9a223a43f5d6b486e24bfc9a223c5a3597f0f1f157d30a7a7a6b5a413f280f0dd2aa7ad3ab0d2624455cf510f5417b7c531e2ae154e2841f034f9256f5121c4419f346c2e8e93387b40a4f23f3a755536565afe2cc64e960fad860a5955c2a99b2bbfb28bba7b4aabf7ad688fdb1494e9e6b2dff848565e2f492792365682f09a3bd8796bc77132b651209331153ed20780df7c0ac06fbc61bf29478b10689bc226fc94bcaf0aa1061f415bedd7b79304f28beb4df25a20ccfdb2f13df6e7d48195eb63b124667eb8385dbb0c1adc7ebb5f019ddc2397a6958230d36c9e49c2927ca5cb9ad5831bd6e4b9b724c2388aa7a8ae121d6c1436d07efbc8bcdd50ed9ba74c89f8bc793f5802af54aeae4175974169f4eee5e50c9e5021d6cb0416b53b49f92abe422bdf80b8c87305e3e17305e6e70172f0ee305e6e505c6512f30300e83060c8c1ca25e5e5cdce02e5e5e5e5ebc4be3859e5050a8d55c29594b5272719bf6b3dd20aa531fdd1b9466140da55e9017e4bd00e0a10704002f08009f07646df0ef85a6506bad4d714b5368a4509a425f68cadb3f17171717eb35cb9c83e3e09cec52da4f4afb399d5c6a3f36db60c75e4d3a8955dc445697dc67aa356c702e5e6972958fbe07d4abd8f282bccfbabc3458b8e963f1e233b5e9b3c1f4598fa6de72a8bd0eaf95fcd2e18b3115e38b3135bd1639e09053a70f05fc427ac31782a174f1859a351265a2bc40c8a71388e87da1bf378447bdf4edf82c5327248cd8b7c7c243bbd35d014b29a5f47cbe97c32c0d6948c392dc1135d8a552a9e4793b6a83a5ac35586af1c92f612fa9a8788a8a4a8aef78cd53547cc79f5cc597b07c3cf27afd11272831a5e1db4d3e1e791139e1c7875dec88137c1c0972fdcbc723413bcfc5ce0a38ec62271e4e9c0d14550126a413c261e7f14709a26fef888aba25d3ebf4dae315f003078e518040e8f3c72840d09241eba5d7d8fa964e62cf5b2e0d132a67b44dd2aa149c5b38a77d36498bcffecc2d1831d2c55270ce5c549ebabc37543e73ce452a3629acc1cf733103382209f2f3d8bb5883f244760dae78dd92cb6a150a0acae95158769c34882d912dea62d8cb26d9c6222d798e695734713495943caf0d7fec9ced4c3e8cddc43dc7b7003b1e63f7eecd77711acc3c97d2fbba33c4975e125fba185157e479f9b2a880ab875d8c86f69a02965efaa1c192cf989a16f4ed285d4c0a58e523128650f45b0c4cc94ba592cbd87adc1575aa63b21cc2c08033873e71268e97c399f3ed5f0c8659adead88f251f7237df6e8df40a27c121b45e572557e965837c97604270b825e724567111bd94a184d1f030ec46ea4b382417373f529206793458fae29b721a0c32f403e6830b93e0f1d4bd8f935865cae99429a7af1032938048afae0a6cd3e590df7555601be9a715c7ce9dbecef10a0a9bf8e94361ece09f7c059f48278fb8fd4472d4c964c54d482b39449d4a3cbaa225d8c4a57318c3b0501470886128893f9474f0b19b84289969c0c7b908192bf168d5e728ec217613af1f86d130c9210a671af04b384f3fc4b09e95544d5e9fe2dd069ba9ce5e1f2691f38261c759f39a040f22640ea9ab38373b2f085b9e6c7f34987383905efdde1bd5a5f45abde6120f286059bd14571b2ca6da4ba52d6eb092774198046bb03d18e7e17b238a10dcfcf43a338a269770841aec39738947834d0aaa61835bb7af156049f46117537959202a70761bb97b02f67c36115f7ae23801875d2c7fd62589decb4a9ffcd80e7e8d7d36b0f45726be3aed559c81a19d755250af62f789f4ead0c6621d51ab5692b4ca45023a399e725697d5290ef02912df2dd0158b767e5ae71bc5e957445604122f2b8ac415e5a3cf45293bc64a32017acd431bdb841aec9a2386f92ef679b3e62aced01c6a6029cbfc2679838513c7b4f98a9d6f67a6dabf9fb98beda478d33c05aed9063b76e96117e3e464718ffed6c5c2930f274e48bf0d96d2c580a87f21ccb3f0602ccee1d0a97cf44b38bd8a33abd88ade50afe2ccc9871ecc2beaf660edc1fa8910c61bd590c3933f71ee9a76f0897b79f26810480cdf809831328ef47892c3d6d1c050027a06e6b9fc4e7cc63c29b9f7dd6b274e17b379f298399afb482531d2c4e1d14981a7cec499ab0db6c1700a107f3658cc33bd4c2f6c6bf5bc52a9542a799e2db18c264e47d429540cf76278d704fb436a59e6792409eb10bcc4308961d2a518e40ac364eac73557315ceee0d265ac2551945171e9320c5186871e64c9655c82a35725d74cb526fd01518607e4106822868832262ef3cbf39173de6779ec0fcfdb603606fbf6926bae4c39da8fa9e45a715c729d5cf3804a3e2f0dbbe379a5d2677f347882a2834b930d6672148d741b136cc8463af6f81b2cca68b1f8c284d85c21a1fd4819d665ca89391f6a1d129db75bd7f5be16c527ede64571937e15158d87af07964b737215af41cba6a16c829d8052be18304a8089e13bece0726687183bf0e03ac4d8c17b26eac0c26f8d1d5d43c5b51d7c872f461dc2143fb95f1acd55fccb2d2ecd0e2242f81428a48b8bee622a2a5d4ca58ba97431952ea6d2c554ba984a0e55fce4a1f6e0a5d13cc5539766479157f1942f8c507c4aeb6d544a6e1a2a9db229e8868a6b0e5e1a2a39949f924d43d90443f964eca4be49964b904a08a0c3d733a6214dba29a8c106db5d2e0df692bbb8344ad93484b309160353d219213fdc60dfda4852a939d55037063081d6791bd390a953020e4daf6ec9375157d44981435317fb76d3d002e6aa25a6648c972f5e6f7abdde8405a3650fb4c065e9341ee10c3cffe2db497f842543be5d237dd851ea95da506fa7a5d860acad267ac4196310b3ef0d1a414cbbca987d6f507b6f744b19190fa61fb2c874263bd7a04cd428437a8c5e8f80861d245248c3b7bbf8880426f4f4a564d58ab91c3b62d486d8b63366e95381c7524a1d92e6a0ad1ae02a5b2e8734d88a3936b88d281dc5a29e2e87b4629cb2256dabc12132b3590e69499f2c5b1f0e9f21adb2de9a755b334af2a8611c8ab9020dd2dcb970488756440031532bfaf351fe6850f27c2fd8504c892402626ad3e7c7e747126124a853ed43423e34d20161cb479bead2a59090e746a5c900b589596619a4c1a121223ad59e041c5f355b1583703f9e7e464821f38bad267ab04a435828b660831ad534cd7ad8752feaf42c832f4e5e34885b7e3ef8edf1c14f020a7e47c8f2f2e3cd8cec1e167492b7a516995d37270b4fd6cf689312f832e1c3866d47f8b05b5cebc3de21e5e0c3666d3c1f4a58f7310a960ffb310a96131fca9d12d08f51b044f06184651fa360e17c187db278b4c7956cc594f49822981838747a6157c4f8740241983066b6e6c728584c7c285b4b740efd4d4ae0fa310a16eb43c96a9610b89b27a273c06756ab0f0920e090c6e20542091f21324883fdc5d827842908a98e04c98f8f429042cfc3f8280429b0664b099c398d3548748325abde13c82e09ee8e802d121e66c021058a459ac515ddc1c389a3439c3a3c783877627838797a5045ef565cd11c3cec9f8ffdfaf8c2c3867d1cfa08c3c36e565ccd160f1ba785ebb878d83b61f384ddf36074d98aabd913ca9f8f31463924631f65515c754a94391fa5cec71857449f1157ed797ce118874c11747a04898a1ee9811e89418ff0408fec408fe8408fc0507ae4080c7a0400f4c80b3df2821ec9811ec1811e49e558922337606cc90d31221731252e31a09658508b98508ed920028f988a46be1cc60cf11cc20861914330880d39f41e3115bf864c041f2cb387083f50b327968408372b624b566244a798129518504a2c0825261453271b4678c454346292c39821a61cc20829e5100c8273e83dbc1c6a3e622a3e29f7fc88a9f837f7d8184292988a466eba1cc62cd97208431453a0122e870ea4e5500b8aa9f8231b43108aa9f8590e4f3f5b01dcec6eeb43968f5fd87d5b7fedd2800dcedc5ded0809f313e607206a86c081c44d8e0f1e21ad24adea9ffddd3cb1b5bf68d5bc34d63b87f9a977b27378b2cd8bb5af3fc208a9378d069efa8b9c20fa690677bee5bd813f2b42becdfc74693cab76de20c006a76b39a42d3feb11f2cfd0ac7a6fe017a2dd16c18dba1c168bc5c44ccc3cfb6c96cd2c17c1e2e9edb44f7ef6bd68703a4b8373fa6d010e5ba884bd8db4b369adf59e320bb56ba346e6ed01782dab355ba05fb48032767978dd9c27a7bdd23a6739812d1a3db1f89cf364021d869941e0624be8f54c02122acda6d55c30168e6b52d84c75758ec2e6aaeaccaa033b9d30a653088755671b8d70e501c2c3e298b27093dce29f7b0b778e73f9751ce72d92005d277d61059ee45c46d39132aae437872821b073a852be81736c90bbd24921c9577432cbeb9c775d57ca37bc4ceab88f73d31792fca4c5bd16e772e727e732790d209ff33c5e03c86ff1197dbff0ba974f1eaf102dce659b16f7b24d0be77284b7257bd7826b91b91c7eee2b2cdc84859bb03061b172bd23b9c6b15871ed067173b8e239e69b849233c936f80af7d9d0755cd7755cc771d2243dc5a52cc92facc04b1792bbb8b8ecdcc5e4f2964aa552e9725388c9b5fc48de75a41c3b4779a9647269f2bcb0e35cdeabe3805c9e2bc5eea4a0a07c3bdeeef8d0579dc82e9f9c64784e9e5645795900e4db2b9028136b0e00a45382cb68e5e29ad6591e9706c805f436ea91e6ad396acb10c847d0b7c15fef10b0b9aa3e42d8960087db4fd7795f1260adbb3792c5ea9678ee76f5f31cda1b91033fbd521a2109115c424cb57d820acf240f89a3499d99ca91399e1265b8acd2e96235179506322a0fc77da3a71f102ba5ed1d9ed6d19e7e4072e374566b465dcb624543435aa5be01719171a5ce659d51ae52ef461df7cd17cce5cabaee52bf55ebec8ee7c2d573f1b8f864eb061f9f8e1b695b966136f494f0ed97746950275d1bfdd5394eb31f1017d567662e6e00c2d29591aae7459918a377388607ac46b03bb57e75f474563a29ceaa7dfa6d47884988a97617db14f547cad87e240c2354237cbbf5eae2cbbed6e4886eb6d2998da6cf951598baa6ac3f93b690ee99b4ca496bb356a0ba18654af529dd7e286d1927ada49c178e2b1601217a4c9c5babb2914fe4509366db5c41d54029a59e69239fd15209b873ae45c77d406eb837b870cee59333377cf3a7d59a2f57832e972fce38311ad59f6c87c577df14925c9eb01f12197ea4e0b415d0c8ae8f7b82ead6fa4acda4a2e9f2912ab5928665b9346abe27c0d2b91a58b87c976767c7a7a747e594e2f2753336541b587c1c0e6ba658532727274565d2a0d6b92c7d602bb36dd37c9be15cf8e6e2e6b8c4ba91e6db966999a58e32698bcee51bb5742e9f36a3514d9ab4882e9aa4697973f9b29916f736b16d2e9f9db19f8f9e9835e8aae0f51d7568df4e5c3c97cb5767b80f888bdfdcad9a81b878bc40daa77dda3553391325052928d8546e8dab83c3ca13654e27939a14d991733236e7120acc39f7a273f9e6ccfde83d41e6a391cdae094c9786cda173f97a469b7e9dc0d26734ed99a44c2a32418a44aace52830d539ee6ca2573e1d2e59bb1a199725164a56c4aa994b2e7ccdb8f0d2cba132e6d3f714b425cb9f07617b9feb878b855e1c2c36d0a179ab6d3a367adf196a0ced924c0218dfd4819d50872456332e5e2540c5f902b97ecb9cae0b9ee083c97cc267ee342ab76eb6ec6792e9354c2a6d257dfe4037283cb37b7a0ce6eb3d24927901b5c73f12d3183c5ba21d39c99a2383385b33353ad737374bebdba8284a2bbde2469296f3f9292e81cd1cc670bdff91528a8c1ed3573457079fc6cae1b02af665a2d7dfd5a9cb93c925c104ccfba16978bc35e662a69d46e9d0b0ca6a3537966e591d2e5dbbaae3cb058fd5aeba85ab7d746ff8ef6966e9e5d1ad6330e1f214e11c1bc252182eda56d0911dcaa88a9768faf0dc8d2d8cc54b36fca82645e7978a810da13b34d08165f92b8a4004485206de2d1a5b123397dbb369af4cddfa673d7467fe69cd6799f11397014227b2eca23d4ed0a5fc87266fa8ed03ef3d277842665d09884d155e7772e0f2cb97c3f802dcd341a2b7df15b4619bb33398a65f5ecc83b0f4f2fb8945af18762adedf07792d90e7f269ad6e1cf34da46f82b6d1f7e8ee3bccfb9eb3adae18ff4f58b3f495c0d02096d41b75efcdd6c449a244ac25ff7dbcbdb5e9be7d10e7fdc3b9678a4cd0c638c3bfc6d2fbfede7db8a106124e1dba4882fed4788607b37b2a59a956669f4391b99b669a2a65192cdb50135d8dabb6604198504b7cd6582bfec4ff067bf0271e1f403e2e2c2858b8b8b8c0333e392b79f930e7ff5b30f888bd30f880bdfba91f6cd0fc807e4860378c6c57160665c78caf6a5e0cdf5f8a39fb71fdae16fbef71d8125a644ad7d4070f00cf701c1819971718f12a168915679b2ada84122a058e5992bcfa5e5cfbeea436dae3ef1454619d7c8696f1f569fea5329a772696c39629ab77ab27269a811daa22dbcfd1c819f3af71d817f7af71d813f1b423d80a587d4a76ea86689b24975c5a5799b47d9a4db8a7b9bd836140aeac40402a4ead257741277bee2d2b8be7235bf9fdde13427f9f685dd6f9ac66d5609ac692ef16d028ffc665309f048dedb84bc5c8e78e4df2dc08e1fdd1b23b7c1a3ef88bed152ca887d85a02c96bdb7896de33e790465b11a9cefb91c79cf51de7ee24b4bf9db77f2de0fb7a2e3be38b23f5ad56901e83a002edecd684e4375d5a76543c341c4e623dfbe1c7e2b10db7e1a3ccd2edf6a77deb79fd1f6b3fd64f9e707888b6f3fad8a2edb8f8eed0709ce458aca295252759a1a3b5adc20eb5481ce14a20c0a93305a7e1c580020b6d4e8b3cfde20d620824022682b1274106152867dc114be1d07b73d3f73a003c8e1cee05ebcb8346af6f4b33bd6ee642fea0f2f6e8c7d69c24efdfa0171f1e96277ecce4f0d02e3d6a83c3dadaa1b759d6a4f0d52bdf234d834d634a6830e97463dbd943548831df33aa4b6aa0f48352de74b83031368c0042dc6044e6f0300182c30ee0906a6777edcdb04d63eb4f687e5d9c1eeecc0c336d278a834d3c5ad0176da48dbb84ebb1ac9c35ac9a4999ca0682d7920658e5b956de6c15ab44482135c6c09fd09e9b1690f198833f2ab915c8e317581d15397f786c67d5f8d64b1728cf106414301fa85251c7fc76fdde87ae70ab81de7521b7911320d7fc339bee9b6ead6d61ad54fcfc367ce7159d765d56d8f7aeb6eee92b47c447f56efbd313bbb3548b7467539c36559963113428dd306e52937b75187124f71a5796bb9a5532d73b768f2b452ba3493c601dfd3599ff3ba1865b6516c0660c7735146ef782eebb2cf52afb37d529fd333ea3728cb08eb46c0909ee394475df6dd909ba5d1f174bb37dabd6f103bb85b837a7644bfb533baf30e2be7149e17001a80e99b4669adb5564dd3b46d661e663ffbe7fc49a38ccce71c8d628b37cb69d6794284f87224be20b9b7094c3f9446de48e5b28b23379fd1f4b7ea7466ac51cde58f5a29a5aed92051343d9bd9bde1bf002dbeb40cdb2f46adb39580258d039e52af0741404b4c80c110f42d3e4201861e3c51ce7f1fa1008392f788823edc60a109e8559228c9d4c287dde3b082723e1e096221e1e391270cf9ed0815b18f47a88031e1e3112a9af8cd84f5ed4848960e0ee0d01a610287b4f5f1dee09ad6901e3a2c1c2200388cc3f01707fdf39c188f2549e267b7c159f8f5ce49cef9e62bfcd58a1f3f7efcf8f157fce42657f1af2982e2268e3d7eb803474a5923cce3959a47a7d43394cc35a69f78749818a157534e4bab4aae064b409d8aeecd2154600c9b2bed65336b03ef797983713fc0a69cb9d25e34d20740c0f20738dc5caf0e8739ea7b2eb3bda1acd0f40ac273fb6ab06e30d3abe64d681bc226a056791988124f488fe12dafa099825a85ba51653ef97083d99d5645fbc3bb343c19e2781b53ced4348c4d39259729a7e432e584a61c3cd42a1bebd54b25cf732f873870bc75d9fa7083411106a1696da8799e87a0e7218c97a94b2c64e379b59e57dad1d01a2cb9626260604030740f4f21ce365e0a513c64ad6baeb034e5bc0087db06d37ee66af433727d3b7e6db04d681b6ad0c63c180ca664488912424a3ca0180cf67abd866041b4a3b693b7864850bfb25b4324e855298e4b560e4e7c42908f440082f32c3e1e79028f17ea546f4abedd731e44507243042d2c79194f60070a01167b180c79eea5a3f501e0e5175e90912577091c7631160b0727a7470f2238af1a222f95c45ef572b843d2d891790087da6b532265c86fc7c101a6051c6a2fe91b6c7a360bf8c43d0f37180d2f9fc05a15e3091a700d11184cc99012258494c4622f1d84c11a8cff72f17a0dc18284940c05bd5c6e0d911711a0a016af06a30aa747abbe86484e8335447adc0d608f87976b88f4904782602f3dc75b4304a7c11a22f139165e4384d5aaf8354472707a155996699a9689c069ad5ce98431d53a5a042eca86b1b8d3349693fc5e7449beeba6e1a753874fea0728a52794525a6bad95525b6b96d96a29a5543a95814798b731465a6a40839afbc989bb34b216f085f518a39432d2c8b22c8bf5a45356cbb2381a699966a5e6d6718c2ddd5e41d33c6a3ea3b32e9356669bccdbb3ea599fe00635f969da48d3368edb469ba669da6834d2685b2148ac3558718355caace4d5b6d464cbaedd1577aa66d6eed0b4cc66147feda6b66d7b2dbad9791c88889610fa171f7f96f0f9ecd239b598c40295512a7929259d734e4aa5943136edd951c66c83a91431cb222d519a65d45a5b279d4b7897886870f3303b39b18c46a35137f56e4ae99c5a6b54b3b48a61646d585b2b8ba914db5a3bd2346d34b2d666a34ccb6cad2fe8087bd9735a3beb06ec6c6b6de6d6adb5f2ebd795762d62a7f5f65a67694eebb5d66aed9cb39b6619adb467d357466d4ba9699c66b32fbea6f98c6e3541f49de63de3a4d4da2ca394ce69eba4d3470d11d88b72b2144e2d736ebb34b293134bab6474cdb99194a34ff32cd3b45a2bcd2cad96f5b38124dff2f187a5f3db277f8549051875837ab6d93cf33003e97c7acc759463abb66d8bb49479db2cf3e7a2bc231ffab9e5cc6b88bca24639ad868892211a366636dbe08a048e8b57c0d1b96e1c296d12eac677b6800e6b89922141413f4a2cf9364312ebd3fa8cf446196de4d169bdd1590568c0af289b5135c3c468f046ea61ae8d7e99bd79bc0087dda3757ab409aa0f4a2bfd018898d829230dca9e545252b8e363a6c7bc68100818559da26a9ebe93f9808b2d615f2d37a76524d2d7cf75d1a3fc4269839e8f48f88824c86b74bffefb04cfe31ff9c8efc82fc92fc94723d770467e351f917cf48262e96ab081857fd9094cefcd2cb7468d2c728b161717e1ecaac0a1fc111d5dcf9c741d7ccd4734a3aee68d50d43327e52e4451964b435e1a37c7e4906fe3d2b5b8742e6e005338e490830d35b0b060bc6265e5dea00d6aa90ebc37bc1f3997ea70b834a41f3997838c4dc23e9f6b19d32a9c471e35cfdc575c1a38a334cdb59287d8350d6b1f4ad31c7c9363cd94833ea769ae951c3ba77dd57109676e3af1d2493629693ea363763c76e9a5929b429486ba34505aa601df7aed469a5fa7f17ef45c0e1d0e39742f5eba0b001817a6bb3ae04f9bcfbd7497527fd14247a3114eb9346ed8467ece0e092323ad1a65f9d3516e10d747a38c22b59156a1463f9d24cadc1bd7491935ca34e08f7c341ad2aae9dd6ad5bc3e25c9479a8fae36d24179ea9dcb53e79c83e960c0743aecc0438c1e54258fe4239fdedde939491b8d48238d7449238f7477b837bcf79ce3a18b716948ef39d703a76ad9246fd5dd7e2ea61b7d24ce07ed9b4fba3450a3d157fd7ede17a3803347913ccb480efe752fa3483246835267767d8b907f5f80337cd22a1bec9a4fef6e94198dbce478e4a64f3ee90aa1f948f391738d6934133e423ece369a8fbcf4859a7bde5d1a928f9cbb37b4ebd7b95803472436d8f968858f489ef0794ea68bad4275f1457afcf0bebc315f6b5066d77cbe7cda7595d4daa84938871a782d8f3e3a6da5dcc4ed5d27ddac759fcecfa1ffc24e6b708749b64cc1c596d0c2be3a976530eb5633612fd99c39768e56dbc928b312008bc3a534d17aea2a2b1d6d950294043dfd4092a76e35f73cbf2a7e5554fca4b9947e72039c8f483e22b141eb5b74ceb9689173b8c29d050b9696951515475d1a2a27575139a154541c7c163fa9b0e4a0bf12836241b17cf257748c7f5a5959f9627c09577c3aea0a417d251f8143276594e15edcc88242a5789edb954b23adf57bc353f1ee95cd03e5d6e857c9f1a4e2d6636254aedfeb9d9ffce4d23865d4cd34e05fbf39367857f8c9b9fb8dfcf4ade8a2803b3f79e7b1c1eee4e0a37c458e0dde8c3a651af051a32f077d9598f8225532ea08fa2a5e84f72a7eafab7c31be842a425057c947c847f11b6e0d1437712ed5d99667adcf48999b64995f19e090027d963914e4676e02445fd4ada7a4e430c557384dd2aaf0baab5536d73dcf9cfe48cd3dcd3db7d73df77866c6c1903187f2643c45c8f7fcbe0077e1c957dce6e42a5106e599f70ab87b7dc54fa84f3ebe42a8f855f1eb1c0f9dccbeeb5c0fdd5f8f716ba0780ed9c9286303c54d36d489c9fde29fbeebf4d2a8f875d417bfde22e49ffc7e47c83fa1e47045ccae0d9b93e790afe22bf29d010e3be7333fe5951c9e7ab44a25db9c5cc5333731dd1b27955cc306c5ef0cd27079b2ccfb47169e38e799cd894bcfdc746934975eba374eb8d99d8cb392ac6b366b9786b5de4519cfad4b0d6f51defa8d2b099ba99b71c05c2098f3b822d3b4eb1a4a4899f8cc4dbe1cf229f58c47abe267948998ca1c049fb929879a093e5bc1678e33bd892f5926e550fbdee172fba8eee3c1486115f635e665e69b8fb24c037e8cc79f6599a665ad661b7b92df92d85bd7fc79615bdf1eb64cefe2caf26833c69cf4e79b4aa7326679e64a0e75aa5d8b7da861d68907f6a1b6810fb597b9795c19e0688410b89ac7b7d321ad8ad47f08d5b12e2a042e46992264ec51ed1ef7a255917a485134d3800f3e0501d15fea33a33ad3804f3d26f1d2b3cc26731a90fab4092d80a2b1b9086923bdc11b84f4ce2f766872f2e0628d204c28f9d2c72898882612e0f6e8ed474820dc4193a5d4e16200a89374dedf7a104ba7a6cb302287e61d921ee07c9639750d3c89e45c7f47685fc44403ebab2fc0882f870cedd7a1f5cc7a9865994d36bd00f50af1bdcd20d64ed85f3b9d30b69aa6f99491bafc0afee55a469ea1dab3160fb03c122037dff211890c602f035754c010ea36739606a71023b7c9324b08fec86791bd35eaa5716380437a7fe050037fd53ee72469ded2aacc2baa7d737faf2dd3bbec9685c442ca2d30c0a81a35e46b19d5aeb9fc7efaf2ed15d2a6e39328d3514a354a297549a097be51243c189a21cb4f9f487800fb39b5b9c2f4b3a99e434e7f417d3a4c8c28a3f9f41ea28cf522bccc479f2604754bdd526a394c7fb695b192ba2f9c78759de73cea9ca6c5e85c6cefb25a7ba6baa91336b0eca1d3aaea6d3bce4b9fc9b38f73932f73c99aab92ed3ec075d9cee1c9de6be98f36699a33eb35c936d32f7542ca38f13afdba451142432965ce67572ddbcc0ee266ea84e5bc65ab55248f73b4ddc8da6dbe2615602ebbd7c689777742f2534a46c95c78e2d99abe6ba3ba8dcda1dcaa372b9bb4e6d0023ba813d909297b393ce5511efdf4ecdacdd68184b53f3eac3773da83faf83087bd33d7ccb930470ea9ce739ea3bf9d5ab7d6723976669bf6ebd6c356f296d5426f6d0e73681ac63937aeb7cfe81c35bbdf53e372f51e1246667293cfc4339f2bd9a30d3153d4a3a95943cc956cdd7c0a4f35778eb06dc324efcd71b6a9ee797b0e658ba6bab76d19457213cf64cee98431ac3d0b4b26d9a4029c79e7d6c35eaf10dd6f2e733ca71e75d3171bf4727b49c2e886bbd32987392a75da39bd34ba4a227df16b7542cac8720efa9e87393a732fdbd43047fded07c7d58c99a231aaee9643f959ada496da502bf976bcc19c9032e437a53aa3cfbf9606a9df0ffc5eac60e8740d94a798921e5d9b5daba82631fd5a1bf63587994faf3b38e7260a88f8d66dc4b7d9b483ebf785feed5937fb8b7fbb2cebb2cce76acbf4f4daacf189b631bff6eadcec1c14e9e2075512fdf4ee495170820fbc8c2d1f18f2d6671420e879998f51f0207a8e76b4c6582bad95de17e02893797ba55e84f742589f517c20c85b9fd38b90f3c50b23a6c7f8123d44459b5131d3801f3d66b0c118da2e0a78baf4f8a1a4c7e933d3805f33f8f473bc882f323a97c9d89d5592a582177b7cfca1ee5e45197248c268cfdd924352868c93cea638ed02fc336d9e722872dd5130934eb54e19a104f2e333a7746a8e73a8699a111e5b97c3e6b2ae03fd271df99c99383f9dd3bac965d98a2fd3af0bb0754ed6d8a4708711ad2984a3ab5fe83dcd7248c6e9498d0a4d73a9b9746e4e23adca7e2e8d0ce286ac5a57fbad75695d663c56baa887584ab7d2a7de1b7293526e595e9aa965d67ab55e7570644ccf7e0290b9e65c9431a3e800cf57b72553a9e42d2c26b7866c7d96c3eeb3cc7d374f84d5db065f1a69addf1b75e4d56dad39cc5cf6cf98dda905e607206a86c081c44d8e0f9e1394be1d4e37d252bad82a1babb2d13c0b2f8dec808fbc414ccfb42b47dd0d9b299d327625dd2c70b1259448ce80e40c3d01ad7ea8f46c35b64d2d1d9a11240020005314000028140c878482e1783ca2c98a750f14000e8baa5478521ac84110c3103208214304040000000000329a2409ea4945a1d20af66fb753dd87c868d9582d74e995b8201d98e32089015d2822a1704985e0cad3819098fb251646c7521db3aaea959c252564232bd6fff703d9f9a9d87eae7db4c781af93e8bb04f272c570e08feb1c0b386726f611fceaa641a6f12dc245fe566910d5a508cb04b6a3410a209f208ee4cc4a894fc8aaee752a2e25054d56e6deca9029e8abc7608a993b74f69e9f4d3b832bbecc802d6abbcf4344b3eb19b06d24f48ce47a790a62fb7c1ab7123539687163538d57c68157abad89bf1b450af57574192adb4f16eaceb09a20081a6ec2719e101b35ffffb49fcab985dda1ef17a6ae996fea730cdcfea9e54e84654d04c9c4c191642a5d6b145df14dd50655358cc37531d467d9d28a064f5b110c662bddcb4c0d961e6088ec420a2a98b65afe932db1b4be73abc9d73f5e35743b536fa4d9280edbc7fac0d71dbf6950d6a2d114ebed720a066d1153673d68b240476fa3854276095f521df21f5271d47ddf1e5c04d9c0f1ff7ac582772edd67b3305eedaffac492868b2ca424524069cd6a63b88d4a6521dfead1040b67d946d0bb32f87b8b9cd9708be896c228af0c2bdc97e8da326cf7361f185a868f2c2ad362df743d77dd966c36f336b7804c43d4f183960c6b8f605bb33b842bf183e821549b0734bb888dcb4474fb6e7d99681d015468a26f01b9e73a20040256c430819f32064a6389c844a4008f4b93daa833c5ba22042152934f03557001b4eb610e7f0709a8dbbad4f43431679de29d8881334604908847bb814a93ea32f6fdbb3ad458d19d16b8de8ccfaf8f4b6d7cf02e04bd437276bd99dc61d3dbff534dce00a724b850554f4bca0f5525cd00718e807bb493d57dd6d28e19def6ed4dcfd3d639bc2131d4767bf1e9a69602aa07209d976e38e7cd09bdd06218c7a82295d2530eb56481de063e7f39c39273de5d40895ee5b4b25560e12afd1b3390a09d55217d8b4f4e2bcc55babdfdcc66666e58543fc6270e6ec32f59e7629b2978690e4331014d512486ca7bcbcd6e252b202e76bb7a74094419e58ce83b13af6204330840a485228bd20305f192110924f67c5d2d9132b4a6c484e1eb94dce20b4b270af854b9c83e32dd21e867a0dbfc89031070d96f0112de0574990a7c24c92da89960e12a5560ef45e02f1b063e532a686b41fd802dedca43dee607abb2818382d31d13e37a9a9c8346101cc86aff76dfa33b89cf6fdf4bec049de5c8932c554369188cdc42ac4decc4b506ca8134003483673096b84c40b11864a88416b016e169d66da4babc915c7f5a5dc6f60c00367729381a119c317c45df837b9a720226ab82a7c4f837a0878dceb3ed94ad4f562598bd0c67d084f0c9b0469638d647650472e8f6e9e0561bb24bbd5723ce2d714b17842fa52f839c2220c07a3cca2789867fb45cb3113bf3e27aecf0623e1ad17245a765e4dbc2cb2be98d76fd8b6a4aa9c9cd40d640529f1013133b62abd8f02a36d09a74bc03e1701f886d1f423798fcb982bda2aaffc03309720cacbf142d368b2f083d7afcca0777b91d577a1a18ce61d5175c180a1591a68535a55b844f0e1344ea5a1e9c5cdefec891bc60ddbd48a4d40820c1f98501fe3daa5122d74c42e0321a01dd214d35238b8560a9470278b52d041ec32172491e43ab01a777e814f9062d5e2f17ca7748b05622e39fbd06887bef50efaf2eb2f1a117e56f6d22931e7091bc875b4a18f5a2ba0fa381dd733e06dc19f0add09fb2f33c392527d04df4d19868305a7c260d1949a654759ccfeffb98418ea8b731c93779f0633764a0a9ccc568e6665cb923e07a25fe2429f1cdf29c66506a3223913b32e4f9ce20fda88e2dd5acb5d0b5f35117298404c31e2802a2e8a40c1ab7caca18fc825c2f9994e2baf848b12aa5b23278e3594c31a1f1991e860c132bd1c31e56d3027f9561e7d4dbac443b4f5a52ffe7e98525c20291c3e3857d0bb973240b8f530421b0149b144a847c9068a1849796be62f74483b731192d8df7f8ede7407f68c53b8398f1178528201207b3604c871d77f562269d7569356ddf5bd20866b2237f972611b0a3d00f21469684fdfa39b34986f26ecf58d2379a9dadb4d5cbd02ee6cdd129bc87148dda4c89c6d500694a95178e99d1b7f63cc1ff0858b63b5d75a765c3c185488e289a67a9c79d511d85071c82ab7fd2ba3fb968655f9fedb386d34f7e58fe250ad3e5d5b22149f1a94458b653ead46afd70b307a099817fa4e68bb15a8b7d0606b8a66d1c579d3f6514dac4eef0b5ebbdf11de9532b978e922d0bfd987b3648a08bb6e84d2c2a9af9d9a2eb8c634c1ad41f791d2da9e0500f77cb775a77ae7afe49fdb739876fcb40ad232019d73677511533c236756550d209ecc10888e215e7f8c3a88ef26d856d574fffcb515803b1b4118b354c07a066f8e208a65cf226cac827d623efabc497689424fc45a2cb771856bc22c151d944b7e5958ca157fde0c788fdab32a94b1c579e57a89928eef9e46de536511edf95415a1cda122503b39c3d42614cde5a1ce47fc85ad365c4ee4bfa47291bfcf9dd99e6ed81935faea1376b45da63b0cce998dc25aa74590616ee9496f3778f9dc96f6a3e73d24c5e690faa0031f1fe267036e2b3524c6427fc4e8f25392bde43bf4c384e03c43354fa890dd53f0c68cb782dcbb30b55bbcb8f374d3cb17da22df486d97db554725cac12a5b99aca312784484ce0d2f0210808459e874c5667ff454e45f078ffb4acd4333097c958ffa6874becbea38807184652c6af360fd0c8833c502c6c3238b776dc874897d4cd1a04321d3c489ce2199037a0f5b62606b412f457b2be33299ae47e3bc76674360c55d092e304360746468f072d4ef30d5565339d7eee4109a70fec57877099223c1559a3ddaf657445aba420fe2c79d58911ac871cc404b0a7d1d23b0d7cd5df824baf903daf4828bd9c8fae4458c2f90f405ed9ece015e673d1c999d8a8195cd40793f781510e664185f4c1a8672b816e4dcf87542d462a6b5ceb3576082a894eeac064f6124d943da603adf94976a5a173c5d3375da6df8cf4744d7d74c47e7454610b0a8c5b45e7bf6f4c2f3fa40e3fe84a95390cfaffc1092d0ec1000a50e16f9012fbac3bd979138e604cab826aa5c23abcf3cd3b887900018228d3440759b465e606e8c5872651c31e87ec611c0e477a62f9510d7aad3f88f427f2678be76ef2d08451e03b8a7cd4a99a10135d789d4bb1f1574c5e01d6f1ad10058e7ce45f877fda81fdce01971ad08350d240b7d715d4e4dd46733e2eb3f7616559c09c907a2aee9796281ee0ca81ec2992a533e1e8a7b496266682ccb8710637a0990f647a7df49864d9ab288ed63c024409f34264f9e6c4b7ac1e174cd7d85588ce16597c474edd000b13e20722fe0d339643b6134cd46f6903eac63fa8b8d672ee21786b240d2b5b8cea0d79400d0aaaec934a147fbd039c75ed766e34c2259d4cca91c96ad2becf9c0f463fa95e2fb5a7b51b719fc63cae799e86cd70cbf6016ecc271f2ead29eef6e69813c61acccceae70188ae74db2ce23a19e4138090959fe5bae9d4be671eed5d8df76effa285d1c7f642cbc2774d815c8b3ab7123605e42397aa754e846ef2444606c0c9d34161b0a8366dde5cc48a6e75327cd2336d21e00151b38b9211f7cee57302ac310b23bcac2b35c5b6040d90124211f2cea78d40608cff48c9b32726507817661a6079421cfd89efa699906395135d33f85d19659a3197211c220d2bbf87ba80f6ca683dd47b0124efa7dc0f86fd76968a1d4498c9121f5e7ebe053bfc94ee0dfd050df60ac1315372e38234bee398b4097a1b57438e9e65a67000456930762b0c539a72ecfb43454255724048e17de46b1461dbb46537e0b361c9a7d34777051c730634bc054d06508af1a054d52c3d45e560062a4c300df0022257a82b9c61c266a631740b458b2959c3c4571798e338a46cf03f5d5185fe7e1f66b20f1ab12caeed231b02c83911410ee659043e78b1804c5f7c176b0a72abb81377d443201717f7090f12131bbe568e49b663f2bb9c962cacba00990c66dad60858a4335bb663b1c54dfae66eeede0ffdbbdb9f9327e9242beef1dee9b8f7b99cb476afa6223847b502db45cb5416b377675ea907badaedc032d0790f049c05bbabd116e4e434545e119ceb212389a0794a5be6a46c3d2eefcf9e2d5148b9f98dab77f329fa645afb232c9214450838c96dce24402bbd64f5bfe267e74db38cc88e26bc0b13c97b87f79b270930001a7fe3af4854654110dd4aa3dbe00bf1315a865c5e3fab85ce94a038bd125000e7de461509c8aa6f4c4f3a3f7a5f132b4a1641dc70f936d4675ee08510399d071931008954592fad892aca1651bd6ad8db16a64e54dbc24bc882a5bb5d4bf1261cc70d3165af4164cb6421c329c0b5cbd2633b8d33a6a88efc7d4fe1e69ecbd4974387c490bcd389a17a6d302b882f07af99c95cf78972465abe3d91957c5fe9e394b8a80b4e84a23bb27adbc20eb0737ef7bb2ae05618d75260d7c130f0b5b0d4a5e8811dcc5c61b2320f75b91f802bd2ef5c66f726f0f9d97c3e902bcff993ba23f663e07c2371d7f16986b65ddc7a72f5c09e04e04fb571406265f106fc59f59ecb26459bdbdec918e57809b137b10a3920280c94b0c4da433b738410812c444e432226e93de543526ef2911e800ed7e487bfc26fe5235ac707c2f9515ce082ad5622fc118ceb8e2227d920ec4666122f10a6e89beed4c2fc88cf6d68a1437e1799ee454a5d6922bb5045a803b7d2abf6ec0142c9579e783913504f05d1936daa05e2862b9eb03e56667f3cff24185d42ee43acf614b7c18aa896a442f86428a88b1940436c87f4867746dc782d1b958ea214b4925bf35810ec561a6db061a4e8630332187d88643220310621da70c6906838e2dd94a20904c352afc42b86f9f3b0b7e2e2fb5a6f52830f0547f81bc1d2b5bc03e66d670320805ed769de4e10eca311551137b773c5508822c711f237b5a30eeed66ed70e2005568d9ee1338743af30dbce5a281142434f983d7e49e9f8832dccc85bc0feab0dfd2b255a2a340819af25a2b34e192e6be87d2dbdd616b575c5beb12244c7851a5b24178b60c7d2926bc875ae6ae081fd9f134582771db5c67d1a81542b8ac4dc9225ced4f5e7c1025dfe0b7c27e3e5e59129ad4b80ea4e9e2139b75f1d8d1fb74ef7454b1eec1f3b41e7a630ac14866838a5b3745136a8f4904661967edf017114ef0f20c4d7d7c201ee6bed9452e480447ee89ed65a09e9d771ffb432f45d0dfb6e214e14f05ce4215cf4d83bb2de71d014a9a9bd4e2a8001fd026edcb7b3eea3c118f25e0f80d83a1308a21d5f044531f1e2f383179ba4221c36eab049f71351e882d65adbcee1032d835b3cc2d23137d209bfe0e537e7e11f387dfc3d6b21511e17d1812195c85504b1547cce4c6be8393f96bf8594c3c9dcdf1ba800d25d9dccf32101a2cacc0525f54cc9e7d8bf708b49dff25dd028d64defa782ace35e82822f88b769c43d34f172d1e8e0167c5917628de25ccc46f8984e5d64aaa54539ad2dd2b9f3f225dcb2b2f5296ebaeab286e3ccf882d8d7493cb2d59991beb145ed0290cec33aefd09154b863d4b4a9fc23060da21bf63423c8890bc2658f33dcc57fa6c13fd10d7b7d8701cbf8d333664af01c8a082bd6261e7163f10bc2a46c28a04eaaaa0a89b7a126522133b65b43947a20f2c0656ac19d05c22aecdd4820a5ce44c2bcab6af77258bd952b23b0bef805d9720aa3aff14ae5b4430579a14c922417e72c6f4481327b67adeafd550abc22554963bb8f1a5758cc091c4a410cc019faa075b9640656c5e4be81e01a60bbaed50bcd4ca37cc6f964201fc997b236520fa14c6500e8ccbbdd88906f6357060ee3d7a41e57c9e1dcee77c9a4139cddebc4ff4b704d8c616147c0f37518d8ee6689451853b18c9ad46013136bdd22a7d956acf74531cc0389f60888b5649d20a8f91acdfb42ef6d11f1e03bcb9b83a0d401161834418b0d899b012f7b71a4f9585926517d6780a68a30ecc8d1026e3c5a1e1801be61cabe17b0756c140306b848788e4be6a63a5dabdab2be78b8ed8289ccdf33559c599ceff57a5d6b3d30bccf48ef92406c2a31bdc0482f6c7987e48c18f95334ceec954fe84a33961b1749dc9eb668aa34866ef5581f51a6965632e71f56766c5ee6ad87a997f9a2ce71808ac0379de373d4c2cd731ec7bc89a173d3e23cef662a5c16c062543bf7f7c24c3829f272672d758ccfedec42bb86e8de55b81de55a8e14fcd40c6c34b80dbd7eff27291a7aa468446d811e37d9bcb3326b6dcc8c1e3fd3f6f87d92f2452e1631c77f989734c3fb24649cae464d6fcd5c392538097dbb681ae1657f181c577462a57b1caf5f2261fafc83ab685dc0a622d0f589e062df819b68e2aa74d0139b15ba0b53ca5d9bfb3156dc0808169dcc759714a00e25b5412320f30070570f4c1a343185f319360b938e084964e46cd38017291ec91d40e98cde77bf9352a2670bbb228ae04841d56a4ba9cadaa61207882ce86d1c3d539b5914fe1b69f81270e22b8c5b3317dc9627b459c540019ba0cc122a287d185eb8d923c1ba31f0af33e5e2fe1fa6020cf4ff51d54b14d831bc773ae71545588bc66e923a321a2fbd674cf8cfe6737a8256ea5859e3f348e74465f2e703c678a49b0a42d6ecf11e6bf28f6835d799f6a58745ba848dcf3c266818b56019eab62490395205890d85c02917a7e06ad4a7654517d3431bc01549a48e35c2b9795a1cbdb68e5450d47a5bff086c521bcc3150b0afb09eaf617e7ebb6a73608ab6a34b0df54ff24fbf00f2ed6b4723750740ab95278d0c8fdef82302a830efe03e631111e8bf7ad244d4a46c2f1b3d5888ebd0c44ecaa4789b8ad39977719831614d3d0f9bc8fd0e9ba774e92d6cc5aeccf561eb03022dcdb88508f531a760887ea12ee63b5dddafcf1cf19e220060cdf5e964c07507ddc580dc40664177c904fb8a1544421a7c67353a27c32ac4a6430b0e81dee521432f4682a4ba653fd1c2de5bd0473e4e0a0b9dd64eea84960d5058c301d99af6a10b87590c1d38b28b1bf40665798049823596af8303b1e0f45835c6d286313b55fff2c5cafba21ff7def4984e9be45ba397f8440de61a03453346712107afcf74b9902b354e7cf68f88e2201600dce42d949d0bfae92ca205853fbd6da93bda2b3716a9fcca7c4021e8ddf0e468d8d564e3154ef42760030819ec993547e9f425cb1431e969f818808a2182d239e50a06274900c14b10ed77754102645959d0f27c062a204a3527930a6d6630664c7469208031caa10ffce54b4f8c2f76c00ae79405a0943a1666b64c5e127443a314995e92177a58e3de540014d26459ae9f6136c0569f931ce1499652d11e1600a9c78ecf17eeba05dd9cba90d73640970fd24017275d35fc13bea25db976ce859c4289bf0150c207734e34b0a3df01f1697cd05bd21a12b774cf3ce725a07344a18b4943027092242a45dfb4f353252b980e343341173bc291f154dd48f3115a00e6b7bf8210f6fa6413d9182d8b42a3fcc10a392231e011caba282bcad1e1fb03eaa2404f500721c5f453872823b721358673b9d724209efe5d12603f394d01da4b9a1090498531cbe8640a8bc4bd16f79e14e9ca4cb33e83b07d6bba9caf2d34dd9b095a551c637a0a0ad4d8295b228bf3a60e084caa8267b23710af41d143a8e538d9b1882cc793cf73794316d8075ebab7be860009a63c272cc80e2d86d0c50574741a1953cd92db504e3b284b9e0171df000aab84c96185e46c8353d05b285e249753a8fa05c7f7584e0874de1bc93785e12ecca4aee381aa9c0e213959f4422f63a486d89bc60044a2e3528b31352354f7c1166413e459363655d7a2f3dc9338b458946dd62617a4be58c44bc24c70a871cf72b3d2a364ac9ad191c7f54af56fcd9d2c7cba7669a451b016e25228862aab5207d5e32818d0fd3e1bcc06d2bda05375373939420cff52a396138a00725cc2d2658b444250ccd8942147e10ba8c09f945c4239adcd00a19b2bebc9690690b76b0786d6235e5f9b01170810e6af00e020a85564d5c5ef27218d9f96cf81363e98965e003f9b82f5f3b857667d810b563df8098a875f8059a07a4bdb527c5061395eecf5855a49820210401f8e6ed938709b461322905691e79a4850e66a05770e385a6c417d03e7be81d2b0fc2cfbd2016a73229498cb299a0efb8294111bd948ec7f31407b4a769a0fbf521e3941a0354c488664078f1e696522e1adb621ceb11b01f87cc0272795e577cd9f103659fe57ee731c493b1fec47c4adc0caf51ff85eabce5c6c59ab592c5e0d1ac4dad69585f1872c1b2795c5ca0bdbd63da03814ac12d04ea5f5b78ca0e6a623f9f73e35c8fba7413a810dbabb66c53c973e24792b0663aa78122f3e43a8cbfb1f618afff84ab49c391a04a24055129059111cd148e8a27ac2627e04daef9b67c7fc3fcc78fe80536352c7c81e76047a5020436414e9fdf8ea7a9e7131f2a434ffaa1dd14e6310fda87615ff15db99ffad76db149758e7e618184fe6f2123981fabc3efcb354e095bfd43a74437e983f0a44f6e4795b302edc8d92992b09ccc958a2fc3f71407bd26aef46a258f6582c22bc0506e9f2318374a56783746132991ae379d7121b85319537da4d4603d74d9e79ba1c95a2c55726f3489a7c4f6cfc343d7c220cc00ac1feca41409e8ccc10ceb83e4e73ce696badf4965b9702410902b8be39321958d67855ece73da0b8e55afc6dda6d60d880bf13dd165d6155b13bd557f1487fc2fe9e762d2cb1e944b6f9c2f4e9382a6b5d0f1cf928c5fa076641092c1644a775ceae49ade4895d04a50adee4202ac3e7faa5910b7ace1c78a2f76000e68ab1b43cf50108ac8451ae12ca6cd01bae2f75bd92973cc98fe0db212e2544324470c0374d45e0f7a95598e8492d317da0427ade50f18192f6aac50378bcfe2c74bca3be39f217f3740d231f176e9dba10faab8a8300a637c4afb5edf5e3ff80e01f0b03b9cf8f97ae50266a4e419520a8057f0ccce20bce06730e3b9f396e737a263192c9bce66aea6b82412638e1ecdbff46d1f35be321116fad3c62b6be94e3bd5f4f3cd923a05e95ceb3ca9fd454f6cf867b335388636e2f608a7c74fb436dde94d2429447f43d2cbe4c9f55292e2219f2efef47901b1e3329b59397eec620dd2f07cf16a6f64a4225a13e7e39aa51e8c3a039e542d906094ca9aa536bc9123f48d8ddde6dd2450038b14baef9dba2b647451497430431874bf0300cd6c2b4a99a07e328e051bb8144ea2992b835907e45a5075bb26b5537f19a7db02addb4ae4cce175a5731f9ad676dbe5951dba6f5e7b9c59add65f0d276d2dd8750f6a86adfd1fe7e0dc8276acbc8980a9c9d3bea856e3daf6734a007f1032d1d0d123a7ad039995fa12c8232e189c42b3629ec240dd0b94b3294a196b7dae07d88bbe3896dcd28168eb9437bbfdbd154d712ca7c0b306fd1171204d488191b00b429072bdb9b35080c0668d974874f991374115a6576527ea66468a05b3d7b56fab1d86be9060eac9b982a721627461705ef141fcd743881d84820d2e9ed9526b6c69b80a5d8298c0b750ad7558908fd950274092e0c2dd4b71eddad239cafc17cc1d3c0ce7a79498758763366ec5cf0bcf2c4623422141f8511dd804773dfd53cc9cd192b56ff0d8290d1bfa95ef3aeea4a42b8a407672d66a534d8a64ff1c234f29e1ac0ad623c494abdbb1083f21f801c8a6c0a60687de2c934aee505cf5fff9ef96d10e2ea2796399aff486c843cc75bfa00dcbb864f2fe1053ce8f207e79fdfabb4461c375d1a5ac0eb2c444266600e9e8e1cc25d71ea97ab01ed7bb12c52e3b23acea384a69c29499b8841e48a5dd1874409f52a16934c641d54743fd473b42ff96f1b80a9a0cd8c45321480cb7ce8d26c186dd0ad4ea326de9a3ccda1d8d2a2caba141281c29ddd498d9374e1402d812a78cec23b83bc929208ea8e789f4f52f64136cf741670768fb9a7ab3de3d7b61d7d1a8510eab95cc2afcdc63c9a15c62bb39ad5cccb34a093efc98e58a273578a81161076f6ab1654032beaf30a8bb95ac3935366ba339b0519b00297d6c4cfd6c6b73b7b8b0688a124493d194b02e1ecd04b1f11f740b828752edf7447cb047ff981b2528bbc95ce66cc6cc478ae19f3572c49752caa6eeba79cae2f6a959a8565d09ab7ba95231a715cfa81aecaf05d5934cb786fa2b27396dcfcd138faf5efaa4c323823c9eff628c78bc059e7f02fabb64f2b2bb435f4a3962dd47cc8f3c39c93fb77429431cc47505db804be774e50e90ecc220f25f057342477c1a3ee5a51b945a7bf8e22936b741858886332ed00c55a0effbc86204441d5cd2c4a95e246ebd50811cd415b6046b225af7918b55d0cd852e6811a6c037d5352e50a6add8f353c787a2f8724d05a02a70c1337daf5cd1c1eb669745f9af356d9b992033946eac46342c287f31d0ba011a2b9be4aaa55257da91444c568bd7bca1a3a5278129c6d349843847c6cd8ad3992a0d08b336ba0b0dc1fce99cd8538729c74c3602d56d31d532b977d3c094baf1f542347e739f8ee8a11218fd6a017fa872fd1d5393341c3289ca7088e9159ae95f916e6f03b2f328b8574a049bc88b8c58264c4bf86668d7d5fd79f7e22d260953dc1893a4e02febfbe5c9f1c47921b5446add7999108adc9bc439f21b54220c9dacfb6f3802d20727d785012fb3e462e2f3cb1af9d999f35d3129783aef7a0c238d3cdc00729ebd83a5670637862efd8456c7310f6dead8270981ee08b6bc1480e641fb41e39603e0987f70426aa3f8e0c04b6ea3566ca49c454e964747e301dbb2b30ee708268b2fec938fcd1006eb0cc1cadcc66745c8e5d6bb9bd899c370840814dd8fa1b92aeefe7ed4151e2afc32ec8bca81d91a0468067a142bdf2d08f6722eb44818bc82b8a49ffd357c6343352442f1f6f9d518f65f47dc04fb6aec42462a53a33ed057a82537ad509ac7610e27ac70e527fd1f09127c2ead3bbfd28cbd0789291ccec45a9865f700ee1669a6882da973ebdba414a13d42c511c895214e776d53f327b51b892a01a09dfe6b36af8d401fd7b7781f3f1d75debc968419cc478410b0a0fd996bd62959d19a6a2f229d5ed1c7aba01dc94954bf0df612b6dad87d98ba2225fdb28a950b0be7aed89b15650b544e7bdfdad728f2a31d619946ad686477ca508a5ad09b3691e297bb1d98beaf3e76b8701a338b24a8791cb63958b12c15dc5a0635c59a504574db16916b51d0b199f293cc6918512015869774529e720298ad4771bce2f33e78467e8f5fa1fb43ca28efdcaada3543e98f02e7a289c2274c956f58f3f7b512d46684a1638512dc23c96282c414b2641e44168fc364b1a0825d16f5f0ec99151a7a13d2b461d4fe3d955f51548ccec453d647d96ce290289bc65e8a6033d8d1ab07d2371ad005c5dcad82b303b07423c06a1913b1eb948b085ad707b7f4ca768e88075ee22a1e336f21c25333b145cd579fba4e168387b51c248ec505220f86f6505ccce8a4eaba6608a5ac16628c589064b2e500721a33c4223c8711f0c58cdc1d88b895adb158c4f6b73d92c2158acc4fbb413f57cacbfa9e0bb76a2e62bcc4023cb17622f2a6a610cc8086906153b286e0783a018cf1e270c2761068ae806cd9aaf0732f2c5884f09ea6c341ad63ba454097fd74d103705f6de0d3ec567a4754e2069ea8b8a809775c32c713b3298b02d4afe81830145ff59fccc08b9f73b8aa710889ae7590542a035975a67074a6ef3b8596c32e52c8617d21e4a519970e85cd34ee2ae7d05e661b1448bb264dc6bd3697648ee2ef0523302ab3ea2b32812b615c0f3c0e960024f63eccfb7f863981ac094f3bc75dd7f0a483abc7b0ea3b577e4a49154f0fde136a44f6e2c9538cb1ff12f0af79319f8fe72067d303577b35a679dc8eed27cedf6960462fed323cbe47d353a043a511cc0bdd1d1e9c514dc3c58b8b12242896cae37c10bae3fa7607035d187ae4d0472671b70adabda42995fccc5f47e1fa04029c229de351628791b61fd00ea38d29d9ae92f8f163a51881268034397ebbc381223cc3370eca9ae39876650f3cb0ad8ba14495bae58ad1f6d55ab3c00b8fe61c355644cb3664901870824f7a04661c9e6408372c40d4ec18b474bc11009bbb8d1a07a20001015a515546e00e373ea74426d508bddda1e04962509398d2bdfca8bc069ecee865fecf10a577e99b6a2cdaa18700f2326c4c9791cbcb6f0be8f9de7f2cd7c365e09c7c546bce361333f68faa6c11f6bd66c7cadafdbe2e7fa150f59267190f66a5b53ce651c9df74bc1d36bf8d54ae9ac0b28e31a84861ada4bc3d77f82bc95605eb7d6a7d393819d6532b556215e1aa7be88f405973e015372f6daf67679f92a78d0d2052c31872f0afb8c7cd7ad556a7e75e3c176a0fe0c4961f61262e8fc46d541b30ca0c89e73b6566a4dc728ecfd3f88871f3f36212d616140035b388780200c0d593cd3773bebce072ec65b156e8ef91f5d3555805cf527de8c847241950482c9365a21d6eff14db05f655dc4adc7bad55bd792ff3a37d7aa9e64c8252588a61e3bfa03db338c6f369e39e5d92ccb3399c47d823ea131cea12d5104f27a31cc77061412f592172be4c32a7f4b00b5c52f416528a08fbdc30f89a081fc35e65edd35fa37e7fb4ab0b024eb3dc69b9f8968c771f9e61d8542812a04d11d13b7ffa05641a18fb4253fe66b2af2c2c6cba86285b9e84f6ac7354c15e5be160e346a721a0faf48b50de2e19a62aa469da50ff38d8f215cb0de45b22f82c3c89ac008765a8bfbee97f78fb30148678482c5a0d9d68158b8cc8d881ce1c7d1e4dc91753e0752caf10a6733867967f9d4220b011d7c21510c9933f5a83c1d3af60d6c0f9ac2d066f63200d5e912d6adf9353c34f28d26a0ade17c6545861fad7a4868f7e92518c158271a506d7bc195cfb206902a002e324e293055cbfc4dc0c8e6fe0bfea9664bdef759857c47112bc7b7366c064519e4eaf843f6982a4fee84ef52876c01cd4619fd8d453f73555b384059f926202742c76539f5494035fee7ff502e3b058f04f56fd5fa60a4fca6da50156fa887f081033c0d1bcd033c110f3ce198660b8372aacab2e3f6a94f4c06e630a85e1435c5f759d0c217c41aedabc5fdce6c9401f8f91f7ddd905582e945343c969915bf30b59133304f3efde3d1b64213678c53b70946f947815d76e6013fe6cd0cbc738accff8934761047e9651ab5d212128d6d49ee8ec641585f1997a5600e43fa33530cf13950f0985c11640876a5d32b4ce9b4914f117ec8c8f37824a425a7283d1f50517bc5c37b2f683b5f1ca4929d6ab86f485ec167086a1e362a087ceb0d7a2a429de34811358be21c82e790806a5fb8a9426b8342249167e86877d1f106ec60ca4200f613bced48b4f40b3d495ca77d3a06f66891e081831fe83ab0327272d6a59e4762d26143605bcd6b0d5221f248201797874279f948804a82d7991b4f8ca6e8e5fd5ba6d886a29b8966c21a1c072820daf279c87b43b82ec983ce0904f5a1faed26c3245fcf1d4beeaee8777fed75252e35d901a7bc719a87b6b2d1a9f774c3587d061673a406ede4bfa6f84d5d3bc375f698b6ebb5575cb3d3dccc65dd26beca694ca1c37b6f7ba73941c87d632886100ecdcf69dc7b785ce049b084cdeab21b8dd646336ade57bbc1ee83950b4ab903f97c22ab9a90ef606ba33ad078ed78dc75983f19c85d428931c44e7208c8ec81b46f6b5766db5fb2a498300469a403aa14606dc870d5393a018ed27ed33293c876963caa03cd9a87c41862d27b6238f96cb795f3be1f985032ee2e4f4ff46a5faff81c006c6d6ee03956e7bcb03bdb21f13e80319132d5d5ba57c90210ed8b8b6ff343d3e23079ce8af02154a5a855f694f1c9348cd80ca1d4d9742c4868d6a2d4d12a40a257a99d8af2b57cecc21c91f9dfc4d49786961649b580fea6ddb10cf150c70d4ef89db50c8f14765d0f58e608f82a40134de4d66df89a13164a042c3e63bed7daf23ff322c9e273035f4f65cf758f853c646efecdd0a0267a79cacac78fe1ff415e863c3f4d9225755340f23c7227af162af33c9dc0fa16cdc10f444cff0efc00e5b62806a58dfab3b33dab982485e6eda8da39af2f8038363372c8e3fc87c52834850839714d1a563dc03bd39b2fbef173a414f0974c73e26ae13d54201d2d440ea5530e33520620e1930d28d68d7d3b845a8b2d49649d576fb1f118eefa321547855d500302106638e709c6f446f4b411adcb6661da27c29d0ece05341ebd8d0dec048dd76fda457657ebabd1f2d056d0306a10c0827ff91b2c00bf2e79a746ed7132cd5e3290d0e38f00ac1d8ae00bf073caf50e6b8792c43f39f6bc0b54770f9566cc69dc4946e1664d5c9406241888fd803f784ba846a6b4e37f5ba86b51e082edd3be94a1f9755108c2ddc909a1b0d9c5cb84cbca0d6ef106dc56c46da8e38da21fdcbb8d3762e50acf8b623f2aecfd06972f206997e4b3bf9fbcff90a3d8fc42401f80d788dee45860d17ab0b35068a99666bbd162f83f1c84423cf000e0994cedbf5db9da1bceee529ac462ce5cd6a15ad239dbc191910b71fb6ec5e793ee8806a658f107a4fe53f83ffb176439f2f9c7f925d17edb2736652eb58e145deda05b0897a6616017ed47de34870531f5949fe465569fa4bcce097720e3c2173a04d1b8c23ece51ce8b3774b9c7dbe1e0d00ce2d2eee2c725cfa6ea54c9152ae5f07033551ac161e478b64f101d2d5a8d9b5171e181712a30f95c562c2c03c444cd471b085803f0f6abe32d5cf21651865b11a65c671a835f18a6c842e98b5b8bedae140278a6a93a224c7eb8918b9cc4ab1639b981202554972a49db3598a1a4abd48c71627cf69109e8a9ae3e823ab1824fe1830e452966b6240ec7c7895f7aeb44b16f0dfa0a9b5d84d075a1eb6ffe42874743138b2a16193fd14147f08bcf27e8ab48cca444f2706f109214a741c0b885de97ca4c6c1f09ef3e4612dd1164fa7c2344e0e00830a47bf49394302d0bda03ce337f25956ef741f080d63e10eecd0726ca109847edc45cb8b1a8c54f4172a0066a4982b0a96147a3cb145814617772bb6a40893500f41c3c12457e2468eada0dfd1674a6675273e7aa8e3998874a37cac33f5c86ada9bd61c9c6fc51aaa3e80d4f48bcd673148b09984ac850d7097c56f09897764780feb5330ef91797f0e432d1899464f08dbc7cea608009b22ecb349b3a7dfe4744c34f760789f6b439533b8ac0f4558ab2b1446495b1cb413680b333a3c223dd86c878ac93a24e04b58dd5d3e99e1f8ae3f8e4a539e4ff347b5e5db241c64f57c4c4f656e71495666f9e9b9f68f899763da9490d6cdb357ee953cc2ba1570a8d2d74a75592f2335b5227b07b52122d72d680bd68219b5ea83fd97175be8db888db89e0946357add3f91009696448ab2a5c75258eb7468388502914c0e0b480039ae13a3fdd67c1a32bbedcf9097d770e098ee83a93a94a8a27fc76ec9d27a458cbbaeaa12b31ff4f859f422f1807d7d0749bb4b61bb7fa897de2f9e94bfa20a241835a27cb71a9ef33d081ce7c08d2915511859db2427e776ab3f2c50cf20a10143d05c486513bbf9098bf09be6063aaea509d4fb70a5cb7dcb4b2b7bd597399f666dd344aa5a7ba9f380c816e6e1d0c9d954d7be962056eaaeb83a0a709d367a450210fe249432e28ac38950d46a340e2e80d600744602f22830c66748ca87953f5de819fcdbf51b5c5518fd4f1ba3abb2529f10325f07af9ec16e811d3eff9f3446c5120a1ccd646ad6099b037094e76a4fa2e3e1884efd05da60edb4b4131a531e1ccb2734070540450d86227231f3165457a3bf488c2303e7dd205ca638038ae5a0383325ae5e9835dbeeef0a37ab9d1e65377f18c95ce6207ba35df14c4d09a0110d12963d2503c2ef2f6e9037f20e869fd31a5b9111063d788b2998a5d705f79dbf57468a20883b7541de979362a5dbf1b1f582c3419b592758f80d2e6b44f0ea2c86ff1266edcbba8bdee3adfc3be109f26017e9c879bb6088339b7de0e2aa0b07e67da540a28cb0faf3cef865c24fce4015ffdfe1233b9ce80b5609c1d6e45a7e1b161787f0033df3b6558198b3c2c68d3a24c8ab21f23c17d1457fa1cb7f0b8a247f581e4c7c230ec4ea73cc5097fa91f9d190f64ad8a7a454bbb7223f0812d2e8dc8b79d3c14fb4c01d66121132ac82e386e4ae1d1b081b2d6e64b95b2085f479b76ad6384bcc7892b166caefe1a7d5323b3c1d39879a53642cb871bafe1ec5b6eb6d4a67b25c17a14bf945df68fd9d084b4f80a19c21da96e9c1e8fa717d366bacc2c20d6a238d3b554ee16c71e750dff88dcb6640f56cc60c95ffdba74f971c5d52133c7d9616a918edbc95f60db85dd8994cc9efd19683f47fecc546f1814142a33a5a858aa3f774dc2bc89d46a69d0daaed281cc6b51b12849716903a23efe04fec71d9e177253c9bb0b24a7eff95e8892690391d298f7f4e77098bfc5f56e3a6a3bd94813a25bd2152e8d8be7cd8b8be6818455d6565e007618ad13c76a3cffc636a73d786e09b15547b3d23ca72751f864b7590cbb3ff568c756802cf4a2c40512a988cc322e9e083e3afdeea31a19370685c644680cdb31c2f9e4346e825a8f895bdfec021434d78510fac3bf8b09cefd5b842252043210ecd26e77700999f254617f4f045876f8d53e3d804e86bd804050ae7ff4b95d0bbda830219086c1fe6e31ef638874e0d4470047722ec9fb7fc2608cdcad8c51d6747ce2b722a11b7574db638f8a88d08dfafb9e620f036c18436d111a4a7b2bc4e242442166b3ca2a957781ea604d7f4b2f1bb6c63a18aaeaa57422ca57c678ddf6036cebf83515095950e7736a55e2b0b87b3bd53dd3dc3979663d7b95280801ca2e838bdb170cbe46fc75e5b9ea50c8bcdc39a3d72907a4c626c5d3b4def8c07c9fafc6a9d4b6d5a877f5f284a3d510815d3b31ca1b152b5f3e44bfbc8349c44c8a7f09fe361a171d1aab14ce8199324f2b7a215aeaf8f87d44ce60a22f035d01d0528ae6c2dcc29d104856cde578d6b405415f224478757e3574eb1cf532ea62bf30f11a09dc84fc6ec12df378394acc800b715f88e2402ce70f06da19cdc381d801f729a64c4fb26df03a695273b5f7c5aab8e46fb1eae317478a2060556f6e71fbfe5bda5c2ac82f848efa82dc82c2a81124fb5e5463f659fa31e14792a15505829bbb50b4b6fdecd96b7dbdefbfb256c27a6fd4c567b87ab913f18cea289ef1ad34a78ba3c9dcbe70e0c53a3c2dc06fdab3702d1291d6c0ec7b9966475d015192cc19c27dad98fc97bedf92c51847f4092f30d99908da3bb0a7babd185704eb761de954c94764ef7500a77bdfb3998b760b7a5a34f69a4408febff50ab3587af60709f97a138e17702a10325670bd02689ce5d5496cb70b15106f0e1698d5b8e8de7d1e7467cc929b21063140ef64d2d290d8948cc9f128380d26466b44c1b177aa9d0c08b39ca3dd9e3459d9d38389109e0e65a201872f6bcd8ea21462c236b88732511df6e8e1335db19bf834b44e9d6729009383cfd9c04e66c68a248f4c1325daa2beb9245e2c8384ccdb97cd7041ac1dabc915e1ae3296d1031ad744f7b625b8d9167d2efcde44894c4254fe4b50fd73d32e483dfc457b8341169aded0d2e22cbc10df76940f9f266cc355c10f7c567b045286ab082125a4168fb7de1f430124779d66038aebae7ef14b9b3e843afaea27ecf1782cc50f739a4e77f1498b55cd156a570d9195da37560cd15aa089cdee3a17f73e4d5bc9ae078958586b38f00cad6cb3d335a0b7f83ae4374882a05cefcae1edc53b3742bd079c8c0c14c6ad3e3d98602012799abebea2f292ec450d1579cc467bae105e4b9280fcf423c1d8caf1f46b0084a4aaad4fb92ac66e5a358b761f0176a8b85a623df8fbdfe7cb00dee78ca61514173dc26e6d687b1398f5f3902a00fad8653be735ea9b8f93867f7ed811173c5b999d411e41041de550e58843c6ac3ce754112d59b99cf27bbf516085903f0f4179c48731c5f2e0a9507f2e613504fa80caa017cd3cca51eab9c4ef7801171ae46114773cabb4cccefc1f1f367280c8dc83a64934c9750d7f3f80c2ff3b6ccca8084fae643efa0d26000ad2698f5dc87e53c6a13398fbde3b6b60e64111e8b6798b4e4960a9e6facf51fad7ec52e6bcca4c0a3813aa6a3480024a48be74d9a81faf240d7788940c28b6f54e520fa5e31ca0a8407ad670b3d19db676080d40719f4b7a1175abf40451a4599da0d15b7f5371312b42691114618ed6f19dff3add042a7364462548bf864066219c738787b31246dcb217d549b0b57165e993cba242e20814e8136107727a9f230f61b50325afc366d6508fa8c677c1253a4486a29a267d8569d001a8ee687bf55fbc6995a48bd9a4500c70d3fdde2d06c142b1071e5d40b7627cc66bb68474c8a147bc26dacbdcb4d4d22cfc1682e8c18e335b1ea3e90d8799138ca842484b452ee94c7e97cf24cbcf8156244e4c79db40a08486cfaeecedb4c676c804ccf99bb528e9f96985807f53b30758441e1bec012de54e85f4e1321e0074d4e61b3d4d973f8a84f04615fab04d3bf75705724a217e8dec056794131b239c2aaf9572480dac15fa8b2790abf53faebeb9ee22f73c5b77b12c12c29a9a8526fd4fea034c327ad67fc488f3164dc0fe7d649be9ec15e38facf07e5a2fadab135abca706dcd2878d11bf8434b0bfdd97b62aa9f81b91a5370d530160ff38b4e47d9ff626e6efe952f966f617aa0debfec1cb97216cffd631f2707511dbbf6b2e6f0f07bbfcdba3d8d14dac260eb7375db1a401068025a9680d42ee6f94539672e7fe8fe520218e81fa7d3a631ea829548886e7cc2f8500059c898c49de5597dfff4e4d4ebbf1f7208b8c4549ecbd840d7331d6df3526f9fb110efe55951fe6f75804f24f1245575f34b92bc7ec3fd3b8d56f7b3aac75f9067dfea44bc3cc2333a8f0586afcd251a2eb2ac5290ba8c1cd5f4fa4f21e4e46c2079759448d7412c1979b11530fa1048edba592764777adfb6ac7d5c5b33d18f888c8dabcc43983928b413cdf984c41f59f0527d31ae6593c0bbeddded3c5956386573967ecaa1c71182298e6a1f91ec208e656baf4bfc5ab53aaa582845eae7ec375c85b4ce435ff07504406087765fb7427599fe59c2a56b066e67e9aa7c6aa757e61bf316c70a4df85bd0f90950c937cd4112fa4187b44b701427de06caaa0ed6bcb03bef235d88ac4cd2e35bff73328428493f4c8a605f230a4f25d8b5ea56b3e634003ee832a2d7aa185f6d16acfbf4c77185a04a6331ddfb532de410c3fb8d769d72daca5d3de42e890d540ce0f5fce1707ace1e8cf78fa8c8291895f3764e28bb2807c39166d364ef6cc2189260a6ae99d3170df30c636113f12ba02e4eeed109fbd9ba5299f2355d16615d3fc6b6a52a4b5700a3b12a16dd8bdcd0a00177d6d69d4402e272e0f293c371ca5f303d669c3ecd240ecc8266d330137ffb99b1cbf8578e493feb187abc3f87e983c77d01c4b934e4f6b583512710510958b30c95e3a05fe927e99826d83237812be3060f3a0e19470ba49b51813259f9d8871cd09cc197640e249de07d945294c3a7eadaf85e426848ad172631fcb99e275916e0a67dbcce187f33c8057b6b61987fa9b259243f2b56d81ef3a74b6d185e75e4402d2b039d0865e86af4de52ec3a05cc44a80d308ccb5c95b99d3e39dc98eed523810a0321acf6a4e6d8592bd913a8dca85c18c7d9d09fbb3180a548dd4c7eafb7505cc5212a4a50603fa9d25c193e2cb160c5446cbdaf720f7e539ded5c02a8aae5d9e18e60402e63cdf2d11798522ecc6984d4d7a48752dd5f751d463ef2531da29d77dbce6fabe43281c604fcd253fa7e8680fbcd875a86a0240d651684bca8da9ee858ec55ca6b2ac6adfede56ee57e8149c0b7e98fabe2755ca306c0b792c56deda3f262995e07ff63238b8aba7b1629eb8608b2ffaeb6739574a8320da8883fe998d33d2260a2513bb218eda8af5353c983712dcc825ece72c3e0256a39c1ca6a8386b550cc11ca55cce7cae3bb6e0cc08c21a71d830213d23232e0838fbe455b84b331d6956f9ff652c4a13c6d6ff341100f18571a54362e1cd6301d55224753adf4a8e7d385bac2eda1fb5381eab19fa610412ccf1472f912057501008d0281c57207778f3313566897f4bb5138484425fb078113a790d1258546b2612b0f8cf1b61a31bbf2a79202def7031e98b8b94a4f2118038592c711812328cd135839a1d1240fed6ddf861343028fc2ce5383950a4ec8010052f3bcdcd617ffbd753137aa5a5b427bd1c905c490add82e9f1669803755e51c45c7ffde6c369c8f5ea0e7eeb5ada7d32ba338334c54b19e7a9c3c160de23956fe9f1e2614c7b8d0699091d467374b5defaaf493212c1f885c766af2b0ddb97f3e32f4bd8ca93edc01d2dd2f8a6039aeb7a4527412bdf22245f078d72b6fb5f09633cb5b9407c50289ef389c11ea77db2dcd3eb24d2331e263f9ca105b90d0c0279c214b995d1fcedaa7200493ff019076d8be6c10d48b955a8d47c6b7c78d351c7fd0bc495752c530f80c29f0253b0390cac08c55438af82ae5978b845f7acad83247bc493656052d2a0eb3b59bef18cef53b3bc8871aeb0d8c311c5f7d8f4e0951c16e71a42570af892362473431e9786205abe8593584f4b5c7c4a9ab381f9dfa385426a76e8652dbbc1636fc6af75aaae90b913cdd37228d88aa4eabb76c9f9a9fe9f388a8a6d95dc305daaffb4ab28926d101ce8941f590916c5a73086fdbbfb503c61b582b9bfe74798878f364c860e8d1ac4051ec5762d86e02e897469839916323d2323e144ec81e8f24c49a898e7b382a30dba2f6f922c592e90b3c7a8cc79100505943888dfe06fd6d07490e59c0c5573e166d4751259040bc85a6a1aa19a88326712c42221dda5859e509029d9cefe1ceee50b0f2cdc1fc9afaad1c6bb3445e59f5199549abcbf3103d29229fbe34a6dc52e2bcf2023441e746b38631dd70f9a4a0c0a0368f87b398e146b6e40bf6618b239be0cf07c516f309d1acd3bacd484285d8c14a80baad0f992e8d629d296c086cd8e641c82cd1158123d23e1f18f2f994b0b214838ecebafe7edbc974ff1cc48ed218df3e4df2dc7bc3c18bbc7fb86ba0860a7e27842ce12ea152b62dc31ee4dd214f4ef37addf23c96ebaefc686472b9bb795e28ca75f45870ca630a670cd768e85a5eb52ce1a4bd407ccec636c13ecd07e03e68983a601b1e69cdcb39adf879a5ad3637695d57028deef39836b492d368fa1269948ed41a9b6a148c0e80ae12582b158d42e59716a777acd277b9419e804e2f85c729adf42f5835e9971f5aacb39327f1ca2a569fb2d2ee03564235f2c272b26c1ea21b75344bd612ae879ca2edcd8d0fbbfa3c3017f7586e5f492e8226e567d8ecdfda43df5fb5b89c874bd3f7c8bdc5e6e279e1f04443f0e4a3506f12b50732bc8e0d09d561930469bf301a598957d58a8f9d80e898c260b9a0ca8c8d2a63504ba6100bbef18140cca81f769998a4ae977c939a89c89716a5a3d513d3de5d8f6214271946238192ca4db175302d3a322b7bd6caf0b20064f54a8c07a5f8d16c35ec755410c5acf8c470a7485125aeabbc7126734b47b05a9046d1fa098730e1072b52f77d4ba5b2c8a0af5439303666b0425071bf2acfd8bf5a1bb3fe500bf003d3bdceb72623c3a6775a9dc326e206495c21ee8aaca5a9e92e9850003405a46bf52b3bd6939e5f50fa0acc0940f785b81fca053461156c116e81bada3d84bb5419e77af2f2dfa92efa471c140d24a8281002b1765dd161db760486a44922e50e3ead991541d8f7724283a7692fad609457ea3afe53fad26b9327cc9d4c11bfc084af55ca072cb66e870a1be0753e946297cb95226bcf1d5e6a969e2f1a8d292ea29c92693d17d84cf440090a0b885ae169435f911faad98b12f9a4ab34cbb21d2e7a364624b16803096f7d409b7cdc42fff4c83bdedc8a3cc3afba17da26615ff1f351dfbb19b6de3994638cc3cb5fb28d59e972ac0aae61df6c9a95f79300ec16ddb306f475c32d80f8ed514ab7e074105614b6ae48c7b03deba98ad4ea529f5a2004c62a28d233f03493907264e34305125228891047ce3c8a65989e2b888412029f1e98010306fc58a91c5cb6ff4eba3efab4032ebc6854ef6909665e988bb23cebebe2cd5d7a11bebea5cf387c26f2bba48a91a0b1048441fc7ec7069d7fd2968ea9c8a1fbfc85399b8224c775c2972793e72fa4f221e002c9d77df3d8567f73bb1741e68229397dc438bb1947fb66a022bb4bb6339b12678614d4e4524cece1b5c8bf5fd1f4e926350d381a2f27ab609217be18a2773be51da95b5a9ebabca356151262afc93379c7f3fdc176d6da97c93f6ac0c2702b1793f9ce9fb3455df050b62e2f4ab2d9be75cde28c2b5da072dbfe8070674e4a5f703c36f2821f847dad03e2a4fe9d6da1b54180a628464bdf601d452c6417034540e12e27a1ea25ac5298844ed116f7d96abda4f0794dc9e19e5165e1ff00e70b8fa8ef6bc33068eff2a1577d824d52d44ce3b2ce0c23adf86ab0bd00dc35b3431702321c87d5406599fb346787db759162a059b93a2cec2831ae6d6fca14a5f13e8827772833ee7e0609580722617863dc4e98e862992de6f3a6b793fbec229cf3995357f229398774c4a5f5546e59c5bce06f9e9fbadf2c5c80ad8b12a66826c39ad79b8b651569f87fa983b61a51c542a22a5cf7dff650656f37c85132ec8b25dc68fb4c05645cd438f44dae70437b6b4d6df12f361470f177fc955d9a6f8ce932b3709e69634dec95a58053b8b94f44e412ea12aceac04cb0f498b7b1f9de341361c3c4afacc294037428ced3e970f26f0a14060e3e2961f4aaeebbaa1e2a9a07f10f7fbcb31660c9caf73b98377b6af841b37dbfcb9de37b48dd21b8406504fa333f6bcc7c88553054ff7c1308ea1e11c07d54914f399852385f2d0ade64e998e0edd6321d4165ca09c2c059d60193c97a2f26586500cbfde14735288759b8194189a8553635e1385c688963918ff3cc91a377858ada8ddee4ee55d0bd386e35675e0154dc814ce50534be7e00ca58b242a27172bdc202dfc6e90b7a5400393ad30225690b07390e50d4288feb024bf993e1642ebc5bb218a6528f3591adbeae0a764d820e0bd5d6fa6f17d06a028da53abe53e9f2d91678ec667341891bfbacb22cc785c2de1c421ae41c29682a7fd58cd7af3a8abbf1f13769197703a429f7167783ced9e880c69e4ea818d29a9d5d177453101b20431e7501ec0da6309c21b25a08bf771c17ae059d3b3d448110e0b52e81e50a9d22d097e4e698601164b89b0f744ec37dd06f95aef2dfad3cfd48a88b861ebf0d57ebc301086aaa39afa1927352e00429d0088228a5565d24a3165bbb884feae25b64d28fac2435b3ca2dcc13cf1076ffd842585e751cc24b9c3a58c82fa1e96083694d940466b1f6b52e4d6c78c2fa3f89e04d3a497fd26374097404b592ef5fbd982be7be5f92f964befb7b358d019069425fba0d48e229b99eaa0b19d4269a5f12623965c7c3ba3a38707e9979dcba3148a925866053a5ae2cba6995b88bf5dc86e1c6b7db1d9cac3792f5a92bdb9fe25e49cb97a97fdba11879c9571f004eaa131ea63c24ca9bdd9040ab38660476691a9827ba93c684ab1c79fa872fa0b1429313602e18d8432c4195283cc481d8fd26728df11bd649aa68fe0d7249cc958ff243ac85c513712ced89e4e964b606cbdc37809811811c6c6b347e7a43a37cd9f11f903072d001d8238d264216d7bb4faf661744ea5f60319bebbd0f75aa10d930e270f97f9a6344278ce2b7448a07246cc9438a39b8e8c34baa011667eced7ac83a0c4d85c99969630ab31a79170825faedb4c41a9407710ff3bb4d9c26f7d050d445bbf17935088b91339f8792a7040873357c5668f7680ff4868672f41bc9f2c8da25a918f612958df3dda06022f8a42a01b10da15e331ccc640c71319cf275828909ae8a0055c2344296fc38ddfb1487dd6b1a06db7bd80b6c00498800e353eb94efb92fec1fac384213b43b4b078f0d11956755882bb0cc1da1ee6a24300d6ca355e9eaf9a93efedebaaa506e00e9063c640c0115b5ebf0af36650c9d4e6ce1256f40129821139e099de5893efa3b6c390bb3d4c7e2008604508ef40e40c5d612b680d1e7cd979032adad9fb425651c3483d531f80cdc7f98df54bdd053581fbeb3455b7129446c0ff20bfdc9e6d588101bdf4c2a785e872b992ca1f369857e2517e531d8792b9ea77fe194301c6577028bdfa2d2c60623e7ae78a02447a1c89b03d6a783d86d09aa27f48d47e4a7be8cd9a1fc48e4c872e93c083e915e6856dfe85c88aa1966484f4333db96a8f3d02e9c8b2b7bc296bfe113cbd96b241fc901995628443be22af34dc4974579345e4e6c5670da30f8bf04b58a2a6ba3af57da2ee8383752939ad3eb8a34f773c98c931f20886384774005209baf685a05ff66a7efb63c3b8c2ae401046682b4036d69225f9e01ad9f12018583d0ef6171427ef2bc7ff21cef7bec8ee7bd2099b75d3e0e48600c8afd99fcdf09047c51875916729793eade1866b26d907dfc271dfc0c3531a712274950deab3eb9b8372e4f9e946846e615bd9c563a561fd4050267c6e72bb97f65c1fda338dd37eadae7fd6a30f1744e0b84d5c22a415930792ed2a336e939d0222c803c3ca0d2187144a95cbee8cf26c127c400f3316b72b1f74cdfc2ec90e9c84200b51e83adedffc171c4b9419c927f43a1cd17643d69c22159a532a4359a56ff11e92a4f49aeba5f49200001c2bc429b3b81551c4221b4aed8cf1073bb39bf70a4b598a6a3f76bc6ad6902e37b20cc1da789b1035ce10350bddc3d091251436972212b7d43549c31a5f39db1a1d7e6dd805afd07b0fb942d03f52ca4ac0003b22bcfd45af3a929ca9c36fb93706c964a603963e28d9c0fa18934ac1d5341042ec331e2d90a720da3efa0ad67ba62b79e16b401dfeedd8ab53e4b742674c4d2902048e6aba56a4cea2ccc22b0130da3fcc894f33e15cf9079087a87c5d428c6d37536afdb92419466c15e4591ecbfe7c85be9dd2413d100ca693b01f0b7cfffd5f11c5e426979e73e985e4db701096ea348501fdc54aee7b9cf87b693d6f33d7cd4f59381e8c27d233e9c00e71388f32374be07596b8ad2a70cd56821966a715686334efa435fac0322760c0707e1b76680efa0df19e79c5309753c34b8c2a45651228042304b961bdb9c2a927d51ca359b6ad8ba7a37cdfbd6532b03d252579dcc4f9a705920fd0fc4067782ac334cfa333a7da9663806e9d139a6e51e044a8acc79d13893c2aa17b4b51ba12589e6652261e53e3d8874059b448f8cd2bd62176c403b78a98832e5ff6846c051a19b9832d62a482aedef978ef9ffd527bc4ad41e6bb0e9bb404616d9b424c558411ebcea2a1f1c3e48207afd43e99ca154b23a17a592863a7079f9e93b909cc336d2c367caa0957ab4d11326fa59dee54d7b373cd614389903a0161001db2434f697da4804eb69691623a981821f9b483cf4951007f8057cbae047a93460bd33d611f9afcf3fcec3f929a0288d578f282ec5a6cb8f211dc1962c33e4d7a87ea6baf27caa637c9e0158e2c326ef9e41ee9ecdc6d8f385a0d873d04098e37b379b47106ddceace910cfcfa720209c6b9a9152a503e9f0aecbade59dc9af1dd42227cf092749f9774ae8c3abdd5b332fdd05ee9e69adcb7494ab04d7472d2da321004a0eb796ac4f13d1e6aa17899e8594633c1de866d88cc535a3d453fc08fd252b245aade4dc29689b6273e526c1c9f3c5d5775b36712ec28c9b5b0f2cf1c71cbc1de1238a183320edd57bbe105340c87c8315fef9a0c96431f6f1020fa84e7db4f140e6f98c4b9ea7c6a8547d492175168fc4832a5fe934495d00d3d9d462bc82e250d5360345473b036e3a08751718d70decc97f68bfca63a436d56496a8d4729aec61140562732472e5f446c852af8bcceed849e7a0887fe2542d9e2c7767a34bd00857c00f1da4d733a6987022218d2d7e3d414302b34d2e07c07703553bd5a3760c03aad26345b89959c42b504ac14f4a5fc7f9f01e4190dbe0b7b60242a3c9e7677cd4d66923ec61fa12b49db9b9cda16d14d0bc9241a7d3da2347626920dd8745c4548dbf874f68c9171899da4ad6f3a6b306882029403d1619898889c74a9077b3e477f854e1fa0c3cf47a94dfe864c49672e17ecab818795c70ebfd2a77362912cbb4d278210495f567fa8958610b4fa9a1a36a049ee1a01c13cbf9769307afe26ef611dd0b108e4fe3ccf7c8ad557b53d15a01a850b99f65ef1e1275c0285ae02f73f67fd1c9c226fd70b234e29aefcc1575be8f03955eb69477cc3d949890052e2420d718658c4287bc2dd69822b5f391f63937dfa525f820838049a1055c9379012429a3e68894823051d16a2089586f85d94cc97f4506c5ae3df848197a4fc843100c160e206a65adcd22436d976a7543d9392c590fcde22a21af98c0dd6ca67c296cedb5ea440406f5e5fd66795007c99495294746000814c20ac4d95dd06f49c51a4370a9c1bf127093e3b8d0364352fc91cbe06f708e21985ca1956feddc09a393424dfb34a529da9ec0c8242024938978611501ee78b2d22c346a2e6759f6bf61be89dabccf31818bc43b1411180f933e89045dadb6c17d94a67ab7100a2ec88045c7912d569ef7a1de0231be91aeec71187c315f7c9541f4780dc88316257680208ebf3fc05ab5201026cacee0a1bd7587aafdeb126b0aa80982af2b1bba40d15a52a195714ea738f7c081ec0d762e78441fce0ea89b32c9de3b10a8fec5d2ed8555fffce5eaed3461bb257df31e785e59e1128354803d53ab2b2705a96a7aee10037504fb3b6e91e13c56a011173f1e5fe354f7f24f74c642c5cafc519c461d7ffdffcfbc32224e8c7b5c2f6f37b9f4a7e08a70217979fecc7841e6ce817b823dac291c13914dc00ccc2456d2f3d36dc175581b68924b7f060819f5b91adde85bec665eb9363309927ecd3a072e6a4ebf9dff7c9008b5a36a7d7fd15d4f0818b440526ddf2cf30446e1ff3a1bc80a07c0019dc2dfee84fcf1c112e472c39060196af954d8d177d15b94a444b2b09c1cfa36e08529093514e5b2c9ce52edbb05243b9f85a30706a547949944d612e36e535c991af8a30e8e4d240d964e637d5ba43f89f27232a62249703afe528ddf4941e440801fc48462a79e884f2215d87684821b4e3d08ee1c0df40ae7fcb85e6b01a5ae9a98b479eb1d0d3b6e6517039fe270606336399b664403d1879210f549011f0807e22c95202a08145e01e64bbb0351ed006e39d047750eaf1da7d45329b7a40e60799bfe9e5edf01c45d348ec14de2972bf7a98da49d2435b2e3439b668b669d543ea435c0f6c18e9af4040ae6878ff852389a2da3386615eb8ccdfc668adc941f3409d49a31a8749fb58cb274247e5c2e3cb4b2a471704a6928cb50713d88f52895013b28c7c9c97ba395eafe2dab6e8a3c75a029427746efdc10cc183f17f29fb87d1f08201b852f6d4067488cd44a35e12886dfed0cc5e01377e5416e98099210be1706eaa114db9f61017495ba2724452427820e8f37993571ca73d281d4ee2885c4fb0824a43c1d0e7e02cfd102e4d4f1db3057e67381b73322339842e596a4d2b4e296fd268836047b27ca1ea0169f17c7a61acf14bbe00f97aac8cb28780facd9fd0eb8f955ba240781651750e449dbd31ddc50fc274cb5b14a0e4ebd9048543ba99ca25046cb5371164f6a8775887614a834eaf53ccf2eac281ab9bac2ea880751d3cf5675f074e867e6ba902cf8a6ba460490d6aa381c4fd0264495dad8ed703029b99052a77991d40e7c6ead5b67bdb27d863d1025f6fd346f7c4c8edc692fe66285cdcd28d7a44c36033c6760f0a2607fada0f89ad49dde1f6412ab2d07528f78abec2117c9d2c4ab67abc0141982b6db4f3b33dbc0c5feb09865668e77d62b7a2f29733c5256d304295d40e6603f08c917bf907ecbfe5b646db9c87ca00f797810eaa72c8e9bab88865bd6509bd3069c91461af3ad0792432176e8111d4eb8dafb6bb8cc5093cd8b061c78021025f06e741607b4c75c714968fa5ef8d3fadd1351ccb6a314f3f5ebe627e05aa86e70c0f56e9a5977202cd447e756a41cf9ea9b8bc7610683f9156c84daa49d73c1e90ca42969350f307840a80454e9881fb448bcd4c086673d3d783f7c6abe423fa97384610f62aceff204cc41a23c43863a1c10d290d17d8ae57f01cda4fe26b49251498b7348be4cc43c452b2ada835de987dc054a5d1ee71e17ebf83ec3f4a31ec31b762241e39db20bea90828443376a21eb670a504a31a2edadabb0665987045cd89781c061a0f5e66770cca3d08acfaf11db394d3b3892cf4293bd9018c47f04d110f503e70dc14ed4bbf06f887caa804cc4cb5dce9e8765148045dce9d98edf5226ec740ca34acac86b857816c1c5e484cc37f9a8dcb9c230b8c9ff46aaca9c050f5ab0ef8a4fd8ce1de4cd49e8fe4e1aae5fbf0306faf38a7cb7e3a27e2239740a284541645571413a603bacecae41457d47086a85559b3a2863507279796c625286567ce9358f89a58233623ca87f5c5deb972fc286ca3434cb0c094a279935d53e02602c96fddf3b553f5bf2f9807e87def19d8c00391bb9cef6e52e943e89cbf50b9ddee545182714229a9170da8fd5e53d5cbfda30f7569aa6bca034d99ea2f65f15b61f76ae514212c0711cba63ba49d8233a0be4ec18a81455379f62514eea5adfe46e3a7033dd803bd55484fefa49adaa151580f243f90c37aaef472984748d636a8f874ee6c295f03a13c17f87d6c955f0ae432c7e188879865957300f8ac624c0b0f42ce3d55a3647f98184a213bca0ff12d232a0efa53a235c06412e90ee4e662cd42ec7ec5be2e113018f42b46f67eeae1e26ee3578b9b51cb778de8204f4635e9d0b2e955b96c7b17d31efada48b07ca3934fb9903b1b3d12dc0edbdbd95e0f3c504e5a388573bfc5371b55cd66bd0c6fc193d8b6fc1bd51474a821b4d104a77f4c725bb53bc2071c192e4c0d4e874742647b476f6fadebd2db3722fc0fcc090b8fa0fdbeaebfeb403b8c58765d9eee984a6bf42758703939862091e2daec3a4b694097bf387ee6ea3370b3472f1b0b041bc0739452e15167e535c7fe01904c090afa2f05b40233a40c287d76c2680897bde238afcfc8bd8c1d2169dcfa2ec191cd6d75af758b968e683e05858e5f233cb30ef759f25ee92f0db641fc1f1817705eec668560dfe452872d622eb1a25208950708affdb0a41c822c938ce3a739b989365dd3e44810e3d2b567fde7f0eb0ac41f0257702df19af13099d6383b7874d2c4051bad4de44da53d10a29145ca29ca77cb9ec933c4213662438113d32c2d28819ab484f498fd1325f6be1a35f6340e4d2402355066d89d49ea7dcec8a24e16a192510349f5b2cccc655bd3b0a5f80e3d5553b182a6c120cb277df22b17b61a2a811998b55c2c27a7d897a48b284c8a5edaff58dbe504905e2e11499520523012e9d3acb3f78f6d614b7a495b1e7d1bafb2d477a2e578494d31f9a688ebce3700a66c871c703003ba003562af4fceca70568250e1135cb29522cef7acd23933d7743c4243ee994a70bdc6791291884b2c58f4400943c8de385eeeaab0a34fd3220f6132f3c1d9c9ee28b0fba4005652aabd2da7a682c0fd9a506506bc6a96cb38be47f1e78c3e1c570da8001943dae61bbb0e8e82b6cab71caffb4cd683b7b0e74188a8d84622234b947a6949046529fb5e0bb4ef281475ee11f6f13a0fa66bbc7260fede45132ec5d353431a174ce0227d6eca5437dc30d8e14f025eba8090538ffd69e09afd28a364b16b1076b55e0332282c7ecbf2d3aed3cd9d660bbe64a973d104ec1096b391aa5b0545aaf97a0c27da69ea72a846b06d37ff1a1f1e14f107c85522900088a86be5bae770cb1c4aa8227286c448f708471a141f0710f9208a6ff956af7f180fad6e70c12b1d7a5f96a5db9feedbf154e788593d4d28186667b6a936d1d84ac481be7d7f8fe1ecdf312c2a2974819f88690f15a7bc8c20430569a3ae21b09233fa33c6e1fb712f1f9c45a3131b1ff0d6f70000320366fda1435e48cbde7dadb50188c5c03d02d58b4975f6c98ff25d770445228fe14d8960dd1bedb21dec7012c8f71be627c04045fcea7be56229a05eea12a8e867e8a45562600628081da48cf6eeb999116d4fd777988d831a70778e7301edb23bb3adb97d6d8fc96a1105ee99345292dd1a3356edba89e1a622dbe39d0ebb6089fe2ed8aa10d313703364b1e983e9779316e244cf6630420aeaee85522a90c3bc319d26ad31537d920610c19b96c7efb74216d50fb88a43ea02b18b99e91906bcff593a12520b0fa78cd7651e50abe69755a16fbf811256c27e153f764f1c02a7f5d49ea252672f0bf42865c9dec09e12146c7ac5c80567a9fbe91b5ae335fae54b71f778fea92693d3eb55e8aab5b4ce218695acc63a5ba8bf585df521d00600d2c21b891b2c7e9643c79419052ea356cd932a01b4621a2f5fe1bb8ff373f88880bf1259dd226cb4121af3368c5c47299d37545910b8de3684128784746499cd3153233c3a809a2d34ded50f6daeff0284393fb44be8bb940168b3f9ce4f002ed3c7f66a96c4f6ef8e5d12053c06523dbb9ad09087d89683cf99524fa2d0b976ce66762f1a8d608db0a2f2a7ae34cb6a52aa43806681a89e73f478d16d2116ecaf2f24e1700ec7082f6dd7a5441dafcd58644c9527da9228aed81234457c7669c1f5a31513dc55b238f4ff46e83368b5d315b128ab65392aaa9c159c0c58d4b1f2a259e91d03e27d541dbcf48a8d4cde0915b327abd08048e68fc689c04a194c94f8c9099c264f201f905d10b94d531692073e0d9904d4109a568507945c610c84e3b146368f1c9dde153f4e3b4facdede0e0fee11c724b5cd432209aacb6ad2bf93700533f91da8afa052986255949d21d35cdba0b90d746452724aae83350a0e4a709752f11b358084445252a27ac1c5026c07bd2ee30c6823b593edf733c2680c43cefe2aa3555bd2ffd0d9bcc890c395380d06cd48c68469a8491a3eab02a63a2d26fc304644f14785921a0be623d87f25f5a690fe5e52bf4c7d3df1d4c88a2eef77dcf2c94215d527f5c024b614784c2d1d82aa22fb709073b4db4a191e243ed59355a5cc372da7d51892611e152b7fe3fb96d3af3b5ceffb054891a176eea9eeeab94fd95d2242bbdbca5d8a121ad2e9871a966688fad393cd334ae60223c324ef2d9e182dac0b7eb8b0f79856409abb944c6fe6721a0078eaa574996d4899d04a825a8123464ff71dde06a8e616f31ba527fe919c2f19de1f9c2ee49d84e612293d83ffd4ef3392075dc2ddb88b63b6e01ae733422a8990ed6d28c7b25d82df556af71f9b46498fa58b6a2cebdc112f2c2b8e7f3ef04ae25966a0c2bc6dbb0a7759a0d15b13cd8bd35b3610bc705ba57f9881342f34bb26fc3da1ae7d483b4ff2d46728764f2ac62a1380b3ac2cc2c75f5420b0248298041bc47c94ceb6d504bc3eec66549799ebbc46f88f974f4f6d83419ad5274b696101db86b9ac414e3749a15538d5fee5a981fcebf2aed04100ca17294f4efefca2ada8117f51dcc93bafb882d60b9b4bfc53a080fb4bf0321c212eee528480bfa04c20c4ca15860d40ff036f311815711878ca689e81af392234aaefbbda6b8ea2ca5b4cb0a5aaf409a45cb91853070b89613477ff03463410f7feb4a09c450c16850d64cfb6cfa55398915c2eab77bceb366414d6c36b48e8784afc85784e36ed6ba95f863a9c400be986f09c34774e7c63062c804099130a9aa71771ee0b529b8f382fedbd58ec4e92f929983e4f2abc11f189bb9f67202726092eef4b0cf4324f613859e72eac25de4034b8e2588bfd1261b1aeebfd227755869bd42093c572aea7a62452b0a54a8d97ce911a24f427cb9d57a88857f16445e8a3dfb3a5035810c6c9cbae301a34b7a3dace657b7d5bbdbe444cb8117f56e0a7ad36bd1ee05c43a4b7faa02addd4a5374cc53886376445c9f753a79c6344591a0f78d4a625f83169a1057a200e1337e7a05e490863e090d196a1e2acb5ed8cd2323fd8514d3d0c13bcb446ac827e8126c11bc86f2dc980ae3fc73c2d9ecbe58e149f57a5f7143ad2b5459eef0d82cad016ac8b4f3281f62ee4f1564286a45c55c8bcc4165d351453a9de02496c4ed40cd678aed24a1e906afff1d56bb917120c7fa9c0e6410d62b153ed3ca29f3f761ce0c8b6125cee270409cd88c67f83a8bfb5963b4639a4ce7298c87f6b441829ab30473cdc854c93ab5a87431c031d748eefb4dff3d6da3349254fab6f5a42855b0347ab2c67cc996ccb8daee17fa7f1d442b76029168b9184d84161ab5c29f2e5db2a9a61366e7611a7912b0abe72615a04313fb71bbf542819b0b4b78ab24ae793905b637409b7c3f442f955a7899dfbd7f417af8ef2dc79b2e6a7d1134041fa7e5e3d122b5803cde6ceeda72519663842c544f86aabe17323c1287d6b94af1c19d24f29bfa83b576ddeedc0446f3f3d48f94b9b333c2e99655e771a65df9cd019df98c77f749d46a226bcd884b4fd82240efd79fb4277ab0b38d8ab95cf5e2a649733498aa610acc4861cb38149a038090aa530a89adc718c17ab0f1d97bc3066dcac55a37760a9f3f41bfc9ebeba5f90219712dd18ba839a794611a3f9764bac2ee83a07b893029827439f0125b2656f04245a034bcd33257e13bb78b85d1928598631c693d7c3142bc5a292c2a6d11628e1453b5d319e0be99826db31f4f96b04e40954a30e67c4c9c03ae1617b8793a2e170c68d2d041325dfc829bd1f2afa3e5643bf7d819b20102ea7f3b238e4af5e83b5473cfc1dfe9715ed14e63ff4bda3b56b885d251ae2a4270b70aceceee95e00eab11d0fb04bcf0342e4af935ac645a378eef89d6db6c585d9a46ae17bbe000a4e89ddc7cb52f962dc176d549af8ab7de1ef912d1b6b865beb06cf3712924f598bd41ebb2dca050321d94e58308bfd3c007a8900d21cf148239a75270126155111e71afda5f1fc4a62a9a7006c59e57c9815097ee086322be199dbfe9ded67da0e0c0dda57a7bf5a19ada312bcfd6f0e69e7f7743c2f002a477a94ccff151e20aa6004fbb717b20893bb4e5d2c7f9b80eea07a48b4eeaa2b515f4b771c7ed065a1ce8b61027b1d39644950450074329a02767900175030bbd6f2ac6de8c06e55c0392aa0bed58e7c933ff7a91c1c4a6a596d80bfc75c5f829517cbd23c5dce2105e67f05e30ae4bb32299e8203c86d7a81a5dbd5f1aa49814c15c8a174dc43846a842a664cdfeeff5a2b6737b832b72a0c36f6e4ae6bf23ebc839306c0fa1b21bab1c73876f94cdf105202ed55f59c47ec832f43db69e263c063ed0e559ad2818f8017448c366ad7349e3bd540a2cbe0322c8b6d264fbaa62d71450ee6888e9f9a1de6e83057ae106b79d604dcd02db7cddfc6a41dc82d4bbd2fe7a25e48c6ee05abf2354455a0ccc17a9e06bea101c254525c2539cae2b40455ac7a822beee61eb10f53894a8f2f69e0114555126964d131193337fc831b9eee7aa0d5938b282bf6635e1c6d5afa43ff2316ce278d9e42a88edfc5f3a7a82b1d729a051473105833efa6bf004c7c4173e2cc5f127fd27295e592fc857beb4a40d3b4bdd32c8c6329528ea18649c8cb9f244808bbb6cf778155541066155865f182648ced1be7e939c55293f8694d38c2f827d57155e5852f7fe888a35896f6b25f4e7aeb8115c8c47f77845bac36dc38efbef3d00612bc08a0fa0048f39fc1517316c2b6935c9e7f00613ebb57011656be8a93b6b282da71112fcea018279a8e661098df2dba2680cb85cd439fb96a4e440bc1e5f4494a75e850adbb902b278d5f57bf451c5a865a0964b4b8876539ac9c2af962dd1b14f949feca0e1457b4e7a8cec15b3e94d6f4f9a79511a9314eec626ca43c9bb725f3f94a8112daa4a69b13938de7b95d472ac0c34572d64bd235495c78dc07e90ec4f72122b7f216da9cb445bd3e5173a4e4d2726c009f7fe66cc7d0de35300078ef4d32bcfdea778feaf8bf13d1b848962860f7e4f2fb6f1cd5f30f3638fbc6eaf8919bc9a38f7f487140213e2747ce838f352c966ed8b4df1d4f3fdb05c461b4ebb6c1fbab78277d8bebc293156c9e0bb6c9110f635478a696d046d1a457ca41df80b5ebd90f86963b4351576a95610c459c53287b285923e6c80aa9edcb9df943316b6b950bd42f0b848b0a5f7100a26c2c9892159192a474d54337d5aaf73e769fd78f444520e6b64cca12d6cca6d5944cdd6002b39f43081c3e4629edf69527ae908d8f205e8390fe30024b80e87e72ce194b0d346bd2f30b304335f171a4072b187d128e6b78ebe7652a0dd916ec8a50e7c94df9e5ac4e63757fb61d0e6e29497e82526dc9d46ea8a4d9799fbbffdbc678cce537055b0b2ddc7ffb6e7df6dc0c4ec312a1861b98ea18f5b1fbcf2ffa141b05ed0d7108b7c51d42d83fa0dd0c721ab2c2d580968dd3b5e98edaac3ff43d2990b0325b78e2e52b5db7b9bd6504bad398b5715dd0a3c87e660047dba7dd98395fe8af087eb6c110f5b309e8b36bae3e1aba9bc186a3c451d8ce610082e68c6dc04d99fdbe326c5a7ba9d10a9028295b25840909ffae8866c329effea53ec89111400d04a30071148ec59f3d47e6525690cb109814fd1f616e674353de39e902ca114527b2756bc6a70e840a7fd8082264202c6f499679bcb46a461607512c60dbd56f0f50e2fcf88798deaafadacb34b09ecf53208b952963206bc1aa582e7ea1f2a102c03ba1eded1581e5e9107177c928fed0962786015fa1b932e151f40824b16dc9122c38cbd7323981a2630df5fe372e70c4b21aeab460dcd3ffd43125d7b655e746b043a32fb5744410277abb67d3ae21248f12b0838eb44b81de527371f8d798ac97a8814723a43a58bb20f3eb2a8823243039a7f86a56d889818c1e6818fdd575e757548179cdfae270cc45a774575e9906f1a8bb49aa4adf14446ad008c6ab3c57849f8041cc4156b39440fe43732b0e9b846e9cc148d1eb2d82494a111a088c7d90b50ac3d5337b102dd98c9cc4dec34a799ee9cc56032ca4f458d4a13de73f5d614716b7d940d3823dd78a4f9d97a5c445d1506b67fdf5a284523885049290c1989fdbe3faa98407cc7e7d5a4ad1e830c5d2258c1b7c7f307c384b8623cea2723f450b8bf86114f952ac293e4edf0c6eb2b0fac118887cfcba7875af9b14063ee8ba6e28a85bbb87a49e06366beed9b3e1254c19b13536bccc2ad44559ca81c04210e8adfd59edfe2735c98f41f3c35cb72207a2188caf9391893dc367f5765923b0430fbf72f022da2c73ab0f59a73814c2202e8cad0b80f3b96093050b391d0cf47e07302044171530ae7a14c28671011250f02de05ac459bede9ed2839785a42bc7f724d8ccb14394be2eafcbb684917f26d02f272b5f2f3e4e7cb71b8c3fc1c7628c7a9147978e1a2e3ca05f9ea9c89c525b929bdebeaeb5e02b6a2f36eff1e81cc9ce14c5741f9011c676358e7ea596c1f3e674b4216d3e393065ac6c909af9903b627f682b3f51bc0c804fb2f13a88eb46d1e2a9eed89c6491b538a46a1a585849d1f7ebb1bccb641683ac73bae51604305af71205096d9367f6b624cc540e39d026163b7f2a2e4cc4df76bc7a6217c497dedb071b0affc451e430e7a7ed02d94d114e18d23cc666c9e27de99f22449c5fe9fd9e1362a392903b59da4e09c6817cc8d2405ad5bed5f848acf177b6b9f8bb93a6a0c9d3e9b4403a1120400fe517bac154405d27f16890a8bdbe4793525cdb3beb78eb04b5da50014c8ac2ceb1bf21ce1c65e26125c03f1a72362effd832bd57100eeedb2fa09c8ac61040c0c1ecbe9a345e0089c2802a733da80bac7dd451c82145fafd8e66a50d2138054520d5ba031f91026e1c1046c1253496c4182a78c127ca0fddd9d93d5e4a8c2a7b01378d511d92e0bb2694736e7fee406bb3945388e8179702c2886122034132895c1435dd84e98a460a06d1922b6b0882e3fa02e43309d3b058598cad3da8253d470174391840093e1b73e53b28999f9712ddb814ebf242c75d377a050f2c921b76c0e63d07bfebf93eb28d0bf251e03af5ae1b79939fb52697dc44c16e1dc94cde8e3893bd26441f64c4f05067b52455bf38434cbe42d7189589d6e4844036f22817d1a5952379651ae5973c269502589c79b1b12df4ee155594b5c994c5648a4fab4aa0a116a19dd69d9eeb09b49b6b946d7740938636e4b194374c166cabcd40f01d2649e75b9bea1f892040479cd81a638caeb5bda423e52d98919a343131e397a9d76e2d115526d1e9d0c7584520a3dac293ac760d272c0cd644df120674bac1fdaf8e3f461381178b3a241c6587d2c1a1c90a280627a789c0ef53d26234752182eecd499839466b6ce241a0be09016fc27248bc4fc1459922d4160b752a97a217c4b25070032599f6fa76b47bbf59a98f44e78f46289654decf65c094e554cc28ebe8aa28f2a97a5316c871823712b6af57c33af74e84e7bee7642c10b3de00546262fa13845a12fb022f393968ee8d78fa76b7c2c5a290f5bac62d2e1ef3b7f2e9d36e0efc53bebab7d0189190df91299af7e314a4b423df5cc3e7038d0e430ed05285c5040b93fae4ddcf821c92d15b5f4c4689f04a16d91dec4e3207c94a5b004ae9941976c457ebb43ac7a3e151762600f81195beba60969b974f9a37051a838ca55bccb54db202b5c9bf7f7c9c2174f4ae63b87696ae8eec327cc42f7f39b7aed9661b934fb5ecd795248ae18bef95d04eb2893e8a32864b3d4b436e66aa2caa95fbaf37e3cc212b48d211d466ff79143f80e042e8bcd1d12821f16abd4398d70f72da5719b3f9ed364d365846469858e9abe7a328a8adb546ee5df69f4995d65125072d337b04a89ba2c758b508467baae1c8e908fef7edb0ea04901ae3a75d0d9458f6a6a2c8f882b14c257f2a2cbc5f4a3dd9c0cab737b608f98e5aab44af0245571065127c8b7d87ff2091a6588869f45924ec6eac1e7b7c214b8958dccec5b1ad23c4113dbb7a086cee6d37bd418ef211e768c70dd0a2ddc054bd00c6b4251a4675f459a53bbe2407b23af4594c04d09d55f80c1277b1ff595d0ff6c7883713b4822c48bdac676df8b2363fab71ca592e69da76038f0d686159edc674f7e3c19a7ea672835b683e29f0cdbee7abb995c74229fffa2aaffe43cc41a26bc1bbdb7498028cdf51e394d9c0b204203a71250e1b0618831649b0ef20e0c74909f0530afffb38ed7e4dffe75f626598635c3e3b340c420696615a592760bdd8686b1a1b9316ad71c53fbbe2cd87eceaa40610b063f476e37569b169e2ca439d1f9af9e8d23863b913fc6863d69abf938418790fe3794e3c6dbc4cbece03b5dbd5830be46843b1b8f23b4b623f9294888b1cae37e2ebb85d8d381562c3e11753fcea36e5d0670733c01d1754dc02538c7ac675688cdbd4b5cb832179e9512867a2dfa39581517ace6655b8a9c0a8a98474ec0465eff52ce06f711d324171540e212455d2f297a130e73cc8ab82696746bed2621bb9fb17c0b8e431c0047cb061798722b9f6f58644ac6c735e6686ace4ccbc750ff978f208db885e3b11c765023e8d554724b44bb5ea87edc1e7fa59b3e5a341e12aac979f1e9ce9b793e8bf671426aa250ec03316d2c0f6f77fe67fc60dc7760fbf4696e79a33b992300584fcba9a502fe1905757f15534b347c7b643db3926eb0f86a7fa0ce25f3e2fb880893f9d4bf9a510c2cf05218ce216ae38738f032a86fbefa94ffaa6056bb72948725815fbae3262133be0d4932a70f3d006e2bfd9d450251d25bb3c777553bd59b5f4b73f851af6c05a5db7f02a6274b82bc4a7e9368f9f1ce621bccde0b07f3f19828ec7d96bc8f69891ede53a807bd58e25cf2da372181a45b99b9915535eebb505aac94f4625b002541cc9a7941323b1bd3672337406eee40a16f169b23aee122255369982ea24605245c626b613b968ef4514a747dbc9180ab04c9a8aa0f35ad89f8a53eb892b7b9367440dbcae889661e56043978d24e8884d23f772b2789287049f308d266cb02a4635afcfa584e6c3d27eee09043cda7681ecccaa9e40aae3bc9a3d0cf18a8eb9b5ec1fe460dd495732334c98f35707aa4364b870e43d3386c8a168e4ec0aaa4183c9b8da4d6a3a0afb2f4aff4a6b3a94c518811ca7a291dae6c070c646df3b82095901b755ae0c42428d2361e09fb0168a8564380f20d582a584472ac1ff5b029d05e8c94c0961ab8a6e25814887e0ae9e060866e21a081dcdccc0726903df0ed29092f6253ba087cd176d9c1f429ce3dc77e22031f880949af1ee8fa6965dd25247f815437783731b6cefa6b51d95fd20d4e7a8aaa6abbe7192fb6ebad6a35c8876afb480a06b439b6404612be9b246e7ca2876868434fc244edf09991806a26d48d83e32349cb29eafd93bcc8d177288a7d4826df46ced05eaec1b569a0d1e148501615a79cc77e14b366f3c458f211374e4d4c7962c2834bee162ec637a5a740e0d76432fb9798ac2529337b51495de4f87c652e81182b64e1e4c3019b423bed0ce180f6b544c0ee1b56dfcb306cd0d7481d20f109a33438e80954e63a8725e2270f3331078766f44b0e7ef9dcc86563b896d86ea732cf2e0c8a522413d58bc6941ea3ffcaf643d62ea115e7aa66077a745e2b8ca9886ae8170995a5ead51e94481d685960e2fc4c681e0f0c95801a5ae219a3a60699133158a2e11f52f366cd75481321a4489952f5e092facecd34023adbd40226814230fd32fa02327bbdbded81f017fe8ff784f10b8b20e9dbf45d15fea577f4193b421d69a5bc3566acc88c368cd91a13e509b65de000a6354edbbd58fa8499958096b50d2f07f3924b4fac6300174b2e2ecb1e22936cc62161e7c8d30ea47bf20fee52b06cf65b68aabfcf34e993f8f8c313aeb37af400506d8b5021900317a374f0c7e602180cb1b57f831a2540a95cb51956b5c37eb46a25cb337dad25ab903b2513750379af5f3c0def5235530b06540612da868c1fb83370392da35e812b90eb5d4129ee60f80549e4b2e560a00e6011a740623a3722269444723adea361b792d7c46b3fa6b3411a9073e1ee2bdc59f98ab8820810501d21475dd5cad9261f47d8756148f7e996b990f50ab8c019eb710930b53acdcbbd21b13e33a06c355a938937dd62bf662f4d935c4c389c7f4c3cb49ef534e09af72b6fd03beaa98992cf81d47a2b85cf60c6af9a672524a73f75b3855fb8a852ab9bf196128f04bd9e2f66e8eeee2c3fc254818c16e00e7df69f10259237a10831c3293d4e0e662aa5ac85ae38b4c78b421d58c9da69b05bfb388f943699c5dbdf9ff8a5e0d39910850ab47fc0b5d0c3db8cf14ce6a42be38bebf1c3e3515d9b785a7ebeb4017265e3e62de90c8f59e79a559d6175402a8a1c1a10f4a5dd4ab6cb220eff898425e7356b2125c0477803f05b21e251120087c6c9f0d6c17f6c97d0671a662457ad01940bda068c1029ee999302b96a0e373420479a91985f59c356433d67fe91a00e5f63adf4be4cabe3e58272445e9c31a2c015b2ae9210adcacee2f05c665cc7fdbf8812c92c6c029174a22e1813924c4e90bbd2ff800d9996a39735971c85564185e9b2ee701909225c1384df268d8ff45c96823662843f3e9c096465e072b6bd6307073b85bcbe4c4492e46f93251007bd0e3aec3b9271ee856ca04e833089e3807b48a1b61b6c3f3136095ae45bbe65e912eeca5e8ca5c4aa0f10db888856f30296c7d400ac6f539b352023f2f5cd96923c13d92c4b66d4b0277316669575f985714a601b45605eaec37bc58405c15251d995e9d11ac14e395bb95c541da5711d5c5eb65568f3138b1cc1a2418b262c28118f0fff3d7ef73bdd824e1c6c541d03f927ea1fb557cc7f1dcdda93bfbf66de630d0ff0db5465a6b9b344208217befbdb7dc3be10b700ba60b3e5bb02ace85b105016f45fe0859ca50c28d6f197145deda2ba18c2ca19411a339f9a227f2d8a18486080a7858d200cdc528124407816171eda977af48962fc2882390d0c9f21568169897fbf2ddcb22cbee25b94256c1a326c66558aabb16d02c3740b3d4cb873b3b93ad8d517f3407ff727798971bff8a2bdfc95b2fb6c0b7d2f188b7028fc426c9708bfc6034a4c1c24e911d2c9250040b2a7cf25b921f16455092adecb14c99e3bac5370704e2b2efc1f822e8d4d9e2ae092b5c8abb227277a2322f2cb631b3bd95813d72ceea302f57f8643da67abc854536b7c2f1fd99dce1c8d2665ae34cb69bcc952bd680c01e463756066f982064487e56040127c7c8cf8a20ec6488d1b888dcee872cdf13b2bcfc0f2e52de045b76c429cd4999841bdfcfc80fe8b378cbd64a5dbafeaa6f08be9b6c4e7bc80457fb8974edf3743b913e3a257de6fa4b7a3f5966ed16099f28b631737dc59289e6b4bf9facddaa794623c55d1dc2c260a8503f117a7d087ba9434447d9a08b8df4a71cf43e39551c3bc7bdf747b57434ea1ea31b333aadfa0938c3ba40a97087bbcea37b2fb6f4b33ae9a2b863d157dcd5a0afafa47a6f15914e6d220c66be8626e9267a2fc260e8632e263dc386b8327ddc998fd6c492d6934420ec259ba35a475bad5b7773ec1c4cccfc2377a24bd149ef1d70063da97be44e342fa6cd238b4817358f2c3a8ece75c68b63ea654c16bc669cf3cf44da6aadafac08537b9788b2c1bbd844a7e446411ff127d89e624be390ba3b4993841b27b6c49d147c6a1cd176ba7d76483ad1b8138be4d84f64d929ec25eb27e00c6bc58c465082a87e02ce80595a18485ad65a224ac291bd687bd0fe6edd0d3dbbb613fdcb0fe3c8dd6972242c7365fa32333ddd40dba56da2672ede235f48f2cdb6d22dd03de8424243933ebba4bf7e5d5afc42d22cd7e941d80bb5ac9ad0b3cdfeb4ddb7246ddded1c229148093d5d24ea6426cd6b1a9e22eda221b24b0b3823e5b3c3d1383abb18227381bbcedd8524cfcfa758d9353a9fe1186dce6b24ed139fb2dfdbaec6a5ed124594747b330cc6c5298e01b9b826b208ca897571b1fbcb859545ae4539b1b01e5aa6517a7a3b07894452923b7b29b2bfbf7ae08cf9db5d3eb9b324aacd0b89bdb7da3b12ee2e24a46b2775240dc7d08b4e9aa677bdc3beee6a813e7a259e443ab5bf2299b58dd26716cf5f3db0872cf4d32642d93acd64ebb4ec2e6d7d944825d29ca5123edd8b7844e823aec4c7eb23c7efe4489f1d450405e99a088ba0b832a63c9f1df48ac1ccc79070072936c49567efc8597c68833e488fbfb4a01781fe4891843b9938b117d068e6bbbd4cbb0ee4d1ad7865b7de923c29c59dcc5b12337ae836460f3d1b8d4e37996fe8a14bdbbadef14e59ae4b7f690167c00b49e8126dd5b3cd1289b097eaf0ca46d80be8d608924817ddad337517c8d3226d302f93de5810557512bedc25910ef332099f448f21bd5e84eb437fd826ba4bcc3e9ac9739331e510beb4803d64bf9b4b3c68c3acea545561286b85399e200b19ba5adc8bf860ecf72eec08b753516996ebd63c678c320e79dccbf2efb1bca020098f7bb9cb2a335a01ced07bf85139642aa092e58bfc1eb52ba5cc8b069d9ac8517b5dd07234201acec8460332b2d172b41c244e64a2b844fc9080e32283cc7561810221594b49e2269116bc103e4208cc10c0c27c70e1bba208ce8d0e9a9e2c84ecf8e8b185122357e4e0d4e840d23344c80f1f3f5b1c3112e3c5832193446be48a1c1c59237520e9192224fe883e7eb638624427a76b7c901019027f2cf9810ff6e0363ed683fbf083c14d1906c8ef12420334f7e8f5b66d5e8262d017bb4ed0a906cdfd262e18249240bbca60d5cc5a1fb73925c5dd4c9631bdc818ada5284bd9c963da59e3d63ba465a59cb7b3d66dd2f842c19903cbb2208426ec0542cc4473ef472e24971657cfa545d75c482e24540e0584c2b13614106b43e5503956dc22927b5f2e21b793812322c036c293b2ff64bf8e514af827432995c0111290690eca54389d689fe61e95fd85c3edc146d2d35f689f190a65159c48426fcd79256adcbc81be3c0e98a3b4def8595957317cb5b6ce3ad588af8f38c6ea1ad6638ddeba7923cb1b90d810f52d97b895599847bd4abc04ccb3e22ebe8aaf38466219d3346ec02cb1bd01734b17f4c91ea65e7ea6611e7d0f9f969019fe4636932536c4fcc34bc0dc8778099863f552710cc501734c05e19218242053d0410c151e79c26993e7bb141df18aaf3746a69bb40f86d4018fcf6bb9decafc9c4f25cff722cf2c600f4462599a6e796f4549b7ccccbf36491ed2c7bc6d085fec8fc4c8b127aeccd6d123cf53f1a75766e3ce4e7a7b768dbd5edf17695412f1c95ebe69dca226cec34c2ccf5f55b8f5f1d86b894aaee783fc943be04ed2e4f9ce459eb8236d5a73b306aecc9b4cf7bed835f3d42543578c9a1871c6ed5d0367a4e0b5803edf27782cd4e7bbee3293da35f171f5a0adbe955a8f6df557561bab7ff495a296d2486b8c466a5f60888197170621ec7e5242188525ad68656f56fec93fd88669ed166f73f293dee2db586b4e360c2bca2c6f7583209ae00cf804e9436779883b999780594a9b14e8c831025b64fa89248b9c437e4e188190dce180399e526a3bc17a0aab7c4adcc8f212325fc721b3bd3537eaf5d5295ae983d1593e28b184cc5803e6e99b3fd9e072779a31b0833fd580d5eb0ba54dffe897b0400d780a7fb2e720a6a79893d5464d7a08e71be24b0bf79d28f7209d98c2f7e083015f3477ad0d5e47c9379f7cbed77b3e3484349ea34a471dc80d0f9123475bdbd65ae303711e8b2a6a1a83998f32be0153c93c8cb1d60e421bf035ddb2c437b20c672a8439b0665429e54d69ae13f01270e194b75c9625a948732567ed768961363c102bbed85692a2284dc6396f2b9573e267658c29c48aa65b1b5075a8de6b1d5b2b1d2aa55942fc8319bec793b11680b67c9bc5a52b1e5d839a49b323757c620f8f223f4a70a00d8f148d46b0870ba149a35591dce9e182bc5287148a2a90594fe942256700451e265917517206194d204803229e0ff2527282419801c182362831c29432c42f03c2c55ee28902451c230643654d05d90fb21f6441b220990f321f50616488385061840a2354f450c143c5112ab2a00209153dcd5161848a2b9a7b4550714422e15d1b57e01bf6a0a7681b5b344dd3b9711b41d687b7210f7a0757cca4b85d4abe72777a3e48f91e0c197bca48c907adad95ce94520ec9a9d4bae03c84104208a10ee6b55db1af29270f2ec01869b060e54c86f0c77d504697173a1feded4e72ec77bf71a55d64b3545567eceaed92d6453e97070352f8e885d998f1466e0302d2800c800e882d43bc2d705598b1e5016179e0831134a90d79fb0aa331939b06bd9c72ceb781ece5cbc4175224c8647e6f38630baf5fcfec0166899f173e850b95809a29e5f554530032059b81d79363ee141655843c800728fcdeca8b91ba2c976780a00e6bd8df8071903aa801500f08bb613646994a25a89434561354f1129de9af8a6134e4a59859c078e468c3031d45e1fb91be9eb0a7a8b751d3b49447b53c464d7bd75fcc46bd7dc56c4ca06aa69418e35c62f2b8a6d099c46cc427311b0f5ad1cda2c4657c4b1b6dc303397ec6447c210401cde6ad8b420fe98c8417c864b7e8a852da889797a7db3c10d60bc0948f8155d801bc2173ca83f11e8c960fda214440425160938da08492560b01a683db5df9d9be661a422158ef75bf207e4882656124b8dddd2d25758ab2da0808638c31aa7456e69aa378e6459e87944e4b12a9554a225146890439c408890449244a2231c64b9f2c21067157ec25269155764c423515c5107da218600ff0f33109159348b97d6ed4a994ad7b9a93524a297b70bb46923b99f9c38f1f7efc00831f7c74cf0f30f8e1c70f3fee555b70672c9821618aebd257c4d69a466ff91dd3b93bf27b3f9ddbb9b18e8cf1a0938931eea06996fc34f7e0cdb98d3b99b637f8457350458a37fe91fbef58ceede04d06e283c407098f1d385a368e96271b8d8b30c28ef760162ca883f8dedb29a2041e9cdcd88bc580dc4ee629b5b8d2222822821db90823d448214f4373d5f0e85153434343c3a3a663f77b53b05e5bafe7a494d2392ba5d6565ae79c93523ad920a59492464a245236b4b0e79c2de79c6ba8b6d6aaaab6d6bf9423f4f3b55a6b6dad94b5554559aaf204b686a01505249a3bc2084893848cf560923e407dd7d71a63adf0303ef002c38db7666735b856c63721a517177ed88161b8f1dfa1a78f1bd4698e1e3a7167f2833af47047a7598288a21033f9876691916b8c5cb95cff5aa746a813573a4629a10fc4aa450f9334128efb4d4868c4104973f43db748a6a71da5fdc74347a6f4ef47924c2fd32cdd8583890bd224d12c1484548cb656996679b1b728ae1b115b2e7a4aa911f41d8410f44414115b2cc7590e7a810c360d6fc8f4162341d236b7089ba6fdddb287b722a669d9bda15b084f59f6ec5e7b485d8ffaac5615ba46a3afb5dd30c3a77b7fdbda5fd775dd2cc606410829e86d2fbb8d685c9917397b2814023d93b920cc0ee1d63591e3bde205817e6d7dd0756134de7597285f7d3582b2ec5d61356cbeb7b22d26fbcd70e846cdf5f7622fd7ea7a646d963db4dd97af84c144a797f652d4346b2189442299dc64a36e516af2d72cb7efcecffdbcf766ef9c658799656599c9e106b3c4b0605f4fdf69568656b8109a3e213cb44ef0b0fa7df510655d0fdbb4d0cd2c6b74fbf770ebdf2ceb772fbcdbe8702bfd9220659dfa9d25780d92ee6128f46eed333994e11b35879ac8d9083e1bddf7067ffbd9c8a6b9063ddbe2e17681fe8255d88b3ce81083c1da255e42989a09b4a560dbb9e6264fa7e5ebba22f682fdde7b98972fb6f617dbd7cfe42db47512349321e87a5f4fc3c295b6b9eeb418285360d4d42c9df5fecb2e71762b6234e62d86490c94615373afb3837ebdb72e03bd418f69dcd98744ef401f892c9941d7aaaac21de8a08fee1241377264ec0fabe1d2a2ed46cc188e911f6d58255f615104238dbd80703703b3e8d84d98c4685497f7a02de63ea6efb12d347fbdcb70371fd24ccd3dfb108eb9af1e637f9bbbaf7aca2a69c82dc6186fe7bbc1ce4f6ef0c1a0b35e37d1ceb677bd2f2c631993b8bb0e3a86bbeb6d1d04bdeb5ff4b4be4d9a13b7fefd36f7b69879fb6a33d94ecadcc90c731f6648df9245c68e9dfb5ddb4e36004605d0ee383bf7cbfd97d2dcbb0d0d6267b0aebfcbeb6a16cc20f9328669d875611bfc253384580d0bcb5355adb9f6d5a76c6e0b95e076dae7539a45d2197d4060e9086ed737b77f34277d34873367d3f8ae56b8c35e1dd46173bbf6a15d5e5a2c8f8de07657965be4ae7fb48f19dc0eeac8f23670060f1f3fb2bc455bae4b07518411ddd23e7a45bec747c9fbb1c1d1c972a70a277464d18d908720783e84e08c7bdb737d4c4a7e8210a25bb4579712fbf5ee0db90c538c8e6bd1f622b2c93ec241724bd13bd8d1b6d72bd583611be2d0c01c9ce6e0e1cc6deac6a216242f7b2b826ec13ecc11741a395ae146ac1bc38e61eafa6bd02fe8e5ecb2b9ec322ff229621b533ed1df0cc11d1c1d2806291a309f3a5bd8c2ac702d4a25f6f94ed2374c5b2bdc6e9917d97e76bfad205ea6f8d48fc1229b8b8dda7a014db6e0d5db751f4a72c4b618ec16c5ecad972dd263167389d84c86588deb16ccee953dde8f36255e44d89e06a6f3e2e4f8514e8eb893814c640c82aee5743319b3b04b83b6ae896cc1f8f7a6e1c01ee0c82642780a7b51a23ac4603098eab0b98bf1e027ab706327f32fbc57b0a0f491d81226cccb8f978fb153d3438eef61d083a104508e877959361783266250b8b7639c51aee009b7d3bae12594500a6b3e93c1b5a7978fd8063ab6854eafba615ab85d4a9e5d7f8ee45a6bac5998b7284603f4429fb728c55e1a143aa8b197ec55d5a25f9fc975eb2e6aeb70dc3f8c9ac9d405bd82ec7541db6559d1aa2a09639e6fce080d828541e1c24c06579e6e33996e90c7275b51668bbd24e146ac791e8cf71c529c6830f1975f5c9191df63d0180f7fbef7768165c19b614fb8598e5862412405f96135b26c296069e42d1bd3adbfa6363ab105d270f7b6c2d4e6deed8642a15b1dafaabaf54046aef5ac973d71adeb92cd324138f57a32749ba36410c9934596b7301fdcfbcab22c18eb2159adbbc4ea2e51fee0b544590459051659bebb419ce624cce915794824cb9fe0ce44d337b1264b7883827bbdbedbe5f9f0327c2b277fd49050919377d24868eb6cae769aa5b3b24810792ffbd6c8e5691edec99e2c83544c891a5bbe986beee42ab7de16ca300deba187f083d91262265bef68f1b07590c6ccc9ad785db0af93579b101964c7a56e217682a9c7ab6c2fc7d06dee7a7cab6c30439a48afa8479ae502d121d4268b1c6fc55bd1d2b275652b87334143433382166ac359d7ba15baf5eb9006ce50b9ac2e0f45f05878b8bc99f4f77cc8ac93cec3161f675d65cb36eb2bd639eb9505439b088a10c442f8ddcaaaaaec2aaf6e61a02c002b01e0818787ae823b98b3c7c3123257bf91e50a1f005de12560ce7017baf598d0b1ca0eb7ea91ca63b2c3a6b2499ddea27f7a45564ca86c306f17e284e00dbc81379287b770979743e415f288cc42129145a411c993250f97416410b9238564a71b3087be84ccd66f64d9c20078476d86d86ee187d1081dbf945d9eea348b1584dac41689133bcb84ca43313bf0701589b3c3267578d8648e955d32880c929de0ba889759d53b43689166717119244b791aa16339b89dc4c131a1e3c7489de6644e73529ede488a93a5ca7db64ed22bd2f46a8342e00e0c8223bf03eeac54c1f0a639f98be756b764906651b93cc94e4e96e5a182a94d7394c7bdbcf257157e006c4934278f371c306f184773f2396cddf58eda501bf9cb53a9a5a49872cadf4bf926779a93af3629a43919c326b6407b9f0e5806694ebe83349d0c92036c8878ebd2a6596e288eb504cc3987ed65175bcad65da99d04c9b2833759deb47570487795b6ce244fda94a856c39d952248f3a25fa6ead56265fc7019f62f0c60fd66d87a854156c526d79d4a7e7d7345dd64739646a539115841f20f17f247929f9b8cc114ecd107c364bcecbe73082bcb3e86c1884e6f7a59f6cbb29388b1fbe8dd63322955d64a8ba39bea535189b428510c7086754b2b1b936479ca6a69dd9a286d80956f894fd56d127125deec22719aa5a898c45e7461302ede96b44fa2b3f9e4af5b4eae430e38c5d28a52d4854d39e12bbb56feb02aab9252d29fdc498ad2471f3843de4625b48b49ec672563923c8fa373d3575747714c9f3a959d0434ad7d91925685e5a30fec611e65c3ee1245dda78b35e1ce5e13dc5ddc994af8c71d17a242949c330912dcc95c12c7d4ee36ea6528844fd7b3963f3e89d8727c01f0ee7f67dade5d0c000c6db0b5dbc5a04cbec360e463288b0d71657968933dfe8a01ce9098ccd548f2c5ef05abd7b5bd5cadcad653af9fb55ed42b4b6b72ec1a49eebf7e677a510c3d56ce9672eb9e2512d9869aaf3866a6701403ec21498649f2dc92885c7c7c7c777c4ab063a18a14fce48ea1fce2add81284bc1d848cc26230cf8da3a83910f2563724723b19412ca059ba670628bfc508c38d6f25110741f8696b06d1dc027c8c686ec2dbdddd31c31cb4733437214d73f34c3437e523bc6996ee2599357047b39cec5fee1e11c863faa03e65926816ba75ef489e2fe288eaf348348bf5f90a5cc08866b93ea957eaa2b32a6201cdcde7496401cdf259e108deca9cf3d48c419eb7873a1e8cf926dc96b263c3ae696eeec813ea5053d05b9488e690688e1ed11c8d8f39524a296c41a61c782b14c7834129ed2cbcc1813950871e06a13b995a718322b9833e287d5964fa239aa502cdd25d179049028966c14e25162f6c1d616b4509d11ce53215a259386aedb9d8026ff056e8a5c8f60799c22264fa8ae92d86370f06d6841bdf37991e1a9182ad2c9d9505bd9a2069adb5d53b57d656d6c2aa7a652d865dc7dedd31c6183f9fbc2e2be68a62b79894a1f0a2b5d6ebba2e7cc21e733d1ebb1e7f5d98ddba2b5b6cbba83893ab4de6ca14a680c01eec4da67789f507f4417bb05364ce9e73ce0965c4d9b367f7fc9c73ce29e3c198c79670e5718ce6e667912b1fdf4f65368c3490b7b81f6414d19cfcb342965c08836c79c023e2c19037c0c1440e48532f0f7bc01fb026cb2d200fe863eb64e44a55970f4208cb5aabd68aa264d85ab98ab9ce668eb3d7746843810421376533d9645ea8f4914db3bc1289a40111c9f41fb6a2d1b5265df0a659fa8e6c4637ad018133b49c3cdab2dfcd1a5df0823ec25ee8bd0fd55a7f2291b0c8e6564c0f7a22ed7eb4cde427ba77892e1d92efbeacc768c9599b7db5bdc7bddb889927bf581991efa6911c29dc3c0f86bc0af7a26e138eaace49734cb062a62b0340407edf2836c07b2a2931bf144c2303d73c4c05009c70d3c4153983fe7d7b33288647600f74c6e3e4630063f0038a6e7a354d6c51003c0267c8803dc8cb2a6429adf0015221cb5b3da15661989dc2f2c8f381a75928dc3a4030b8251def05f77ab433ec216e1a1f97fabc6deebadd948052be697eb2bc751c12e4829e0f5e018d5c9025ba28924e4ffe4c47b9c95144505c8a9dd2a3348d7ceb90bd234b2a895c6f551bb19e1830f3b981996c310774c8149f2afa8ade629a44eef979bb7ae3eec6cd4b9c3245eb69ac98013354c7faeb131342bbfdc48280593b7d3c88a274a3b439f141a73846bbc53128a7ade188520305cbe64e70866573268dadd2ad92d516ddac1963865803bab64efb33b39a844918dd4834469a3dc68f68a4a353900be6b126c2a71ad8fb98688a44a277d44b007593c86d4fa7d3f67a4593754e910877d467b559ef6b6f677da8fa9cfda959ac611416fdba99dead4b225f20ec2e5d8394310c8d6cf08ae6e44133b8f0485440cad8f286bc0e8e204b88822c0f5b90258c419642b2fcfbb1c91dccc9523e868cd892c34fef6c4a0ed80536a5e0137eef05dd609e8a8e490fa08784d7de1835f9eebaf2ef3d8b8f680e893e00fc7bdd8c68c1cd524a4a25863f7a14017fc88d0811dc8aa10e1a8a85680e42165ca8652362cb6532fd1c7745218828c26616237a057e4ed9f86423471c43b10c03cc4d668c84113d9000b1e076170f3c0422e519a05b0eff43b73c20e017d02c2fcb2c02d99f8f346fe5e5061029c15b79b9cb1a90849fb7f2b2c41701ae49c640ee53b999d4608747493492e52bce7dbf52899767ffa04ffb984c11f2882dd0c78707843c200f087d20843e10fa40e803b7282e86575c30b78fc974efc39e70bb8853d3b2411d133434b048efd410e91d21cd023fc400b1a659b03edc948070eb0c90e507f05a62cdbbcf879766a1c2270617382b1c0aa6252020cbc713bcd08b4104ce07d781c906178a2c6d64419627596e59ae886c6e902c2f5bda34277b27aec85b1f8877a61862243692eef6b1d747deea2cbaa791b44f6c8953642984f68947c8f2edd33ccdd32c3d21ec35d7b86d608e4e07e99bac27088431cc3667d32c6ff2e8018b6016fec0253adada078b64194d9065ac7930baa704d6643e705a07ee341cb2c1ac81e7438419e4236f32dd7bad7bc570bb1b8118877b0b5e1b6b24cd8de2fef5dd608f5e91b74857c360446e077db66896ee29c9d2075e5e1d5288a0b87d7bd00b0804318b290a3b05028140f8943d0674fb0c04cae0d65d19e2909dc51a93b9f28529b875f6c6dae08fe62845318bb5e664126e632be9c370653472f549fed3dec2a6a051f1bce669235d336fedbd26d3297cb5cd6db6e4e81579cbdd1826191fd2dd48acc1f91f7b64556398e0ee19e134dcbd222f755d137b64c4e08e5c91258e44e2105b61d933858f1094bc5cdf5d48de09627b6a4f4f4f6c89d6dedb53098935b1a573f056e4a7a891afb88f005ac1eda44ebe4e10b9235f7371482c728591782412a9aa1ecd597b88104434cb02aec2bd8871bd7c61a7eb2f5b55dc22fe481a5923796459bdbe543ecf074ac4aa17ac3693cf4d48b35ccdc24435ab6d8224082ee81d0c0253716e177bba26b6c02513a482fbe0922c0f878c9ec3bb182ede7129ef8ef2ce7462bb26460eb88bc13d05772f1d0577a67bdf931f9db6f6c9b969ee84e58fd8226d856dd7e8e8e8bc9c05f6a2135b648f44c1f24873f2279be4692e8be664cf096bda2bdc3563b89d3492e5a794469a25e309bd626fca4b1ff384d32cdd95f3a3594e2ee1cd0996f3843b0b6d7cfc903aba58b3a359ba0683ecd12c3d487c924425718b66f9894b248d8c43641c92e5adb991bae6c1905813ee6bce84bb97c5e3c9f3fd1d8f3c6aa954925787974c3f6d302f574739d94e557e98490cb7c1f07c88cf622ff3d646e55853b5959a50938abeda281cd8830d05a49a1595436b55cdadda08eaa0b6759d832c3fda3a00647951d764099219068142e85b201012de73b97b4b5a071c23776f49de1ebcd99ea5369c0c73323c7c3f1942083f694e1d802cac8ad3c35b89b7f6f68081e1f625755dd4df2f8aa2ec7f6defd13ab6854b5da6ebd44dd74537b9b6759ae99d4db86b229be077afcb2447b38cae526934c2a7ebfa552a456962323d9af0f550c8c4e45936cab29b989834914d27dbcbd949663a29957e6dd94b26d31b7bc94a77e997b3ecc464cab66ee646cd2777e9ece4e5eca6ed7a693bc964966532afc7badbe8a2cd243ba6854b3d898bca28eabab2995d5945dd8a1f3d9353d9881a35f6a2994c26133e9d3cc6f4ec27a66737e193873e932f9313d366f565529ac925d2e8d748ab9a25748a129daa7c88a0b8b76096ddccda2076511745559455555545adc1d2ac53f8ea577f7b2979613d770bd73ef44e1bddde7eb495ae8dde7984bb262e45598a844f14752acb2426f30877a39b3c7a7f843b99eb6edd4cbe9a76108804025dd39ac81a6824cab2531be819753cb2d1b21108748a128d46a7a883402391e8813edaa8679b0824aa74b699c75199c2127ba96eab97abea6dc12bc6cb402757bfdeb4b0115ccb9a50b87e512a6557d8768a9fc911db4ed667b285b127dc1d69ab0ee6e4ead8568f55180c4e73d52746c3c6e6564854873bb17289d853a96457cf6670bbbaac6ea57d655dc72ccbc2f1158e699d9a8621351684b0a3b8b0938d2343dc9d208c651d5e3fc5f463e82b0b86516c084f7e43f2c362888f6c75632fb36737f602f30c4e7d412d6dfaaeed5adb6d8ede9469672af3e26aade50dc9b3e535b59bbd70a8a7faf7ae9f99dedaa839e6190a561e5c08b3be656530cbcf076a11bdb3133f036130d9df73827a37c7cef16af5f164a51de451258ea187f1ef7ee2e80aaa15d3c37732978df937f1a946bfcff71ef6b0dbf75ed3aa84d0f85eec1c7076d8eb9c0ddfd8ac38e6bd524821dc91b1531cd3cdf90ea9cc747befb13e8c1db45d176da1cbad1f8410d2834090bed3f7b1772c7b27439b94b92a06031f03c2177cbfba2bdd5ea6993a01676031f5a03fdc65358bf3d64227e00c0877c837d661bfb6eefe893ea63f4ffb33190728c39dccd82b7402f68051bf360bdbdb7aa2f835d133598bac103d2a0b2a27d264b72c79ab8b7104257e99b5c96996f77810f612e5269ba316f5ba61bf1bece26649195f6bc571e57b55bd8b632a0a089c11fa13bd8a3095935fa89379ef557d8fd83e7cb287a7de23ee6abcc777186b8d540e0883113d6222785df1542743892e0ce63d46be4b1e84450f771ba0aa2c72356915638d9d830999777247bda270e08c77d8513ab9a364557147e5640a9e92c71d9593a9ff55f9e4e5d6c9c418a95338b087780a83c11ee8da36fadda86318b63628d7f507cade7176795965193e5140a84d04c5ede2ad0e7b676f3285af4df60a751056e10b539635b3a5d95240e08c0ef664ea9dcceba928aab2371490774943f336f8186f6fbae53505c4da442a27cbedba31d4a42a348b1027e2f940c483f1ae664ab90af722868c1f8a3c24b702d7857550315cebd55da2f552d1960bd45b4cc9edec49722d3fb18d2d5007d4f16c46a20246c496208a987789725edea279d2ed34f3cc627097b0403743611b233ed9987966dbd24251c5133fdecaadd50baebe568c0bf2e53ed673ad53c7b6007ae92957d144d79e6d5df61b306f6086f4ce24dc3591495aac6016e56816d211b93b912e9b2b5de6451689f089443aa954d25e9148c73011869d4422754de492c9f63266f2aa64a261d75e6dd8b5eaa56d622f181ed9bceada5dda2a55954955dda8d9c444db5ec65edaaa6b9b497357c6b0ed867e37d2374c0bb73e89aa62b556179b5885d97a2b620f9df49a85480f5db435f6222a954a8779b9844f2637291d7b097326cf3e93b70e47d66eb2952ad236fac334d23693b551e855e82ed1a545367bbdaf21bc59f002c9994c55b5aad556cadeda7046f781dc3166b9c53c25b6da5b96ac91fdf770571f71746ed8dd97f146ec2aadb5d6d7d30d85bea9a364d17adb6e7c59d6ad6b61d62fcbb27e5dd82f2b74eb2fb68c4499955ddbfaa3edf58a68cb7007c232a6d0f67aa5316c5f856566bade7169a15d59533bd96db39ce5a22eaa22dcc50c5f9574925c67c59dfcacea84970fada877d79cc955cada3944225185f7d7ad5724ba20917c772f6bd73b327508a7083ef46c0b617c27c4a2fb18b23e0fdab66babb056b5e620c517a4a95b60e1851ef542425d3dd4a5c4be4ee60ee2171f0a76908fb8526fadd802696e7deb74e3aed94357741b7aafd8cddad315eac2165b1096794bde921d711341712d2c63cab53a85a938a6c270877c849f59468c7555028e56813d2a6eabe2275350adc9e382507063eca735290be279abbeda90d9e24b0b5831c48139cde9c4c31c9d66e9276b0a64bdb4803d54ec93e1414f5c7a08754028b8f0532748f528abcbda2e0ac9f161a1454e0c7269017b807f98c5570fec414e397b5c89537137b324db77b1a64a4b5f2bb66f88927ea277348ffcac8841cf1022d4c0aa6214567fd4037960f3409e8ed04810cdf51530f00a1e125c53e6a2c947bea259faf048ed183b7684102302f4f1a35b6c4b38a4b9ce16ffe8956eb1700f0cd2a0105cd8b255d6c69082834aea1a2cabaf960a5722971bc6ce94abf74db1c5c614ed27b654f8519890bb725189233ec12b217367a25b4a34994638f9264b7c13119ac712951d432c6e9b2b3f8c07d726b6c420f682109ee2759a8338cd4178141ed78487bde3630717ca1d4ff4e477040735553cc1f322924b5fe4a5acc50fcbe6a0769add2fc822a7e17ba42b84bc908e101b29d5705f9e32deda60ae2cebd65a95555dd5856155c55e2c56597b5595b5d57555c7acc7cb82f10755d12a56902467f959f1e32377fd01c14905aa1fb3da95698b7c2436c67d473c8a3a967329dcc95098bd284a0a8aeabeae0b77f5fa4cbeba4b402db9a8254bba56aa5254373ed990a7fe4cee1fd952dd52dc0e2e29c1ede092a6d151b3a3591ac71eed23ebebba407311091c2428d02dd075734fd46732157d408804096ed73faf079a72104b75901f10056a10a863c3c723cd41aa93b92024c2ed601012dc0e06b1acad9bea7e3dbde160104288bbee9b115c1a1b908edbbd9ea99291882d90a757e2adbd97a707f2d834cb4de3340bcc11ff543f15fea9f04ffff44a7c75a8925376ced0859fe6647e10ffece0baf44f7351c7ed20cf0f8f0f7cec2ce093c0c70812c1dd116b9a8b22b8367434d73fbd648b34cdc5c39fd8344da48bfca8a0105c789393a1922d3a78935d624bf35893e95e6bef35996cb655a060493e407e55a0c08a6c41d8a357e2618ee9f5c01f36b125be79624bcce995782226213706c9f171b60d8d1f305c98fb27c73f08dfc9cda53918317872434f15f95921041f99477e560c81060db72f0425b0fb5d6407ce11f2abe20448f2098ee457851357e40eea805d24f729fa00897830227ed4dfa3e6abf33256d8cb8b35462ae74df95eb70975e07b30b0296ef7e22a29a61468a5d46e8cf142a8036bc3958f14ac8c106584524ab933b5dcef237de4c8157342088358f23e126226f5e4754ef9f90be44ec694b3885fb77027e7b6d8832377322610f662d24c6eed2dedbd43d3a40e97fd44d64f60303bdcc5a58b83e2535e4ef9e95dc3c3f4006c3a9cb499cec356ffce30ca65a9b4a33449a5d2c97bc749f798a51394c943e9a4148097500280bb2e954aa51226c94f94d79b1e710763cf2887181feb2c954a37edb07530ef701006331fb178d3ab2cdd74ed3a946ae73092f1d5540d394141d95e3e8931c668ea1cd3d43f4e6e5935f2d5344b589a4c559a6e2201e09da632690240fa4d972728241396ef1db007d25d6200361dcec366fa0edbdb81741a15dc591600601d0e71624b6fc7386678d6594b379d74976e9dcee11beeeeb13ce926141d50b0e934f6e480eb491fe17ec2a4bdb54fec452b9d9c904e3efa49034e4e642e812e41ef1ad143d7fa8988c194623ccae71461a413094f8230994ca613d308f71329d73617583607fa693b99fc6510ad17c6830b27057172f7401cd0bba654c21792e640a351f3a0aa2da447ee26cd9288274d6ce15e274daebd625d3d5d2325965b34b7e43af5115ba22d511fb97f24f25c5ac423daa5c529e5a3483ac1ed9a075debe426459f6639d178f07c80293f9d82a699a774318f5eb1d3bd9e09e5a5d2f64e4c348782d2f98472d91cca2166824f12a39c9ca48444ba65b2954a72b34c36d31fe68093ec27d96bf65b6bad8ff6a2cf6494ede4a49f6c33d964428a1928bb62f4893d20276e273b53d72ded753553ca55aa0e0f2d001ccbeac97d0b36a6d58fec8e5b2f1f1ff3284952be538dd2492fe128524ec25de9a71aa49b3cc6f41a26273d8674933f69914ca6934c276d30c76b622f992563ac51abc0f6a2b98e9732c61aab3c156be49a7bb4dac7eb68cdc846cb69eec4148707e37a476ba40e499324779626463c69624bca9b5212577a94336bfd69aea5c4758bbaa4a2c9b44649ee2fc91a105880889fe81b98c932fe46cca59865d7448e19ddf437134370bb1887dcbf9a280e251313d3ef7662c2b2b967d55bff6a8da9e1cee4a71ada4b8f39f9cb27f854a374ed31da4bf5f596a69d5cdb608e22e52e3d4f2ce843c50f1286643885fcb018e2932d7b51ecbe985cb4590b7613b6ba4a27d5c94b5be7ea044b982beca59452b5975e2ff3a2622f27f8a45dc3cfcaac8bbd942edaee9f887488dd5ba1cbe4143add64eb1c3a6199432429b9f8b01a9d49f82471bc4bcba7dc6ee91a29e55136d3269b135d9eb4d9928bb70b9275afd7b75b684d09e5a592754ba55b2add77be255a135b6493684de928d836572a7dfea2bc622f28f854bac9c936da54489bc96669e2f514d3768964fd49cedc4d622f15c36a74d66eb25d77bba24d238d36158c86cc1ab6a7f912ee649e2169c75e4220b8b46647b384dea73c9a45e2d3d45eaef67b91484492f2a1b991a23667f28c9b4b1485248e12521ecd35ad09d1dcfe85599adcbf36aa62eaeeeeeec62fdbe6f0299ebece64a9e5d700219a7b3fd920e2c178bfac1e94ce41093b8a64f18d616ab83cb287bd3c5bd9ca9eb2a7286beb9cd6d66dde80f6d4f6624f6f1fcc9b12fec88da5e1be8ce513ac3e0aa6058804facc1fb8c4c6891e28d0c9af8a1d3b59467e55ece0c9981a7e2e99a33998733b950c6116cd4d9ee6aaaaa2d88b057a4cfd3c088b6c2ef5991cb70e479432565585617ca792214e4e968f3937e2686f32863b1abb81525ce803e67863ca29fee5f97c34cbf3d1dcc3d27061868185a1e156d7755d57160328ac33b48e0cdf3a72efc8b021a556de4c1f4fbdbf183f238cb16b42bed7f3ddafa947e54c3adf1b2ab9fded343fb3c45027c39d0c61900c77662848b560c9b69c30829afcac00028fdcd90ced10ef2efdb628645ee448821ff94b86a0b93fb13bbb527a88d5a0b9e228ec1fd6c40b20a490180d9ae5e72bb621253eddf804eb35cb06608673cac3889f32c6dcc7f0d5b0019768d83162d85ba0dff1a193a38de2525ae1688abb53ffd967ab8824d22e2269966551d2368fd069e4c8e421912609b4912e4191748dd093f0adb0b9780a34aa1689443d25c5d42cd4744d1cbd45daba2660be2ecbd2a8d61cc534a539537397b56a04d5506c1841f520d049d4e19d469a8bf4f66aa66d64cb70bb67165d8af0e82e3d8d80ac6df2dc6bf2348b853bebf5f15dbde491db3c62bdbe9b46acc754d2cb306364b8dd34d22c348a94d794cf23dd9282bbc73cd22d1088289b07911b974425710b3b8d90f0a9621b33d711c6b6703b95ac9d9e45ce095dbdc19ccd74ab545ffa6b96d2db84f46e849349b886b0212c18ed158e6c6efa19f6d235f4bb857eb291dea2ce010281b49d37bad7701ee8be8a0e04ce40f93b0d213ae1aef3bd96931fcad689debd07028970cc158140156b22dc8d44273df4ee37a98240352793ae6518cce90d3a914227854e18e957e8440a59595cd1bdf7b173687527772150e8a46b244d2777a13e08f4a785480fdd8a3b2d27872a7e31dd85505c80dec10cbaa12b0ae1774dee624bf9c956af6df72e1d23474cdb5ed680984826d27b267c22bdfec6fe7d85b0a7a4bd1bdde4d26bc5a1d34e26b1ada8b46db5b5450c198d04002000d313000028100c888462b1603c281986393b14000da1b24e6c4c9986490e44c818630c011010000001000010040018d4f3d8b44e15c51b36eb3474b49d030e513955cddc970e0217fc91498341fe38757064ab78827d7ec7751ca8d6843fd83e132d52decadebb85b0d5e8877b363d0fc0e6cabb5efa74a00f2444ae6b8336b0ab4f6744347a24fd1652fb82e1831171e58c792ce8c2ef36afa16302f8aa553c679565d297fb504bc22ed7e65643dda3791f71f29868435f7d79c060b2d281903a6e0062cf35bd5d43a5db27add816e0e8897b736259b506fa9d65d73a78b2557f58171c262099a1a3359b886e3a66f41c4e30f091b72728685d76822d164da5d49091ce08a1e6207ee0ff80f10398ca30fb31365eabbbf15d7da355bd355f3055fd47486cb6a013eda35663a0f7c1b3fa2bd5e933f0c3b2cc66219361c7321044cf43523c3980ad0bdcbfa823e438b0f2b161ff3004087c54e57804b7e8545f90fb93f119c8b3b5455f4b193aede098d061557fb6e02e0a0a51f03a41497887b19c5727e8d7fd0af838615343c7836864aad0240211e8b40742c0b4d4a9cc331269dbc92a91b87b1fa7d5a8679182e736f4880bfa923ad5c704e45d9dfb21b8fce90d644934cc673c0c796e98489b1611ce1b22754a9826ee74a1385097e1557d8de15e5f56a714e81c30a1c021cb252a2194a98da15f3a45acfc4d2f52e8408d11cc4ab6aca0a0e3c103ec6f51f2562777588320dde1b54ef0efb4c104cd6772baacae2ccb77c8dcc702e62a0c9e400a27f421824cc052362805982345cede15e9b3e2fc4bb75c4c3f314a2fcbb701750e177072ccd228fdd5c8888a82554dafe9fbd14cfe8cd940d929ede5a46752e403add92465b7a55637e0a245ac0a0b28d6939e35400ee2b1df118beddde104af32414f4f1489b1e2f7b4bb6caf059e6e69e6f1cdf98eda469b925b1d32c17a5b31572748ceb32d4ea0c8b100a83cb085153e8a88dddf51d90e81a0cfb15fee5ccf4ee073135808463a0267fdd085b282df928fe283f7180582b473896b574a2d259722da497c4b41f404c9c7078113110f9646ecef57b177751960b849a12ae8bb7887d20016c51e8294a43268f269e9f7fe243e1898ae0375acf02665f23c7eff19c27926f8433a35fbb7ab8e9a021ac9d505a05bd7593b98ff8b7dc8582a20f1956f81d8e3606d95318352b9dc927f999503fd96d2e81528df1e66c30b8c95994586715e35279792362f0c482aa08ed7708b97836336434f32d7e19eebd00dbb1c0a4ce9c1887c9a08ea14b8dabc0e3f69e551fc7da1bed1b454e6a410863da5c82bc46b270d9456696d5cbd13e5ec0966fc33bf640e240e66d3773becbacf6b5dba82bcfa2ba6368d12c5cad8fcc97d3065b574b0334fd8e8b545a21f8adb7c3be5d881f1cd4b216e9c94797383ec3a586d4bc82ee325662d2714207d81f31a98d5a546cce8adb90c592be968afc84a90189df285c3ecfb5106ef4008c2e995d530bc87999686c6b3be0f29d04710a86cb5a69cc93cc35c1014a723281c1c438d1a41211fbf2c817ca391346f18e862899f508425acdce14afe999d5ec178851c8845ce652aaf3c47d74703bcaff01078daadca9547557831438f10135f60675c8190639a843047caae8d91c1476056bf442d0359a643cbeaab3b1fd7b79a10c6509acd760f6a000d5a2b45938cfec461e5e1f85d2976b59c80b96b7dd91c101a3bcdb60971a3bbfa5c2dbef136e551c057fe185aa4dedeb0e8d173bcb49865fd115fb9c9b1f667baa5fd0467af9d3341b91ff42cfdd4ff8c7d04c3f56be8dba23600404afffc63a03467641e10c9612897034e20c01db0bba3fbe37ddf09cd6656e4be177cef296ce83c79c1a3c2b118f1e1017adb2c11b68016ee0377630e3a588e7a038e4708d3b5f2432d93aef208f58a9611fb935039e7c85c9a372185306e72385ecf1578a36143da3730a329667d33c234813d8b0ddcb0365a9023a30bb00c3f4e63b0a6ec4ff5ad035a2f5adefec00ec11ac5f784d1fba4bfc534887390f3ac94c9c5332da1a82211f2a6fd2e01c4ac092d900afbdb8ac19e8f1d56df257a8149a5415d6981a2727604abd10f88df5b9c4e0269f540bad8b8f9eaba22cf34734e92f4271e68bd74e839f934674edf2757af695d90742956d4e6c0e3d48c0cd5e41929ec7e842de0685ec9c2d6dfafaad3d73dee61b28bce7f1ca57630079d806aa71424c06594b59adc9a2eab13418cc9d007f61621f8b05dcf00e7fcc66908733c597cc20fee048319dfec710c40a0055f20e3047dff753ce2c479e6306253f037bb3b3dee6aaf1e33592e6b040066d2bc3d511222c9baa2800efd892804c5e86e6963ada4eb44710d5b4d58328c8ca275a189f97e96ec9fa9e5608dc5a94c45d5beb41a875886ddb09b750848fa0a7431a9e8c57e2eb8d3c6b69a600469e379dd0ae347a424e890b6d66dbbbf14ba6417a97f80b00738ad0bfa6180c4e27652667ed7c36064a41faa101b7b3a814e775b2df61246e8920cefd7b23dcb4644d52236248521a9b8bc5948d147d9a8312fa90d5e38632e0214cbe24e8486f593767d8185ac0fab4c5060cb5418b9978492ac24a538b3a997c8bc12936aa4345a951a6c020890d83031c2962a61b9b01d0fbe211411d62d8ecf2afba9bf0eba8092dedd20858190f532a66add448456c0780c4a603c49a1e1aed2a953b959227ff043b172a9dda00e4916c03e5615aac650ce8496f269386bcbf968e95400591ca595c59e48dd400c351c1147506c7afa8be11c7c1d287217a999298644419915df8043a3dce916d5bb9c982dff32c6e352b94eb83f0c7f9f1458dea70b35b7c0670aa834c0202506b28526b46d8e7f44248395c7d8a512f05a0b7be2d9363748aceddac8deb4cd893dd40adda3df56c44fbcf390d8aeda2646da7a8e1f909909abb49708c51dfcba66add79f2791fa61c72f858d435a48daa3898b3f9f88a9106f2c835912854a6754a162c71b444c5063299c6d4d3525c7b8eb0a83bf1e124266f51cff439396f370b60b866fbc8f296eac4efabcbd78bd1395782df932c2c78fff3c2f798323b05e6c25df991fbfec5f420886386abb031e1663006cf87b8a07c085e42a2293d8982f53014118acc5547815bb3390e39358755038ed22ba678a078593d845537c578870fb64af5a5298d1b94899af625ec8e9ad0de613f7102799d7210a757d4b9e01f35aef2ec43bd4bcc087ec5d47aad5607cc6667052820fa9d4af6fdef44013d8a573c86f0d3fbdc01b2eafaa73be4d3c436dfb1cc730e24827c1e8abc7d07acacef24751d49a41248ce84013e7a9fed244e397bf7ce7d59844d593ae0d8565e05aadb63b331bbb922954419bd9a8d2509c841599a84ec1498b8fa8c0b216594918dd94630f34e3d1774ecec029b92e3a850c065f16818d21018b68e3e88f4a2d0b70fa2f25ac870b820e74a8792c0455dcb482b46223ba66ce7307adf653f497089c966933a6dc8007861f5fada900eeb1bb9d515fa265ea66d72157f1f196e9cc649cffe9b66651ce21c6feb21dfa748ebd75c115013973b0e91938365878305898a5f6f96fb333a7a9d23a8fdbbc0ace23c66a9f33cd2cf992910df44f550a109028fa31cb77d9c929cb2e3dd4475132a2e1a37d7c3a300a21bb8599f2fde6c1127127b7ae0f10f1a475056d969d7a37220a4921361bb646ef04f358ab22c254cf62b0b3a444e093abfa8ee41d2f1075ee45543dad6c9a1fc4c961dc5e58ac182a426fc692335b45b261fb2fb437401db8ea1c1abbce24bca604e9188c219422ac41d01aa1d0464d96c5c5a10bac7a6bec649033da0a3a1c9bcf738e5eec206f809f013d17f849a22b5dc432f4f0d27827ea0724d4f4b59503f7a00775a9fe5797f79eb745a80ec1d7d36c9c92d9056367d2fbf471d3a83f9639af81e73e2f2e5a2760957f774fa40ade2c57af471b7268912621bd4259e0791689b6ff7180adf13751f6d555c56073be47f170f7711a00bb30f58035b11886c74e7ed988310d429987912e0b813d8b41065bbaf387fdfcbbc48c9eee3afd0a4d64a4c63a50667423038cd642844e459a36379ec701e719ce2cca9bdb9fde13ab1317233c367d951500154cf97999d94a0c3e03905f10a4b0805329dcee9de63152f6d4368f4f63139a772a4f7cda26bb9e742927bfede05244a6ff4e6347cc14dbbb03902095c134460f7ac4755319941b0a454db79400528c247947aa85d7116e0863ff11d8d2f553edee5997aed4d61b2ab663a631f56d21d13e8f7f468119852990410c53f161099a54c0fde0deb1b4637cb41f1b3d85aded0742381743cc0a003514fe12c8f88ae1e610968262bcbb4923b6215dd1b01cbf61d26a52ec7582c4ff78deceb9ed84c8d6c0f1255a642a5c1e12df9ba42b4a6d9e61fe188f629784e9db9cb01686fb6acb46c3ccbea48635eacc597af153e9fcf028f19633e565e7c5e19043572e330e55775d2285ba2073fb44732b501b1de5adea1a8d555a7237a292ce7b2c0e7e025a14526e2b6b7cee4a9058d984838d24d5250fb397a0449e920bded150d8096af831d1d15ecf02977fd5076a29adf04e988adc68d88d1c9c10de38242495055639f2744a33ef44f2c62171a201cd3442a2ec6aec823cb241bf324c8fe112a1acb07612a2e91751d1fc2edb5d6a4317a3d5a96dbe5840352388a50314044a86ef63b31d3729ecafe4dad240738da386f412c4515b91b8afdddd8d4ad6d0fb580e967cf59bd847d6d2797b9f3bf35176308e807476f2b80bf03c26331b5a3292b74373ad469ab843c06d3f2135a1632442fbe6e41055cdb7d7aa7653a061811098df7047f3fd8f10594c5635acf95d7f2f84909557d660382bcbf1871961562928a8a9bc42de260053439398452369e0480fd6fd42e4ab29da70c1c5a2d99b1493546f7318b5175655cb4485b9947543eeb17b2b4d6c91df17da9806862339df9331a2e52d81bea5ab909aa0e4bae7ad22f43247b79541a1b27f4c7f1258b99ce7442fc25b6fd1bb300d9d18df5a56023ad669990dfe7553420905e7c2e5f2d133cf9c4446e29207f14fab57af7edae062e249bb409d1e189d1824c0d681b6d1fc204215b3eb5ff80fa25b4f05a2fec4b62f80d9e1fd3d81aa3087b44227626b41914049e8fd53a7088f6cba505b006ec4024bfad47b9869b07baee2a8b8386ba7ae58eff0078af3453f86d1295ce302d90a46f2029e2396f5be08e4d8e5c028e37f207607d416534c00575ee8a43eae7eb1df90e990a8c69d98096b983462c0dc55d0f021cc6435b4ea570df79272f7b2d220d19698c8e8472b5c9fcfdec290458acee3c5934d39a95998c38545748963b30540c0f224e694bb90101385567d78131439d0e031a1b49c3272644c6ab21013b1f0ab31a7502bb116f9ba1afd95e93d7f8fc91b6f013556ce34af85185a975bbbeb773fc5de83f3cd94ff24cbf9e4f36f0014cd690e7da73f4d62927f04b42c6471364935c16349af4e47d4c9573f87bf0d1834b142d1cb134f7be1e49d729c10b9bf73e29180e9df8e5414a1beb7812194461047bdb258c8c3273b4fef944546715ed075382d4511860fa724e6ca022151145530ef90c8c54d6bd0a51a3f63955e32c94baba4925a45d1c93ba51312f95d85a5ea2b305a602eeb59da8ba5303ee66ec0b6ba15fd8fbdb7534c0648dc198a5bc852e4eb2f85f935b1ea1ecd531e8f3e945eaf750337e3faa118891d823f1001098f33dfa6a8c6f845598013209ff449d4a7c1ec1c43325feab58a907eef64481ff63c3441541a50676c4b4a5c0048055b9368449c3d9d46b6f0aac162006abac96209e66a2a3bb5706ebf48a592a31863821c065c37459a697b761a05f9e184a8b8bebb62d431a357e4cdbb83cd83f9a738db91153b1744bb82605ed296a6ad482e694514793c87eac79420d81a61c545b2278049b18ea204c3490a07172ae7b6421848237d0a95be46051f097c59e8ed29587cc1cb3a054cedabcf035de31102c2eef62594dd6afadebb90e1a825ef4edd7593be6be867217cadb93352296b0af893ce40cac4759dfa33acf977803aa23414706f2211ac53d8282e68a736d9b2da93535a1e07ef407aaf26eec75fd8e6f50562bd60f85514a607db7bdf688b19aa17da951dcefa13f1805f9c5b451904097847842e41684a1914198a3c1e85f5cc320c751b2696e936eb0c0219500929d0250d093035881a37f4787786b1c521df43e270e9eef53ede0c505986a7666766b9aef8e5cfa249a77aa36d6fafa05b284ac787a64aa30e1359469dc6333cc3b53e0b83f094406ca47e665bc983aa23b70822e796d232cfe1a77e60cc1e73a88a4b242befef5f8f9e4953c552156176dbfa6cb14926d48244986c320f12cb9496a58eb70b73809412f51d465c7713d7c90ca818af49e24129f81f59a7402134c8cda87f1f40067e57f3320b438e6086afacb256986743c3a962d8496b839907034e3f0319c0c38f20cab802ebe14ca4c23045f1fb6ddb937320b15fe294b4f0095dac63169ca77045d15bc750d0ab724ab188f303c8b48bfd115416ea30154a2db07e3baa60e794112c3cf31a3ce758f874ef42c7c2e1d5704148c6035486be410dbae426050380aaff6ce9fb783a614d5101eaca23011a006dbdb07c2a143c0e039b4276cff66745f5538273a3c8ac123c7310be5507820dce2247bedf53ec5e08a2db4edb083c98d623d687005664addadb48452e20e83af39cbe54c8d39fe88d1af8d0ebf4b3d5572c8bcc9949ae85a9a01963b2578d7870b129eccafca9558453e3ac44f3cad841ff4f7e30805e987efae60fd2fed051cd7a2a601c75e5824ca3b4e3d01aa4fad13f1b88cd1b78e250c57acf57012c8879c1ef03aa6d6daf1ef647a60a9d8bd9f767d48ae6e302071dddc64ac049966e2dc5590586b681e0b6accd57e2b58c27d1a8bd5959366b09a99ab6fd1683fba1f25ef42adac80932fbe0863cc152dfc377434c35faa0021f5ddf15908f0e7236191742ed5a3ecaaf0120707eadd1889a9809cb021593db39421a283c3655fce0bac2c2b0160c8ab703c70802ef25d0e37e42517d3c0c2dd82d4ead3ca94cabf0a63839ada04f30b8a65cd556677972cd71d470f99c23e464c6c32cc8bae147a1b0bb171c5b1796a88774585e7e5b155230e417c0df938dfc896b6f4b0b5a9a2f61077d3b54e47fce85b71b7641364105a28297c184e00f7b6ff92f067f95ad9b3712e0951d2d903a0fdc5c77e3230fa7d82bfb58c03dd6a811289c077db6916d467897bd1d33d98bac8bb518c9b0371b29bf85920f8791503dbd790aef479937ad0e4fab34e8d7df475b562b7f8ef0b296c83a261011c8c8c56effb2fdcb476ec0040dce1f0ada437f07d57f721827462c78ffb62642cb892555631d4544f5451a377605b06e903cc7ec8f9ec342a9db0c746a9f631d06e52e7c9e95e5842de7bf4c76a87e8a5648501365a538c95d366a0ff3869ead2804bafdb26cf60174257cd56791df74c298527911d7a33d80dbe703b40524d7705a6c049ff4cda1ccdcb3745326a0c0af48f16d7e67552c6091386fb09b65282697c76ebf1f74675609050a663735544dc42214f436c2239c1e4ec0c67270b47ceb81dabb070c40d656238affea8bb7be6f2aa6c57e43d0f9f080434397e7f95ebcf737eafdaba7d38f4b9279a922384739490d1f7d88f88e45ee6afd5cff7badf844a38c86f3ef0ecdfb52b9df144b0eb3c39905e10e5cf77648b1bd9623a75ea4cf564dc7e4ba276d8326edd38cecc9d9d67f67eb1834fe294a96522414a576ed3df2a9476485128c82292f973172cb131b07c0a602f928244a0a023bab824551bc196795267c4d3ceca32ac365d3a61d22fe6b2e5272842c47dd79ee2511c8a27076ea13fbe358877737b82003bf065f4f17d1800c2bec1faa292639fcedea63231a4f12118692730b71ade94286bb1a7b9f5ff1e2c6cc4ac0df7d815977480f127c9e3072cd022c8d11ababfabe65018865051e35c9ea731089909ac06f0fac50c9f1be070b12ead3280dfa299534f2f8292e62d98d30a0cb090f11acd52bcf95e2bbe9012db75e001df4d57bc839c94775f89ec6232285e4839a81594ab21e2707d5a645fcaa13921fcf19385404dc929002bfc52fd47d583013d01953697001985ffe1a65639c2655ae926243450ede4a81a9e8ee38c2f97250daf69734c4f2298caa098c34c4c41ba64dee0ba8884270e4ce623bb5edf3c0fca3613044e7527e5c08ad867b2e7c5f8fb96417c68a17c23288d27d89861a8732e37360007fc11b8320c6325ce157cf0e3008ca1176f7a1ede953ef94ad3b1da00e02971981df701de6bf6a90079579b4490d1d97245518b07699040c542505a824386e06311a8718a78a315cdd5b1274ae450e63573835b532b6342f1c230150a265e54dc998c39084855d0222ee9a8cb9c9c2727993ec9dbf2feb0e8f86a20d082e0fa1d6f3aa793938f6dda93f01e85a76a5f98730f6336045c71c8156e967c2c7e28a0fbe1956bada5e8eced1c52a43b3edada8f7bfc8d5ba9ad9905494456e2a27e7c0c31f4cd8d681498c239324c0ed26d073688e65a14737ec67b4222863b65df1cbf365e829ee76023e4a6cc00551729dfa510be76c3f28d17f5445753d1583a165c9df699f617864bab665e5ce4d0011aa13e41e95b043fe86f2bf8c17b5ed0c607f9a859c47dd51a4c751e2884ad041e4358219fc4f135f26fb681636f702f29898177cbae4961b2b015f01dfa1331101b6a42832f4ad0cf0fc138522a5ad63171212d8176ad2953a3995cf73338ea8c5df905ee93983fb001e95decb0ba9d4619490482dd9eccd0000ed84d63efacaa8689bbe5553ae114a2f423d99f1eb7606384c756e00df9d7083af4251db259f51ab65ec1a569ce7d649fd5e29809070442ea560d2dbf5945dab89a1172ef8eea4cfd45aedf22029a9a7e63d8a9c5ec60b0926f9aeda6fc039a3bba731bd74fae83af298d04ef386b2227db162d383544594efa415f424deb31f23a55ec8b1cdd544f07ee7478860f4082d54875d5fab4dbd7c97f8e2ea4a348cb751df501d0105b3ec1e942241028c6c323a7e6cd83c89cc306031816b43395250bf34dfb63b747ccb824fb800f1daffa1141e546ccdd78214b1a4d3e34cc70e7f027b9fd3abc03872bff08b4487fb35ef15947c9da7d26676497e9943e9956d65570731937e3cd60908e50b0582f5136618e38e07f731f071da635b7383408bae3158e015425b4657eae04bc7e87abed73161bce3f9342ab740813842fe56be7b9be6716887b41d095b73ab0a2b4b298b77b7254eabf087d9825ad5844ba66c8000ccc5705bbc2de541e39155ec62307bab4e361ae8deca529cc2e7f0b2ef2e77b8d1d310049beac49c4ed74fdba863534137d60b5096b02809d51fc4d355cb9ea2f37ed793acb9ec2d3c2d128c2d903bc11bbc7512b2229fbc91f8e4bfb72e364e1813110cfb88ac1e9e447c5ebf50b6083f606c02464e422edf93c27d8d4bcdd748ac9d067182d5ca8d416d6bd680152ad30f204a11a0309c923d968e2ccd1b10fb305f4007a7deb6e0092108a779437a2fc6782a7d8404c563e8be4981450f9efff7a25f75a80c16277df8b2f6a5a47a6028bb5901cce29e57d0940c92ca54d37a52cd73dd87bcf9ce9ca598519fd82b7d89d6d2c183fa592afba07a02735c2277223ea5475f0ab219f12cb4cc523d356868ec26b2832aa53ecb9615f2e192ed157617804d0c33c9ee556543664497e2a41bd7ecc56f28ea3c855af6115eea54e665f50a9e0f0da9d3489427ae36a79ea596a4544fbd4032802c26853599bfd3095297ba22161770d8552e0d3481267477cf392357b66b887c9473bf813161b490dcb2de9a40046f8ee9304ab4e452826c215ed95e249732175fec2fae3f09443e574168de8f2ee3f482054c3d31b776e05da9dee60ab3d96eb17861ffd5f0c9a5c55498636ff2ae46645ed0d9c5de880f88019e6f74e3606d44ff967d971f8545dd681d7fd6aa22a99727b650e1736babec29bb8b907c071f387892c3e962d07ef97297af991c88941ce582cadc1cabfa97055c4df17494fa7810b5f55124b458018d32a88dd6a2c0440de8a2dd0dd5d7a2ed99c8f3c2924eaf0e82c2cc7d1f450f8dc44c539f9206d8b167b11f96be7a6ffbfdc9afede64cf9a3964b53467047a641ba611e0eb899d19a176b631915d194410b02b2e3a0eef27af1e7e10b103d0abc329e6515369dcdb1c38ea110542ee42d7cd5013b53f6c11742285bb43edaf98dfd7fbb63e16df341e7c93d05d68156c16c490d25d57b8753c4271670391b3658b0b6dba4a7f7816995796b48e82cdacf3ae7100cb02f4a0d724ff2c22fbd95d343cb551776e6b6329e55eb8fed880498e004099df4a3ae4305d967803fe16b3ef72a8d14a0a641a8e74a708229584afc89533a636e6567cfee7e3b9ef6fd9486ff78869b6fda446eecbe800c32ac7e0bdfddad29bdbe8e0fd81098ed96c57f47fc23d744f2e892f90426ef62c15aeb1580fa72d5f95e0e172fc125bf6cab7509779b03f43ccc1cdebbf468aed6fe86932a3d4e8bc663e36d262b18666090d46b33d4301af094d5a24158e65d543fb1ddb680bdf66722fb31b81cbb7179f3569bcce53046c423ec4c20bdf826e41b0e6e96cac9b0bccb2a5a516077cbe8dd31fe87682cc9bf5ac109ef5bcaeb7a0efb9e68486a888646a616944850111af7c1781446c3f486ebd01bb963bc30b51a30cf2fa832d7eaf8c81d9bbb52a2a6a71fa52aa59fa1cb27ab14cda5e23c1c18ec08a44b51e5123be5c5d20eb73c59f2f827e940bf197792c6128062933d336bcf063c7cf96ad5fcc4e8f9b09f6260acd7af767485ef16eeae9a07a520131ba75fd613a5b4a1ed8e318aeaa10db46a2e15b68f69d93920138162f67404498a43cf046d8cfd68e313ce513862741f3d1eaa54187309164fa5d0765db5c220108dd77361e1e223ba8df6a189232007bac45c75d28da23ae7abd382dc62070c508ca248999c62070bd86114cde24c452666664c45904c2ce5005207e06a5e77e33d8abaabd35234760733343aefc5fd7adbf6c67809129bcaef4ae04ceb8d1014bbe8839cdbb0800845925adc17cb8d130f6e6b5a72fc4cc774ea11ee475159186fdbaf47b61bb4197d5d600b058019178fc397b32c1a6833f7ef15c81f381a45350441d9d9c67bd5962b625c247219731f4838092f962ff42224b100e10aa6d539cd0eb3405b953d823b277599554a416c797711ac0089390bf15c0dddb6ff24214f982ec9e88df9d9707d996464353abc67ff5bfa0a4fd539c166cf4c941c4e2eeb0f7371ba25f28cdc1001e00447e7c895651d8cc0c90d7fe0fba6f31dbbdbda36be0d657d7762287a51b70bd925f547b538ae1be245da25d74076c4bba49e5aaea10cb23670b5d1037cbcd6bf7deab5bd4ada001f1c9f71080eb7081a5d1a230ff2eb090ed6ae9781b273b8d0e4f465f07df0c8b09cb5b80881fb077d436df1213614942e16b2516a23848cea14407e5d41fafd7c5cb590d6c6609ac29fe86058fcd3cbc7a40d7383f3b116b631d6972fc7bafd6492e6a7a7e50a5564c477fba868f8297490c6c48c191503e0ba34d4780d577459878465bc70cf8636e12d318f9fabfcda0ad682b4ec7ec44f8a661602d2a90e550cbf222912afcf0f7222485413d7f9ab195ea55c7810c27c410880f79c38fbc505a8ca4f2ef4fbf2d21c7c186b9f8276fb61708a600a786f33c6f8ab36eb1d723c08f246aa9416446b30d64f1f7a4b5e6eeea68bc3dff0bbdb0567bdd20db6bc194cb751418a569f6d8ea8086895dc58b2683f3037b83ddea43eb460112c93b203ec13cdba5ff46d5e35a99fd911e8b54a42a15aae6260f3de19b54151591869054454c2822fd328eb6ddc62e49d82fb740de58788804ca6b073b530b21296c8a19c0871221d936c0c95eaad450116b4db40b10218ea8465f618e83e54ba708730a431db07de226de53369f932607488dff1ba62abd9869513f18c682b2f0390613709982d42812f9dd6affe103f8a01429ad2e933dad03d85ee537d8dc0dfb79627f70be60f8e7da4a5a6f0c462b8102bc8dcbaaff8e12cda165ba29f3352602a97a865e0304bac8a0541046337151c249f6ae50e3a1eec4850f43d7608caa41d3a226886360d6ad660b22f6e5540ab19acb3fb3e544d013243a4efa1ab892913eb43f3c1814a3524cedb913d461073aef032a748199030127cc3a705c7c9816eb6df87a60c5ad93ea6d0316c151e40b642a82814fd483e1ac51002200575c5c3c28083007c02adec50c308d0bf4eb47d3ff7a9b8699aeb92cc9ecd4cd37939223035153fb6f83ca5e385deb95f71e4a258cdfe56615875ffe3002c3b4b7c35b5f3251859267eea7ab7392152ed4536a7a8076f2f1648fb54d74b8ec580c3f5f6bae14bc18198522488f9d9529ee8633224b6e58608456b1f88f28409508bf044e21617b14776b0f5f858d8a1d38c1703bb413f1790d294a251162896adfa83b7e6799f71c2f679b290580138be9dfe7f8b33510fcc2fc97fedcc6196d8ac51dee0c7b85860dd299b1cecb79b4f2c7ff79ada3abc4563fa83274053988e5780107fc595cbd36f40df106dabc94505a0d1394ff0682a591e606929e0bab2d52edf1b68d730458b71e0f7772882843c208ce5f27c5f1a68345f5540cc3d79a65abb0d9bf3cc595d6f6f136c87a38d05095b81f9b3015eaf6974b2009eb51a793361e23a6ffcb41f311e6df36401a837c6048515cc41ad447269ad7aa55b6364602a330dd986e4673d270ba0653854290d424003f02bdf1bdc6869724557973421f9915cf3ec93053c7cb0c6f0f84e302e609c6f83028664e624ba63f70663d6062ac77d261c04062e0ef44c0d2bbdcd6d1aa9b917e77f809c1081f9e101598099d900a381bc4d8ce1da023ad65f8f954f8a9389bcb652916f156a000a70c1bbc1e3da02f435612411b0dd18084dd825392d7b6d8154a768094e271075435b93ac8e9e5ff342cdf1fb4b87497eca1fb8c1b46da2401e96d3de9bd816775f630d6123ac58601c71120fe24bcb83fb30bc29be0bc26ba2904e1a149e63a934bd69de81d20d89e49975077c819bc73b6180fb7fd00609f50c7e3ce3771fd1f4dd4840d98cc9215389dbe717b489fbd941f02897692e526060d395b52e0a7b5ce27845ff99a21065c1a2e1f38883bc0ddeb98937f4b2788862e18327178ec7525b52b05ea9e39cd8fa44467a028d3a01f70967e9984ad7d1ff92d17219bf82e0dc771b191108147c86c7c681acb8a36771b903474eda4a7bfc9b6ab33cb5b49eabca65338e6d508e77eec5ec1ec703ab24a2ec900dd9bfb4cc0abc860691fdeda2504d94290dc40383ab035f6f4e665eace094d68b066713800569aaf357fa29c5e67b05fe270b4e59cf06f6e70a593e22aa7e482372fc394c563d4b990c53afb8da0c662da82d85f976fcca7088c64558e80a63ac20bf497c7b3766062463ad52e00836eb8060aa0cf16363ba6352848d8d992fa965156febbc092843d8489fb4f9e5bcd127d92a4505355a0ed2760dacba42bba7fb7b446251a98a94d006525015a6b6273e19c636b62e015d10cc14e49d329f8c32adcefe5fab349783d69ba0ccb96d8ac33e6918c21a8e6c330c94c4eb8b249b67c46836d264f8ad0e3d0427a2427834c3fec1f0c8cdeb2cc0b59ff04a0563613e015fafb64505a632006e96aa150a56e14a2d0377c4c4db724a292e1d04c7aec289c187922eb16531686b3ed055ff0252a557438b6e0be68794d1edcbeaf5be52550c64ca4ba7786664c2fbdbf0037a0a5a885f91ace6696698abd1d4c71231c66793e1ea6fa94f6c70fabcedd5ac0c9474e63be3ae7666fb776ddc29911e4287cb84202c3d1da254ac8398efc9f5ccccb62c20829eb0bec16e816daab14f903f9a056ab31e6685db716dfeb43473186a9a3eeb068943bb1fc01d68ac6422bdb728ede9ab932fefd874bf9018c701acec29e142911667b4b22b6fe03f9fdadb59f69f2530875d2af4162f4f66184562fa641ea76a80ca4bd1a9d9fec85f5c29d209ff3af68c5fcf215b2ab2d798b589c1b8acbb1ed8fafe9c7894598aa20813bdfa497c79884f03a29e9c3f57684d41a67828c605012b39f360e4395fafa09ecc695394a0a647c4e520b6f6fda10992455a85b5b41511ac3f2c0f9adba525bdfdf7fa7afb928da4d6c3d9240a4e0e35de25aa9d87b13a70e0fb87b21d580c28c403f26329ec1ddfe239af9b2515e86613b8c8bfe930111a5b066a8584eb11a18da6b869504340a6c95b356d00697ff12d366e02bcbb6f1ddb5350c8447c018435d6289e7a9d7bcb088c1fb8cc3449aa5e0e4d4c3720374e6c1eb76d81c13f0bd514be11c07c2528aeadb5cfb3bdbdda9d1c78acc375afa0d8576372ba512bac2778f96be345dcb6657e553de651fbf68e7b5df8d6d121be4e280a1865ac20a647a73e6170970c8b69bba9d80ab9e2828057a73efa0f3349abbc6c5ac561af98542d7de42a8fdfd58125fc28ca7b730715a3322f0d1f209f34cd850ebbbf1cd0a73cd0f065af4d990bbbb5b102f0eda384ea552d2c507eeb2637dc22ac5a205fc0c8400ea5387fc9a4b32eac65ee897c7a95cdc368d87f4878368117d501f68f8a0ffe5cbce11685b711582373fcb5b542d370e68b9d7a5d756ba13fe94d2430bb671d83935ad363710a43152734cae8cb30b96aed7984506845cdfbde1d442e3b60c7dcf051ff1024bc823174ee0cf3491ac03a47f8cfdb03472bb086ae03f742836ab63150373166f09df7f28f0321802cd55c63cf9f7a43e9dce44f79cadfb57ee5a910d164a43502763e1ca635449928afdb4352952490f65867eb805ac1696387876822857e2afa3fdc142004c66480c3c7b8e28b2dd668d9c63080b568c83482a2db6c6d49495a78cc03f1701448e3863cad27a79b204a72c29aa6ef4a8c9cfe816e4a1c6e2aeccd758fd66919c516619904b3402e8553e25d169779810dda0409ac5893465d809d1a56c5e8a4595f3306001eaf001419c047819686b68daf6405ab2e8a908a5bba06182f89c3fe1b85bba954923a5f9103a5a07aa8dc68a8d505dd0ff6663911b5734de86815c43f9798e8bc43dca2cb0db35b35eb7a259d135b4d1a4e0b72ba93eade6df6c561c015dd0b8a18b4a339ea94da6b1bb345388193df012590811c9c558182ada0ba995f08ebb66c311edb020b12225c82039495a10cb916d9e7cf0206712dda3abb97f63445a7f0612ac4db82a316c37ed9f0244b89b695f23a90a02075aec45d9556445c6003d42674a641f9402b6b13990965cef6479ef9352b723005efce8113204fa6a98777421bbb11fce54cbddf3949173eba987fa8de4a57c22c3aab95b9318ab2502e076217f1e115f295e04a5b5ddc41d7ac74e1aa1a4b8e2a97590c95e6949c0a55ecadbdc850fca97967b1ffa6e61ee7d8b5376ecf1c241125c3de180497fac0361350c0dd0bd87652085b136bf552a487cb73a93f50283a1a3f514c4c503670839fc5fbbffdfef4051811f4e10d12318926d623c023c73107c651e2f79c82c3f71038c2dbc3dd1248112ef292f25040e44b1cefcd478f9d5f865c0a8c307b16fe1d4a681f199b9f21b300d058a4f7a0aa902c72cd3908f636733bec81d98d73c0d5f47fbdd84e31b3fd860e54852c2d16539fb7db7b92cc70daf25c8d8f74025496610ada1d55233d74df1826ef415916f51d48237238208eacaf9540587580db15e5e267f7a606cca89996bb9381788679687ae179c7c4c82b08b7cc269fbb231b4a612f5367fce328303d8c7d3af92949b049bfc103e347ab442e124934ac1da53f1f03194efe3575054336926b141b3b19362a462311ddf0fd14832ce09e3f3cb28409c5b30b5f8260ddf94a51348ce53df432a47e441a366c049aa5f7829de50f97d7bb3d78e54ea8a8f4a73148fa2e8c21866550efcf3cc9d066bd93076d172daa723da47b60540384980eb599480d8ba4ed3b44951a082dbaf4ba92428962e0efeb75c54cd84639bf2b49dd58738798d424c80a4a1a8052b278e0f8483559395c584989dae02f5234190f809e76918f01de1ca38c9e6789b6a0318b8c5e6586599b2a88dd622491d10a539ecd3d64a4aa5a83159970ae52842b93f7528ed76d33ce3c380422cb96530e54f9d54699805610ae5f21e74542f1d21a400ce8a1a283b603752faa10d4777808bc52f9847fc6e412258c3fcffb938e237785381c87e358079dc9edc6f7fb4df276ff72dc2222182168a69ccbdcda2f29286f8c264983c9486fa3ac19d0f4f10e2b143204682a0ce795ad4135abc5e77c2a4906785ec16431d27df5d9b5e3c1ee3e99ebe14e6114d7c92df508a7dccdc3b2466bed60e5a4fb6a71121525e27d093f5a69800a2aac42add42bd4545669ad327c380df69bc80ee4f133fd6b3457e66370628ecff23aabc85c39d510407b208dcc8a0242e3b3a383a144871fa80e56f01c687b747bdac997ce968ce73ab97e050aea8d24439c1aaac67c8963baccdcd3b5673179def6f084bfd5c4e04448ffac4246f1ef9c344a3e9a946dd4a6dbdd46c40d9b62ffd92da41edbc72242af970230bbbdf11a461b83a237abdc087664fc5950342f9add882b3e464a36889fe63182531a8fc1a6170bd7e8c26e8bec704b52aca61f3af4ced469f7410b34e0296c6b5802d2fcb4b238f7282ee9c8aa71f1fbc318befcf589c03333aecf42219e434c873ccdd0bfb39011bd37313a7fd06f7e73558aafbbdc1acabaae5d7305add71615248da94c6ff747402444ea8cc548d1f7c257a7ff1e6a2828b1284d1ad355e47df96d7d0bd3343003f61041ca04b071b42ba49d6a811ec54c37355be3cafca5246c52e4d4d2fdc92443dede4413e4201fe1270bff8649d95b7ee09d100e688697f179077458718f597625192408efb79c2e03107a017372be5c5461d38ba1f99647fecf9f9257befcf2dcffe254c33149f23275e83e338530b07a07f0e1ed28cae2704fc741d37b82fb812c47795fbcb1735f4d46e1e146bc1063446c5197666a47e707023fc70f91744129f88ca0ae741936bb0c92e9e9b69190237e9ea8b80cd0f6f91bd7395f0e85e55593eee0fcda6f44e58e5b84ced3a745875ef89a9376678cf744fe64b9f3440038360dd47fd5588f4ac5fb788f372368ad8fb00ac040a249870c0463c38c8b9ce54948092c131d7c425eed507835bb9d8ff237d96ac3dbaf83513009f00b6d9c21790c28e30abd494bef729bdb4dc8fe0728b1d714ea5361e9af98529a1a519ca11f03fcae3c3209186563e88076e93204bf74bff7bfde5181a221e61efbfa9ad842efbe215ad4359b63c2e6103157ea348d4403559b386d910eb836a2260f4ddd29fb56a8fcb39624f9be577e9ecef75992e796d8645443d7bb8fdf178255331fa8b209b44adae2b6f134c0260bfa09810fb30693d1334cc03098b2c3d0ffcc6f7a8d2fb0cb9de40a0673d9ed52b7e5a7c24e547976263f0c2b26f0e7d793692d6a75d469291c578b1f13a83fb2eb84ebaec52f5482a0200a351f385bafea96b013e80e052cb2a8bace11319ed39cd37aef6d486f0480c208ac92152f08c1804196ec73dac6d77c1e45ebd617c36aa0f6a695fe8694a9f15773624b7b61a3bd90151ab0ab24f40d78866a3a37b2c43a4042dcce899dd277c92ed9d1f136907069ec84eb58d05ad1ece272fb3b13be67c27b73ab26f1dbb19351a4ff83d807e0ab17aab2e6ed83940166e5cecbc4741bec3ab10878274aa5a8e76930bc4373594501dc489c27baeb38ffa2481206cf4a8f2c24be0a3170d94085b0e1f26218d8286cf296bef27b39bceaa575d81b32ca8542b4d57f311bfe7d99cff5aafe5704610ba58630b923d475b3036a5095a8a527edacf9c13786d5ccb007619d052606b73d2d8eefd71e6484727826ef894fee14701b3939fa8e22297554e1f5a30fbf2e148282b6f43845225b4fd7d9bff12fd10723611ae5df18dd229840a1a204ae56d0474e55a29b2c831f4a4c3d8d1762e3d0630746fcdea3eb0d826e5edf3b44713ed4ed571494ca4cb8133350a5f12b8e0a24def2eea92271f169b387ea3d8d3f5971676b8900de493db60d57772a131cb52c35c078379d9cf4f6b8c065a1de8b37a5f1446c0b7d691c57ffbb2fa980b37e55a2be7c18957cf7f35d0f0ac3885c40266d252a017e07108628ea3da9c30e91270f5b5022d83c626ae4a803b5df60094c5d3e0022018d062bd17322a2f57b93fdb618f8aed5b3dd5ce5b4ba4aa8a0ed88ee00b1f96dfb93d4b0172023815b72885bd0e685cc07d7b4d89b59af83c4b87385f683b46e896932b0e5397ece78ecfe6c4bae708c6731897f9f6bb7c29615220cd0b34d512cd831bc38bd978c377b9caf0a4aad682360b3c1256ef3099399fe2d9c9f0ae0db64af060d9991188787aee8e55a8d6cdd285545ffd06047c1e03274ed23e1ab3b215cbec8074ae5141a6aa00cea240c6a9d99f3f3b5c6bea82201603cd852d1340b1ba88c1535b07acc67774d097eec89accf3439ea81fe62aa80aae8c25ad6fe7f91e75ee33f2e34dc8827134eebe17f1c14a1a3dd1f13e5b22fdce2d78c84b5e20efd013dc62820a11ec85454bd4ee21d0048de08b2d22525e6a8109ce3990cc39ba026b0cb852c15088a8c043a2dc0d3dcf3bf8146d075b5eeae405c40e9c30019265915e78a95e205761b3f43e802f45e8e593cb2030fc06c21649b8185ea3f44379dcbc7c685ee82d9068a7f36a743f39901a6fa6c0baa4179a78018addf15b2f27595fd290032f5633b2beece63c8757cadf34080575bf2010ca055215d3f8765c04955b9a718bedffb03fb17d28454d484b5e14ac0f6b7c82d5ba12f81eb1c97f10c9190cabc4107cf995ad8aa559885df172ab953d1a865f1f4f58cc4bb1cdeb8d6e04e5594515b7e86a8da6d42c94429e18e37dc28d7966f41fc87adf00988361aabc5714361b7036c3d23adcb23fd84c5ebedb159214aed2c5b65744ff04169ed8232ce5aeb763f8722b691f0b02ea81024ac3d52d180a29ae4866fab11ea091e2a1916ed895ff446ee9f9304c45d5148a265c8523889442d14cbbeff1eb4e94bd2513d6048ed4688f185e30d02d204861e40cd56b7ee2aa3528b1725bbc176f4e92f9b602f58d09d90cae6e21cbb3b07f598d14ff62e5f9e9c15e436df95991e4c55148209d44f9c83aca34fecc096ac184b7ee0c7a10834c2ae318d068b0bb5d2008472652f2863970dbbf7d303734bf158501ae07c7e37b65f1657300920a7888142531f0cb2e07b22732cde5786fb857fd38cbf44ee4886077fe1616e258a50905f986e6939d45a07fdf388de8b621891c052a1b7c39394310cd498146064f9ddfa44c88337ab77b7cd1fb2e653d150a94aa5271d069a5530cc99ef3b7fc08f8d008f722d79e9b5d58d6d7979b0b344e514845b2528c55a7481cd83b6aa664b2e6df82447ae2c94c4ca11e3f17c48a269f0c35c3dc277f6f43054766dc388fcc0fc5af202dc84f5ed5f7227c5836e903db8443cb4d2890db99a0ae2e588a124a225b37f5219cb935c61b0ec6bf137164a2660a270aa406f5b362447393e7f474f3186b2dfee6a343b0b29aeced09203c001cbce31e16718ddb55be14f05e4ee2873bd0dfc9894883364c401ff5ec6e9ee41197ac6e861709f14f4428f660cba52f3033f3b4914b898778a260cafc69240e72facfb6da2eab0cc8fe42a8300d0631fb73f6f510d4845ab9034bce1d3603f329eecedcde097424f996e86ad6fddd16862566150345090b18fa199e0d99175cf2d36b5b339921461be99296d1b111c2570cf5ab8f365c156d424a42738c8c603c2ce5c1e09497ff2c1e5ee7af9a43a3786376a767acd1a82af878118d510ea84950633b63fd3187108f845baa9b570d824d84030085c049265e6c1a0320c0ed3b2b1949a582d25b6780a0aaaa1a5c56b320ff3522c2a52c56f935b51ad46fc4cb82c04d4736a28065949e29af50f3a33baa0e0821343aba07836bdae1209437a0d07ae8be02bb18e12c9b79c3bc0e89fd6857a92fd0907680b45b93660467e100e85384388f51ce28838effa6762f4bc228c66ac1a4fe7ee43df9413c1c0162785ef8ea8e5da2a1882bdb7c95aedee4c6caed945aee0444ef839f5a146a6a43ffcff8ae66612938c71b8aa39e3580352c256c063bd969cdd1251d9dbe03d1473edbf401142b81c2d8bc3ab71b522931bd7d62b4de5dc92e8196109fb940f10b71dfe9789152514fe89d87e4eb6980ff384c386278310f03b75da7b8456e55c111888d7424b2304a5d0de8ca5f8bd9f576044ac96387e6a2cf4cbda61d717c20a47264761048f0d071298bc0500f750466e76a01d1d8a31916cde0223b9ebc036e068c54e72528d82039f7f3a9cb733118b873df8fb4a86647fff7400d644041cb11ae352d97d0525ee19f372184f9dc8525d4f09008ffc0a6e00b69be56e0add5d9e2f003c3b909320556352c0432fd1be36fbde0070e45c8371c3c9a6d50306287c7c06c0326e39432f06653767804df3bc0943de457a69af0ea0638c3d972cd3a773d7497ab2eb1a119cbac8e3962afddacfb0904a11e6793ad1234e234d0c9fbf1098708a55ca851aa9853f3394b148162c57878704949231bd90ee11ec43dd877b7ce1623b77de38a55a9262560d3a4428c7e618eee0825dc2812e5458d035ce0c1f9e590d205f21dbb0ac0531ffc0f723ca9d6fabe755630c8ad98466979879724b96039949f3fc8a0c7d87a3148d8c31e480caf2b335112510386d31ae379c95440a5e99030fac85739425465293faf4f87b3595cc07849c7b699bb5b029ffe366cfe7f68f0d59d4c03564d394ad2554005adf9d08d7747d1544ab819b55ecdc0786aaa15a347c4bff84af4bc34ad41f5799d7a0c798eaf831ebf924b288382c75de2a61ee312a50e4dce3e1a470ceaf3308d610ac752f5770ca1190a77941233fb6595f8024566dfd6259073e4652b7ee86988a6d85f958afab12c79aa182a4abb66d4fbaa56b642084f697d0ab1c768636a9f83e92e52c62f1cb27497bf79aff1400b43544baf588038a3f48c7d867cf34f8620f2b2be44f4c7a12293fb605d866fedb84d1c8ce5486286c7b96838a2b64c04918a37585adf8c358177856be13a4588cc198c2f2255e64d12a98536e973f18534b2c0b4de7606b9ff8e4a1f111e3993f77b260b1b38908d9ffff2ea48d8f86d6a3a54fd2e45324802318250d46eaff519388f7ec046876e8c8ad056d86160dcc4ea85b7fe580b993a548ebbef730293a8ce0dc73d152539b69ba15c92928412e61a1ae36c03a264d26c03a32230e8c071954d45ae32267e076514f9e086755ef50dc6f497c59991f038e80bcedddc372b8963c82c2caebc2132e6971f103850d0e0afda86c18121d04ecf2c7b0c3aef07b4eb64c5b16c0064f789564d65ec80a918655717a9bfc403b178c0040d2951d1463927c17b9cdc115d78b19d5681f88020a5438f055ddc8accca41624e9e47ba6e5ebf493e0d24189f65c0562bf0bdf3b681d1a911ded75394c60cbc9b6d1510c5bcdacd07da56d389d9623dd2ec92c3b010a422a7972b7b0c22518bb48fb4492d9b2c4ffd3b0e81fa451bac7923d17f41b7b1a7b027cff10d9d78114509c4a2670c80bf57f49da6f8bc2d0e70f3fc2a37e1348a17ed8bf05049eb20db2e6e2443e09d7a256e70b592776063acd79ccfaa8ed93d51360b5397acbcaef3e151cd95af701fe3f23b9ba1783271bd625946db063f500df76f83d0c09fea6fcf53e002b68c7a29463e86dc98cbe4de99ccb7cdcf3361a189f1fe53c2682d4e9cf346627c440cceb3464c558ad7c4e8ccf1403cf2f5cca31b32b67101a2180bf9a861426da99edd05029c992e3de84558bdac6128cff7b3dbea951e0d83ad336f584208e28bcc8cfc8bd3bd9b142cc33d8d8d8138eba454eed1108ba8eb66ee5a8e755b2f94235c5c463852e039a4a8a45c5dc1b0d571dd289e7f0f6a27dd88a61ffb50d216bc20efbebcb569762bc9e582423e2a3b039935d8599268c83a7ef948c2d44e8c47fa6d7da96ea9e145fdfd5e8072adcf45300c812a8514bf69c2d8598fa1ea189b44cfa0418200c90e7ae8d019240c058671d9e47378a87048b290f12cd612c2050b5a11f15f2ac2f1ddf2da1a65a57675518f133f46b4c8edccd27c5a073191f71aa433bc57dabf69d0685d7ca62a93cdf9ad3755bdb0f3a211142b124a54d4420c22ed1cd412b6dd2b60c7736150442f0a1ef8f7c257c80c2ebc5df0ac9789936dac4a6eeabeb1cecc121514650acb82e6e14abd167f08e15906b6285a3fa12b0483046373d5d306cf3eff924cddac20226ba6cbbd2295a69b60c1c1e7bea26e5a70b10ab37b16bdb033398aa43b730ee1d4d065b9a73843a57334b954c68d884c803556085f78c83e44ed52006b19351413f26cd963df255f4ef41ef3fd3a4777fde0befc64e9c0399b23b9d047c0a3760dee376fb43cf6492018fe3429502384a2f9818e895080665e8c7f8a2a2e57be52462284438ea684ca57a26440c3c83139a8e30ebbf572ceb4813193ed2558885b05dfdd2e501412b295717a1f786abab79fcc482cc9742bb63806957fd8b047fdd2db583d30830e98e9e489f44cf2d980633c7ad1511e29d1ee67812ebb9daa4ed27ac94edc435a452a49274b1874fe70953416b4f4aa3c47bb538a63a9562b36cdb5fea899129653702560fd5f467c4c5cc3fe713ea77b8081bc42515b683257608d774849450ae3baca742167773f9f3b44c62f85250b2d5abd6a46ce7eb5b0e5e330c49033a001894fa26a5c1b370eb2faabc283bd92f53b3469058e8fa4af3c694a665a4df1b59482c88602f5bdd98710e3b288928d1fab089be58586dd4ec192b786a133b8ec6348a2207f77292663886094134f5d518db2b9a7831689bf4d3660c0de3b20635b1c6014043cc5965e093bf5ec941adcb2ef69536fa9db6ea8d88d9574abcdbb6612d45d9539d88a9a183dab812407a638d78580a0b36a809dc584a111b3e09162accdee9915f865c61741059546f8b0d877a1e73f58f0ba36dfa307c102fc330b2d092087a6e8270c45e77ab16958934783ed100e8294a58cdfe6d8b12856c228175f1bc2cccc27225953654bcf2964559d44e6d147f6e481bea5e01f03e0013a38dc1eddef91c79821977661a20d8aa893112ead6a72a4724bbd93d2961af9ac19eaec341d879f2a70e9c0a237f838c93126e655c36304ea7439a5b213fdcd959d70521cc67ce6518fe2ab8e3e505abbd74c2ae2767bbf0a4348d240a4749456a5825ca5b8dc24df8ac675be6f098734aded5e208fd64e3b601abd50009508f046c046520edef41b6147366e46c673be5cf1e88a2b8ec82f28578eb83ca0ecd068331871c371e302b47a9445cc20dba32909977276d9ee4d5deec90b9fcbb6b15b5e31d2a0c44e70e72f76cd8a930681478417f42871e8ae06ea3dc9b46d79c1cd874ef3c0972186aef1cb733a723c969bd77f86431c019f1f2a4e9de55b5630066067508d67b09bae0b03f10a73e2bd53926058e55124bebfe278a556929ade2e394737ed9ef275798b8bbe58dba1b3b9d36b7bbae4b12adeb820b78fe7e0bac2bd1a042a8e908ea5c52b33410aba0a254d14ce8b5f811fa30a9e319f49298b552bcf11fb3db3704c6f81e6e0bafdcf8cef2a070819e96d74b3fc08e10f671655473f0d3c722d62d7f9a72042be570cb03c49b1b9004e13687955cd26e3de0ec89fa3bddcf91c7d40c5f5b9cd82b464a31f7bd06885d7d6b9d173059627c98ee136768680112b5f509c93173ba0975e695bf42ba3839a0f10872576b3c5a907f8b2970243443d8b42a07a0d264c0a0c9f88c6cbdc52c200005f557cdac3a51829efd5fee670238378b989b5a78baf60376a486d915a727f50f62a0724d29191ed1ff5324333c2721fa71e58bc2a99948b119d8b2f84c970a0bcb8de4074fc7b50fc8a2681938c681653373b24d3aeb52e3ae290202f46b30f95e4a332b4f4ba8c4ebde405aac82bfdae328ddd8c7534d1866e8292e44c7a43f70ae7fcd9282174a1f7f74eaa7449ff64ab34057ebb2793b6a82c8e1c6c68750ed654224ad6c04ebc55e4383abb2d060f1ec5f1c8918c9c5af3cdaca2877391374d7b8a4ceff0109a9eaafe1acb5515fe68e67c9237c29f0933f276778472268639b0d57cf265ec6790d56f5e466de5958f544f8f6c6328292b784d6a88385a273303a690f094af7ab2e7950de16da12134d3f812dd26611843d5ab5da405984caf480e88106c240b5a7d505e1077c0886765d57fb6bc3fc94abffdf8a04058dda83fb3525a66adde05bd1c6baef4e48d192623174b000d7e4404665e242d4cf98ca9ccbe896c3a19d0b41394d80288957fd09a53fcd7369822e65ca20dfc52a1fc33e86a1ce0a222f9e09bf05c2b3792d4cd7d6ef1dcdca60085895c406aeee57d6f386b0cf58c133633cf9c5e94349619fd035ea4cc35eaf1b37562f169c3313ead7489ef7e22a93d3eecd04e24edf0c59314dc52dc9070896923e0ae663cc0da33c0df682954051c6950128a822c41a124e32eaf1701c19d34cfab0dcafd06c57d17b98615c138fdf44c7c238b00e14827f89452f247811fde440aa9d8995f6b7577e611ee3f111d92d83336df21adc3a578f2d02a163f18c8e412b1b90f222f05c040d1aaa8c16b8ba4a8794eb9ffe48a34bb2050f1a4c05206d039b54856688d506be78eb7f26147bf3309c8efd69ec5f5978e1951677410c4e2c6f837f3767a1165e2a145daecc19ee4b1991a1e576ca970684f6cbc192457229d49e6c63a39dd2d013a92fd5bde09e51bd51fc03607f1cb4cc326d79beaecb2080ff0e28ce12b08e014778a50f53b2a539a18c98846bce48a134172e3e2b11a109dcee0f1287aaf8082a131a9b3edb070731052e34bb6cb256bf587b6429c847d9564af95e93c63f5d6e46f9bf3c479bba53c5bb6bdf3bf24b2bd993be2bbdb488ec466e85d10bb43886f7395d6de0f795d926f4c48e9d4064a23df8a6a10670f9af607a43ef42e11c017eb2a6400a2d0d2418087201f0692a2b50070ca495d4a4be4e929ee4e8f20f5df20ab5b94d1118b1a1f04bc510159374038389196f2ac7fa4da05288f2a2d0cde5009fa62eb7057832bb1d7770dd913ce7744bd134f2c7355e51e0f5064a5a5565645862fb1c6559f083640c4f6d31aba0360d3ffd03e63d01d4de347853d65104d0c00d5ee3cf776e8a7004465ee62943354cc4c320653ff618a6c6a40fc496f2f7326c874602728bb88566b0397109882b0b57def8727225f9e6b8118c89564e244d2af562e88fc3b3437b16d2bedb184f378e9256876c394e07122dc7df26bfad3679d04be1093cb6c4e4f3d9f05562e2f4f2487de9b3fd7de0c0f2b83de9a2642e53e08286e0d0716f6e19c567cb84f8044a7d74d391c1cd7574ce347288d465bb3de3f3da669a2e30d71a80caed9b4d7e17cd3b5f432e75d62a0ca41fc7178299be225b957a4d54630e67646fd88842c7a8515a5ba74b103f62d3744a9d30b6651a69ebe66a5fa30dbbe510d625e418346d75e7f9c4d872367282d52de473ab40db151055c3a671bb0dbd469db2175223b840f978914864bc1c0bae4bc5b18602ea8332c8f98e349b0a2e6362233a4dda82c5d763efb9a1c71ed1b14d65ad35f7de058a118b6361a02137d8e82407fe176734c1c171e5eb3d8bff12b94d3fdf6727b63a88210608e475507fd8618aa32e3a7e3262616e2cb68c5d98512c3b7f7688b3a8957eec37b397fa9f524a1e2909e4c7f4c1db86cf47de578fe9d5798c3e1ee33b710c8705ee763796cc00fb47552c76e0e4e653e119b1413f9c263cc42d4ed353c2369843e05f87ee1847b83fb45be0f3501b131f0fcf61db6198efcefdd49829ea3e2417f068ee967ad7e169a48b3129b8b0a0c258513f1498a053b0f5aa5120e7b5693f051f61cbf2f09e68540662afd2c38aa61efa6b261de7d802c783a3ffcfa61eb1641b4ec580127dc3bcb1dcc043ac89523165abca582c10cc8b723a749db90fdebeadfc179f674864adc9e3f5e4790291dc784e46f0b70e9987012dd214aef1b33204b4bcc0195a96b420784e88d2c62e8780c28ce4744bf2dcfb33d68b7ff22f6cbe2a7188a10cae0af69e9af6329c3d448414ddbc9fca55a496f780b22b4380101a80ab5ea987d9e75f974341cbcfbe31a6024c684af46d6e0b626e64b5d0c69ec4aba364e39c2200d222011f5edefa74f10cebd38f4c01436e5653dec01985c10394fc3962075655f376c9ac6a54e3aa7ed531025513dbed6e600e1b4106a77a82465318517a768cbbf405e25fb6931cc98fd06035bf798508ba9bf5299fe18ce51615ba1b1e70220f677de5e1ade6c0314938999d3b3cd07e887536a71e5daaaca8dfc380c8ac5d8e14ee134ac8db92ad7f12f98ed50e4feefad7f331a216047fb67b60ac35b5ca96c80dca80025b493360f388b297cc3b26631c1b96960f354b3a248df8b5648f60fdc577755bd66d5164e81fc45c5c4d73db426c74046965ade636b55e8bd54f485990874875dde408228ecc17db935eaf8fd39c9600cafcda3560fa69b9dab780a30dce070369090d99243446302e17853af940ed4ac650b34dc293c4465a419a3605ecd3485968d059ab8f5d9e38f3556eef3d9bf16370ad28344911e01999e54c8268adb4f208e7a6bfe86932abef9a4137520690d831fd2cf093115c056b0efcc6c956dec1cb57b49d3b47cd1cd7dd02ab0a0f9a53df61fb0cbf05c2420baeea7f8b4d1296c64efe6905c3c2ad5dec089eb801c87a66a5957ad2e5921854d29551ab7af00a2ad94fb443eb3ea309fa350a75a8700c8b5c8c9ea17a00cdd24646efc07fbfce3129b1164af75730ef4d254723600eaa0592eb1609572d76c572d11b989dc1f517ceeabb0fd8167b7315d3d9a596e51d458fda6c9e59b1c18531de420f616ec296e55b89a3acfe86fe49d145ea75eefb80ff0a8b067a7efd57053018da74dda40073436cdbf20952ae0ebd307fd49f4b45774594214a0d0996cf80329a6a990384ce5274bca846859a34de2e8705dfb0a76f24828f8f8a1f4c7b455e4d545d9dc1141ab469863cc959df3f63c21c3cd540fa3caba1f7de785c4a773f9c4cb71bb310afab4217355704ad481022671b35e884a8dffae6ada5968d123623df7c6e28142ae1151c0e1707522aab18211e6adb0a25658ec4cea7d5be043c7bb982a9f74d65db1c3fd05ee31faeb1af5597c6951f6aa9b2f1031c374f652182e66a74925c88a79ecdf49f940bf119ab886e210a22987d79d9c75375bf96b39263b39762318b23144793961db03f43c6a3152209bf205b4de389faaa36bc993bcbc8439ce3b60c7c2e0a6b3ada15981f39df9d735a7bbf70292cf022f0197f8be2d99851718da0c90c1981590914428655c10ac8aeb044dcc50f9b2b5160d45781123f5a1e62107537aa023667d5670022f0cff72a44dfe59c6375dcbafd21d88c79c6b94fdce94d496ba0f401755993f1bdd909d9f9c16db0371248d5e6a82ebc8c511a98a04d20153e91af26c0ca41aa84d7fccbcb5618dc6ce54ef3d2592b8333e7bc2cfd32fb86f4566a01f413c5defe56b89e51050da9ef6ca3a9ee75b3e1460bf5d8215c79279dc75ff43dde8bc2bc3082092a4e271368e72fc70decb2a12668d2ffe3c6baef50cf7c8103194f6cf3b1b0bb3c11d99dc3c1fe4a4104dbda5b09c1e315ec9553ee4adfa70a3b55c9113804094d43f6640c01eed5a6222ac6d844842133421336051ce059225d96c94d50660aeba8e3a2299d21169011c5488dcf00b20524471044b2ade59ceec0c496bc19ec4a31624fd1fb208b2b5eafd2806a40837b0451ca359cc5d916c4727b118746a3c1e16bbd05e33debb99d2efd74d1769ea9df006e46cc81994cdf46bfa7aee54e803f08b1efb0607c5179329709409e0917576906bdfcff1898ac86ad43e5c62d9acba8e1a5985b70d181964249ab1683927552752586866c498e3f0e2e858a97a038f53a7d35b3827d1bdd8f235433d0dfdf596ce7fb7305636d1e600ca85c2efca02d1694323bfb2f2404c2fcdab205c1871433c6d4cd19fd8b7ad1ce2d8aab0411472e0ac8eae8b830729ae5d648beaab616fee909d005b1041a52cd894a1b0ae635f31c3a496ac7464af859ccf83314b877c687da4f89ae87e715cf5ca6362838ad97db03538b3e10d6473dd4fe798b26c98201887662a2a3c4f0c068a13948b7285710344f923c09bad0a30cfafaef7c7939e52c49af4ec491ddfa30a41d525c03e4eaba07c1ddf56fe4fe20f7a83523276f0c7de70928c68818fa836118e64000094876da39e58bd3567684e230172a9891f0ef672de2de46d8c5086d26733e4915fe25cc029a3ca743f6a65388ec1514a6ee8b6852889d6bb479dcb25fc85e911383a0d09cfb4c2807b991843e20aefca38b35675490284ff76f2407324b0df9de47109284339426070108177c78311dce41d47f834dbd2704e89807c0dc8ce72f288fe123772682441aeed5f5ac8a0eeba93dc6c91d4a6715c9f2e166b7df163104e976ebb6c2ce3c11d878f4b041969f15c65d5042be2f471b50f6f14272ce382aa0c7535484583edb94e56e64aa98432b2b5a3a246b5d5d29f0c4203690a00ec0b1d88a55d4bf57358e4ae950746f1c0ce1e7e0f1c847551e4e0bb0dbb29ca06edbd324ecf4c904384783d0383edc6276490d614097d243f9a65c5dd169dffe48866d007964405e0a48fa081eeb366410fa8965899513e6794e0ad5bc0b53632d954db32b6ca531f6b0bccf25cba9cd3350d009e4f70169738e966609ef1de9e1746e340ed1770cb3ff5b445e6c4d95b7e5e5965f74bffa040fc20572cbd26011448db92828e4d5cb1f52f61361eb9d05ad26a820dd602ef7a61a4be64d61b404e50998d386b6b85b929aceb282ab8579a18f91361e58e5b646a4ea3127c41e4944e019d34eb6f2d2ed254cfd55b2dfe343a8ab6081aa940e5992dc9ac1f60c69b9168063e93ee575801bcb8866843092ed977d201c62748b7a588f77150bd9c9bec191fe3a0a0cb0a430e6c8d49700b70aa68a6890764e3d345d2f3055002929f241594ddcd64e2ef1e963bbc4d4ea9e1810dd79f46f497d2e5945a76e32a22769221fb6a81d1bafcef1f4ce290e85ef744e29b30f9905613a16a4159a7625f68b03c656d6fe3656d50c30dcaccb3294ffa37337ea84137b0679fa2c38bac7bffc4c56e8539dc6757814d848e248b004ebd4784f53a7c2bcad5b7e0c123a41ad6329df4ed1e673eed785d633c7677e4a0083332ee77c81fe05b711b0dd5ef2aa4839c6f6232d0ec96d922f001169c94595dca2202d223429a485e4094ca31649c6be425a030f97149303e1b24b9e280e48f47d40cbcdce15e6f003211cab55e71282adaf8b0f898945f55cbe3c6db93010fa86d0ac6a04f3e30d24a824823f603ea3a547e519c23cb88250f1d601bb3899a56ea0eb4818426cd18761f77edcd5b8cece809e6e3fed525678decde502a6868552697caf58d1f4dee6dce77ef84c0699e40e44b4e75edd60da9212e2afd2d3517c8f60407f6ddf19ac197bbca3145ed88d62cb373b92d572ca94b74da53407d67fb3ce27e7394b8aed9f4b934a50c2235dc6c2f4ea3ca95b0b75d314e5625beac4ecce966d35667ba357753cfdf6b61469bad069843569b4ec7cfb307b473d28554ceca3e1b9d350c9149fcb35eb3fe1aa74c541805bc77c2dc5a1e9f8ee41684d1746de3a53e2704e12f9c5a332261b30a53aab3e52801324157dbfea7b782ab4ed7d3f3181b35e012ea7bae0ab0bcdc748e4e3c3b41ad5d7ff83a1abe48a74a39bf207c0d785ad98414bd8bffbba8877643ceda5545031e37403d5da9bfd6ca99884e63d18b1fa4d5d61abdf379628218f1ebb273fca6caea3719e8e1d63521466c2e21a8dbe3262e7b3d96f61c544dc42d0fd1b887c05b43a63fb142b77641e87c15da55985a4e5686c7f63fe18ee0cc3068c3d7112d19e0f75f065af382ba2620116662ac8647ff32490e4445419f74196716c08a511c3affc323787d8a33049670a894211ca1fa1633c2612b301c3c332e19cb9509c22dfb84cb41174ec2ff01a432c0bb6dd97f03ce1b697c7e244ff078a1ccf96cbae19aaacc6667370a52d9748df88262b5310ec0a5d88616515b92b6875a9a9884b4145fdfd0be2a5d5e4e830a8273cc4ae5282b8976189a94a48f67af7dde2c7a6d488fbfdc063956fe6129b1c9b8fb6adace8994d3f2901c64bdacc2e170c93a28d79c88b3b590ae6da97ffc6e10ab929c1b732f43f1758bc9a61be968cec89d9cabf10f33766c851d27eee2ed7c18bd8ad5e39146d035dc0f8c0b3c2951f7744dc678d81feeca46dd6973c02f994d2a12f353b1a30d8b4dfd87b5da02f3f08a413b3561fc7ad4d0cde0723aff83aa158c02c503764f9c2e32618252b060bf5d422c9699d6f016178e36eb56a3b5e06539c520339ba156f8042ebc7b5436d8a2ec91427182e1744df65cdd0e50194fa2ca071208b205d5584f19a429025ba133ec52f88af37d5ca80ef94b6234c6c23c1b39348ee685bd5b5e755b0561f423cc1e333d7fd4e19be89a70d238bf0c39320a2e0b21ef56202dcd44aac5a0dd7bcaa90908aae3a4f264f1c42e428a52a3ec556b6661fd7a563671de4a290298ad3a601e05aea72ec5f314535f559ffd5b342c44f83dab1e20bf8415a55353fc7be52b0a2b31943819930407b2282e8bf28144a647e9d7c57f94d28e6aec7c5969533db2767fca072e5301c8b6461faf8c05011792ad4a9216017b2a0944c483a0f966f746c8576a17d606acb61a8f00d0303ee7224a1dda1685f71f5faf9222b95079585be7c59c93f53c5724a56e37bd14db73c1104b4fd34a9d82ac43782a19edaa54c5295e1c6464372c8f25c9fefeadb998486a11e2ec30081fd7843aed1e3b7cb2332b853d9245c6c4c5621033f94c0d5261b89376c61bf5468a8755b66c7e213b6d80781cfc1da1f0967c8349a2001aa6b9e2466f40d258ca4af89a5ec153a0da07a22204aecb8c70de3713495fb1f5a8dfcccaf6cb2b488ba8c9dc42c79458b4d37779360cc7aae64cec049ecf9539f4b6e38e4e77998d897fc4085bce170b0de3436b7a10010f169383112fd72f97256cb7b3f87400c4e2e59afd02d942d669602e1c5860fbe65b405c8643ec1df27cf2f2c8ea9233eff2e737f45b4b647427cc5e95cdbc5a867b00aca446cda29e46117b8cee87b58d2d26baf90d62a5eed190deb68ca837c0877f27d6516f92e8ce7f1718ebd642c060b7d3ece04cf9a2ac6f72ed8227b89e8844e3fac27618e3dd02b03c7942b96d07c4376175a559ccb23ce7c11192c90e8fc465c4c66d4558232c2b6328121e063c1065718afed382de57eb41c3fb0088451ca2610a0a93d725641693a968c84a58500a9024898bda1d7caee2ea048a47af5f588b48a9771c4fbaaa716dfe2a0fa543130670bfac4cea66afa95599ed9ec65720e40a5ce7d22294afa8f8a01d23c8788db1b0fce7e53259838e0184c1f813f5bd30784bc9e20e6ab80e2114a5ad0c85a49a68727f12900f3df7701c2d3425bc150c419ad78f41f4bba614808969c269c36d4a8b75ec1d38247692d9cb434d016186a4a2bfab4e70f8a18e0f8a4cf369942688247428e5c0b5e66b9977fc9296dea33402b29b2d3d1cef505761021e9a301b0c3f3ed5cf4c4188146f0629b20e5a841c0a0f5f668a91b6846e0ca61a339734cdc29cfda4a1fc0c17031888754e10f2d4831157df9c38699510315adce6530c5cc692a44c55f87843bb63a98582b38f072406e3bee3b026de9a88a00768dc0c49b89d44a86cd386dddab212460208dc205069cbd60852106015983b61e4b1190d38ca16be58037c61b24960f469ae531bf834100696ff700a39996ec48db2664fa0c324f1652f2601cb24d09675e7895be145c784c66d694879073b23e481fa0df2265ebfa8db47fdb0132a64999cfee1238c342b37d6752d1998e4609eaf0da52d10a096cf34632925a3631d204546ee92233414f9389042e291974e4e48725c0d994f050a01b635baeadb297fa3601a1194932e379ec15bd6dad1296aad4dc89082bc0629a6a2a96fecae6e31ca4e630338aa227cba6cfa0950ea138507ee17c58b905c907525ec1b803042e432abccb1afee47627b743a5092c4147fb626851b66c2f47bfe50705497c69126ed17c59d2a9ad0c681624adea03d3c1cdb8d131655bd16d6e9696ca46cdc4badc4359052d6351d25471b28b7ab7b91b0c49aac3030e09fdab5d9a58687495cce19afde9612415fa09d865735cd395d80381c92ac9352a850cb841f451996b1d96fb90f06c774ac22c1fb7804a69249f8b901dcc51dcb323d40fe9e3f1ee4f176125da685c6222eb514730281e75a20609353d4365495f2a56b0a0585466b7f4230261dcf243cde303ce366b8086a43705b787c69503e1199f34d1b100f908858a0955490ac06bb336281df6025a26704f89b2b0f9859d1cc79f80b3d4033e4ec2294cd8f5181ee6beaa651abdb0a5331764749641f918e480c05239e885992f53d43c55097fa985a851cd01cd61865556ab701fac3736458383752c4d115ceb684595e586f0c5a0724529e7e7cf0a626e3e070ba211a4d5ce6f52fc9bb47ec4211e871fe8fcb255d8d405051cfa6d484081e2558e3828072620808cab2758a4252b83b9dbbf1cd911f84abfe2a9a0af4054672960f2c3a6b0e20d6c8d6aa93b427c9080575abf5758b9de9e63e4e27b6d0ef3835719e2bcb131efdf06a7ad5448477381ccc8360fcf206a19df0365e87834c272ebdf8695b7574203b2701193e9dc1dd80ff920e5b0c51e060e46f2d7799bb50ebc8eb9b078b0c902c0419ffbec875c0875cd4baed9c503a690d314d237a5eacf5d00cb5231e0c7cfecba9e72a25ec4b7aa9748c65901ad55187d0899efd86d00113fc9b7cf0d48d52d1a6dc8470a279f50f4b130ef9365f4aa5349d6541644d613546ab045034a43f4e91f9e4d7cc528d50145ed516b75ae1d2d76bcb0e853671d0197bfa4c24c247f986328decdac261652a1025d70c692429164c942cffea01618358669d6bb82e3f6f37117c1625607055dc2ac0312a6aa394126c9278b29e7c251ee4bb81af11d5ce5f6c95c0faab7365e54e8bb1dc682e81fc28ca77701e78959c346ef5acfad3ee9c71720a3a2c836fdd74801be1e6746e3a9ecec6512ef4697de728b10350466cb31e7f16a1cfbbee39487930528a63cd9e27ad1431128607a6513a711cb16a21399b17b2a3991e0b8d6e20593a3cb2ff6d352398db497c38705eece44483a04442e13b091e21fa73961da2ffd53c9184c17ec279d6ae2a2803e8ffee038bb40e9346ef6ee367da391f971abdb3be699f57d80bac15620883a32f5190bcea0988d0a072cc4eae65a9aefde7f25b3917fa9f41681af8350100e110cdae6002d06847ef7f0961c7008ecbbb79d6d05211cea2aa000f0c36ea971b0a5660730f8dca4677cfa01489267c71c3e8816d0c53c621cb611aa56b676b9679e3e253b970166cb2ea9d4752480632651d40b41db10a0681512d85b072e499607643282170a6ff2e7ab9adfef8b258343f8b25122c8c51907d1fecb9e5255203e0a4fc0a77de559ae7321af4e415c071a9546086a78769990ae2e858e80460e3b1cba0edaae4e5862ac93cfcfda431c971d103a22ef6e60f6660c1aafb8ad1cd52d8d8049d83866050609cf36699fbbdeb424ae5d627f914e74f80980487813d37f7eb17d21bcf47ca019a16328a92e0e871da69b4dffa7247cd308149ae5017b08da7c5bdf0ce0d308daa296eb482019718db89627e4c9233d4707eaca2074be4c07166e7346839579c7de1dedc5c76642077e31631f9f2d07ef6da068fe3718c33fad5428b8ccaf4be84db2203dd9f2545de6abc84b9275a6be575ba26e305330ca821a2d87b2ea5be933898c3ed138014b73128cc2a7fbd030302ce0a63e94ac90d102aa6cf8705f0cde1fcae7c62aa3aafee5789a58a7a136945044d58108ff20a8d2a90c0a0fba6e358a94a618d7f28689717a959cc3f250f0ffad658939587bf8d0226cc2ea470f6c0d25045166597b9129e563d9bcecd138f6f3fd158f7c9b07a87dc33058b2b4f029431c97fe0d2d97922fb9bf57a66f3e5e4002e99744bef4201b9a5c2699b86add4419dec32713d65adc65cbd17b30c903146c075cf44cc1e7486e7d66519c186103606289afcbcbc67c0c6be0b0654a53ee54b339bb8a87c103a96ab147a886d4969e06c3a73a1d9f8a7098d319a0c9d8d17edcfebc8ddde53322594d709886f6ee276832eef5243dc790667239b2727d9677f9639783219be8d4d99c4d32953c65b08bfe81328ac3a7208f696a8b0325b7abdcdf6525ec5262b7203402748bc1b8b7ea16a84039a477584c51df900d68112dad1750bdb2b493e2122bcf9b5029875349c86d972db20f7d7e26fd721a6c56027a3b26611f01b16991d5220a13198ddefcbfae0cc70afd02c7147864a13e14e9c745db473694f68e3b41c000e3392b333b74b0ff27116a89bf62b06ca21e6dc1075ea35a7d125b193008b6d3d3c0be363e204ec96ccba6fbc93130fb8d5dcce95d1bd72178478bd522e848f78be2969a7b8eccc2cb3911e39a2a11050bf2488bc51435c7fafd51bef0ad99e277e2e0dcf72ea503940e0e82d0168c733f5c6c906034a01f175836c546e8997b29433960f49d4e363ceccce42103fbc81f9fa6fd166e0d0ecc5134a085fb1e06dae70d308f8f39eaeafe01d12e1351f9a4217b0ec3e283794737d78589168879b75522af059cc2798b08b66455cb0d5ee4cd6da0843aa0e524f119cf5589aba6e2a7bead2a1e023ca6a1678cd5c517908972c795e925f0f506f31e52db21cb18ff8c487f0f6ec2fe4e77eafe444e798fab733f67ec5af79715505850e950c10b80fc06cfd989fe6c1fd166a01899d35edf7682f3d08a8b578a0876cd5662cf417348bf3b84944d81f477ba7f688b45dd912f2e8ab8cbf0601ae8b7dc8162230354480baf090c1026e8acea17d73f5a8f13bed706a650e12c38e7ad812b6a9b4c5ead46867e4f5018695cbcd531cca58f53f08393d21f3e42ab200354076a4d83dd15407ad070a3aac2616a6a87554a72614f0bb04b8981e359f95d9d0b210f864c1a481506067a89488e3e0674b97813554b36d931e863ca7f970a9ea336c9e786253fa8f2a033ba457bff45a1751e2893a3d75b50b7ffa35131843884ce7ac106d8340700e7fb3d38709c6cfbc34181a97d86191b728cac662f513c3aa22b199c04a3cfb374b37b61a1074aacac16199ffbbbd4c9766c7d539ee81f17bc33831af560fb15d563cba94dca77d867ec3c0dafa665027203eaada17ee3b96d792d2d300b13c82201ae95d050cc32907c051c0af877bc7a740c530e6f86d1aea09212de7b4e634b9cf8493f37fa53afe934a114c1ae341094a33e168ec4003424d0c45dd12b5eac9acf2bb7929c62aa33da318e856ac0db6a215b9289185199da54519ab01a21e86ad86f7e6e017a3198d76958fb54afa805d27f63e0497f8a1b684faa3971d6895c01b2b1869a95f23872bdbc7c5dfa19982bf4abfa6a94b331880c3a917836918c2bcca5d6c41e63ea30069e70d692490a329dbd19ed93d39f67cc99d8a8cb2cc981c9c2b9782c29dc17cae4013f7f06947c47e13ae2f533ae5e94bf21fcafeb3872da8d40906abdd10b562439f4153dfdc954f287bbc279905be8245d1222bd2ef9d0b517ad145547571ba17bef2143b5b460414cd2de6eb480c0b0680819fc08e22bd27e50219443e59db74999dd48dc4870deeb0fed561c47444930f85b9de9b70c633a8cc2b61990846d4a9b5bbef6e796c3fe394b51b74acff9e066f538aed6619eb89502722066d1fdf93f14c717e4cf5861f950326b4b9d5c853133de34773cedd918f9adfdb0725d2febe3d3441d76aad01b37723c7cde492fc7f89f0f0968e2c53e82db6b37bb9bc98c4f8f26b6911abf372e1a415137a2310afe15b782c1720255a9611d4048ebd14bbda5ab82f70a586971ae0d9ad0b3edbded8c1e62c27a23acc614269a410d6e4696a34220df77f26ac10732a9a6368574d6c353d54a95c0d9f98be0d984aec10fe625a450d9a6dc2c928f5b3ca2765151fb97c6012c67640c06c68fe2978cc22522436832d95a69429349ad4b3bb44c6e555aa1cb64aba50d5d26b72e8dd032b95569863613ad57f2a569d5f23aa6a2e3e444b24bf6c04a13c8f915dc8ce7a3c32684df9a6f58e9872b10841d29afbe4cd89a5527ebd23eb1eab25569852e93555ea0439bc9564a23f44c5b0163254c392ba6153a265b07b212a68435765ae5298d31632b3d722aa0ad2688e8481962cd1b3b0ea0492e9e4c32ff66083092362655c486324b124e27b76687ab7df790ac5bc24f0684481e4244067380694fb49d88e10dc5c25267f4e8640a9a560ddae122813b7dab8a4ca534f0165d2c1619a4c3941500308e855d42b9b087fe3c3619e21c9e254655435428d9a99a501638ab25d155512767661465810c7d1ff5ce361535f55152ea50696707d0812114b00e79d9822ef516760f8718db85d93d9f18118620f74bb29ff616b37880689b4db6e49276594ec88c0fcd3505f73f36d34636e8e9f06f932c84728240b6fd9a49287c5be1cfa23212655248efe02ab6660e3d77deff5c6536e8b509f27e869f4cf8c622cf466b5a49fa5a48ba3ec127dbf38e22da3848418826b5c20895b04e176697e6a59ac8314b79a2194a4cefcaa1f5193c03c259bfa7209a66b3ab543c13e72b9b7922a9afb170c53f9d2977a22045a46fe049b2774897f04f56177a734381c7cf95f5c28e4fddbb8439ae34f1603fc1ede59bddcd64fe4f6f13fb18e7fb55bdbb81e0f1f3c97ab5e3b78e25d88956fb096e2fdce48e3af37c7a5aece34959fc385665979775e2a6821688e86260d25499d6e506213c3ca94e7babf936d504b95c8614bf2f49b743562df8981b464f9257f553ec9a97ab931a57ba1419b93395190ac199b7dc1c52137b5ef3a21dc73ad14bf8a6ee24e2b8ee8057966f22a86f6635ae56633292d8b63836d7aced9f756b5c2657d61b254c838888222b8088f645f313bd4cfdfbd8847f58411ff0dfe34b263fc1fa41d1fc242216ef1b2ca768d367bf111728cf5da9b4b770e1bdf0fd29efcfb9a28ee8beaf56752c5472751b966c4816116ce4800e493313b32c32443b1686bc5d3f2ddefab7e206cfe4ea6bf88bf6fea4e881feaa631f2243b4908939ac8927961390739cabe00caa8170ba28caf0c1290cd738a2b756fd91176126b1045d266bde61e3749d084ac944a710a9bac1136d318c48be31aef6cd6a498dfcea692e189cf2cac88679a1b0873529061ca41a69bcb2cb8c239196f54ad9643108ff367d70ba4b89da5f21fa858a345a5c8514e7cb9d30da136bec010aff8156661dbddfc9c80b1c078079d39d57146a1d496f6472aa6c0cc30b897628e79c60544221b80ae641dcf5ca1ec5e1cce4fe3dbe5f5e005487ca66168fe2986acc77eaa6381e34d352958aab8e702c03af0dbbb19a16dfa9564e154e55b4db0eac1f79bc5544c19c4b80462fff1bdc463955314bd2a528d894e79997b6fbf867f24994ecc2716c5564bb68e79fbe31e98def73eea1c1575e6504aa2eb8f5edc40483c24d521568d45ad276e9bfe7e50f7f0e4c0f779b1583a7e45d9e1a69128f1d7c6bcdf6f779ed8d3d8410d2f53007690b5e9c6d4537f93811503d94f76ea02f7a88eaf39d588bd52c14c679a0aa7e7d3c2289be5c591b8eb1a4fda8c2c1c5cba6b6a50bad00adfb8797bb8a949503dd58da0c93ab53da17ed06bab546fc06ba5733ac73788d66a818eea0191295040e56b022217f86343d34ca4043d0d64ca215850617ae2963e75f45dabf67d148279c0a44605020676655ec323c0f97a50d82c7258b84b46f841041928089e679ca4a9e2468aecad9a6d0081cfd0b75ff4bfac336a790437f10d518742b67249d69273a21c1c66bcfb76a82beaefd89f3da96b496d1366d6a9ce70447da434b41f4872bdd1db933b4f82f31883abf6ce98d667aa41a038baaf6642471927e30ee70b9ce0bdc6f9c5393a99b3728c1409c58b77aec0f7aac1c93a682ef8ad4fbf84cee07b4492e54e12789ca5c352745406f07a8e69bb58a4d04b8df556680486643f182550d9f6cea372881f5d39269dd7c0027d52c8867f1566f165728a462b5b7586f8dde113a932825789fe824bd0be271759f24aa25c0a09486f6bfa07238e58df28e229ae208112d80f66e38ad24880216039117f71452cfe05f3f66a28276a7d5dedac743db7537522a9eb2da35ceadfcf63fdaeb8243cb0ad501d9302cc311a54c881f1b7e9b4e2cf23442317d85ba006c6f4ab8b9989f1a64bf25e5987d2b6580484c5f31d71258ddb06b2b0d4ae3b0c06193f196759b000f858099462c966118737225f2aeca1feec902e63b66313b9b93ae331c5e9da36dce732dd99ebe92899e90724210168df1f6a632539e2aee69e7a340f39ce5efddc680114a65fcbd7cfa68bc705002acb5212c4b899a601111d6e6789a456a1241a0aedd80e523da8bf511216c7d8caf9752a9cc59c5d8a22530effa28fe76d00e740729360b3059d8a3459717b08c79cf55cc652cfe4ccf711a2a7be0f85b835654ab5bb6d366e2f933283eed531a3f2c4cd50c9d4c449c2184a00be1d7e57e30b5582bceaed586119861cf018ff957c151d54d3000edef62b3446311912da18cdcfc2aa297e96cd3a891f02c4840865de22859d355845cd82aa749fb368c42a89350f70b430d0eb7b688c302f084078b7c60a92bc520854eacbeba0bedf153cc30b9dfa8b4e092798356d916c12125c6c1e704a97501e133691b21669780ace1794786e1152ce8fe8d293d4005e56198fa112b224a8bc9d21f0eab5ca32ff18ae3dcfa0bb089cfcd5763dbc4c279ded709390043f7926c06c1e3406a31e149d70b72e16fd6820b93b8a77d4256a8331f727c51670223659780384bc87be5c851135c5f606782b4fa19218da243068e33556c0eaac0aa53f0444194e80eb8b9c4e89a72c141a026e794173fd3e01e5117a7bcdea439db1d22df1b5fd57ee8eaa7ef5dfe354399341177c8a5c65c84001304f2dfc732e4e26ac77c18d7bc25ccc763dd5244afa356e3ad499c8d01669f49036bb18dea3a6e09f37ee4bee23eaff72289f3ac11c882d09116f45c4a96b4a897c7d26c69b798da7dbf675f9e97458667b13d5d149f77e68c459f0b0bd161390ab33b07dc3e2b3abce009365e723bbcc488d4a73105f73276206641b11bb86dc6ab029c2364ce0b7e019124cd018be6332890c0637d022f0d337ff36680a07ef24fbc23795bae126b1dfe1fe8a0efc19ee3ac00e7b41c73b7354e0fed4bc191c68ce730c7bfae62387935d7f588b539f68f4558c9a5858b7e34d0e42c4a6a5b99bdfe4107937c65037f0187945a177560aee6992f2798f998e80971abe544c406e029d8e9336ae9b4b14cb1f00c9807aca5230ecfd46dcee8afa5f752007635426e39a6cdd2d5e27754285b3fc79456ca26a9b468aed9d837939ead983b1a012a01963bd22dfbf3028edb6434a2511be8d38af8fd25a4e19a7eb6b0bd9292e95f41c9baa13eaf01884ba29c6733f36836792f2c33718fe11ddb750c30e33f9af00c69fa2c1ecad9e079442b96c5978e4fa2c1ca8d64423b92e230795aac7a23fc03905ff129ed2518b785db9efedd82454a6159b3b5962ecd681b559c86de349b640bf33f5930ae49b4ae6154ba29e7c064d26f16ac7b1da866cb701e5a6030cb94ab01a9de1e816c285b69261a501900b76152c79cbb6bc3e7c3ecb098ad6fe3ae9927db4e670485d985e2763e9c33689bf69ff84fc12a14e467d873f4cc27c60d555679c8c2a48407c0ef114577fa30d2ec20036a42cee22ff67278243f952f57b7e37dc7d7504fe5d004c5ba98c5989d331db9965463a8b6be2720321a545c1ec20f68217d846781cf07635fbaaae552300f76fc54a1c9a0323ec152ea3d973bccda6f6712b94ee094816621916fdbb4b77162ae5a03fe9af721ce90c4d08d7ffcfc4e54afde55d5e87f0caf6da6fa854bd53311951eaaa23b7c9f3048345651d7848d0951ca8ec5b55c3c95db34f51bbab3138d481794b3ada68bed6bba63560dc583248fa14258b205ca4289078f387d9fb09e87c29abed1503e8de4a22bd5f15e22f2a5dfacab2511dfa8d60402010fdd019c08e6b61734130468a6a454ea95cb38f2de105813c17f2136871656a448beea7ef039bfdf980ae4c4d2bf3e8bc64f1bf7ea632f96b61cee9171388632227a7c1f76d1085bf6af82ec104224eb93a46bd77f7dafa199397c60cb7f2b827525045d7d08ccbc83d71fd1c467e316fdc8cfd83624134b12a0ab65ce12735ab199fe8084f6ff77dd190384e07be853e4ab0af181254f6f5a59bfa36cd5bbd68ccc5e3e78b70480d6b8e5935870f770663576f07acd7993199409eb99dac21414daa21bf453845627b13bf2c5a9035eef97d9fac3a59aaa9b8e2572e2f66286f681533eb394195b444207f75d3b61d0ca895a20f6be758341ea7d2dc353281dd53c8c7dcdbe2540add78e9a43dd917989051f0bf429ac267d0c876046bf0ebdb1e8d3278fc74295e3114c71cf8deeff43ecb7c13d7c80fd36947b5aa5683fcffd18a1ee72871be9d8f7c610461e1df616091ba6dc4de16b845de9b435a1f1262c7580379e62c873ffae96ccadf8cae33c8bee1bdc3612d861efbd21f4fac0737752acc168950abc821e0e53b9e681f42d1ee16dc460e1d6ab88875bce9372f6ba4892984bcc49622b59029d8ee203bdab2dd8bffe1de97201fc84d22333313a5ec6a7a434e20753fbd4c502bbabf7f700fe199a6ccf9af8cbd795f9428da16af6c8b6f9c90276e8cacd308be5e23d4858aaa34202b983cd4e09612fdd748dcbb68612a0562b26f526e0b82feb0c9dfd093afd31ade094323884d7eb4ccbbb74029ce76ddc0b0b97c189be5af666c2650e790678e32b918531df6d40a16a726cebb0363cb644862ac22b61c3d71d01e9c60302afeae6d49774bac6ddf0b5060336581ffaa08ffa583e64b1ad360213a680afe491a986af09a62332b322b50b28444f4639801fe5be277b689f1a15238fc4c634b8812f47f2d9539c1c55f381f3f6e2aa00779ef471c4a418d14209ee6fb245aa860ab33613efe16ce4bf38274517c1176957bd8ddc9975858499f20c3aaea69378179ad1d24962958f57b3e9dbe2373aca2bd432e4c42a1bc2acbce3bf94039a10b11e3c6b1050826a8124bb1f8f247938017bb570d8b800a70767fcd1674930a9158891efb492e20f144ecb88457658ff3680947db98220aa83b586dfe1707860200f5cffebb67304e510a404bd6fdff8b54d166bb50b8f236d511befd0dc66dd9f014c8988f470831474c0b7d046435641e02288e89c830a74c139e56e2e8c918adf80bdd54444779a60e60dd1b6531a117cb6ad90e28f38d3b67ad72c2322b3948baf0bc43c55e174060bc3b009d8e176405bba2cc785fb93df90ab137d1ac0d32f0b51ef08ab1634278913cf0768925ce3f19d798d83a1d0fd7cebdf8c682b07521fe3b9737d53d28f148ebc0eee0f51ad027eb472a0bc0ef623e0b1a101a5e8d01199cf567aedebc7400bf5a7c42550036f46eb8b1b467456fa3fbb874c75eb37c8004ca665da582441032b9ab778a35300d38ce7d7abb989cd70a45eb05abc61159d9fe0ff50a52674d9bdb796e27111cd2386714a90a76c0e1b8b8a190c6d966adecfac25aae431a17f292987d187d78551bb8388f04af99af35f77ba18a89448b03ae7b51ab721c7c5328a9c4f0771712ace2f6f6114b8c051492b3818b3130f4405522d323cacd0a55170cf5bb3ecc20e8e1ef18c5ef7d9e83961aae70e8edcc9880fc0881c871cf59200db3e0b9328364664c5637e2eb9cbb8f27da69357a0650d1bdb08ee85bf9064c6791d4a18deacc9c63413f82c4782f66c2a3348cde5d3299c397a0d0cbdf50697094053892ae7655d698e6c2afc7643f6c45f2d8435aad56451c049ad1b82b83c0344b50db96ff1e54659a414d9da026dc2bf256d8e9bed45ebd9f9f2d04962396b54681559d7106e964c4e6e5e0bc5550269f6d1bcf4c2be090c7fa89ca6798b972c4a46199981a38fc8e96015f5bfd345ff0b90458529ed5e2f932e7951029f6b784eaa04dad52103cbc5eb8ce5eac8d2662c27a917e3bf6a330e8b01d96e50f8e7203445587548528aae61106a0044f4ea8b698caa5f5e8a31280047d23e32c75672b4ce420776174ebc4ef8f27089020e4fd517a00a6c85df913bc5cda89758010f700f27dcb57d43e8ce2ad26007d9d94b072d0a7c3e12a093cb504f763690597e49592b868147c65e554dd1eba8374ce5bee1962b024fe2081345af6c1cf68fecec4ae667398e69248a6ee13173d706e55c0b1e9f6c4c2601de7ef31021ac96a7d0ef0fca62966cfebf79711365ea59002815aeb9948a8a9415bdf0418ec3a745ce7517924cb2554cf833fe510fd010f7b5351b3620f6ec12a513ffe4cad4911d82c5d94d428bc751e4c3357018cced91c84ce3d885d4e01cbe975797335ef57004dc0cb12c0c6b0b16999bd21ecc969bb3bcd020dd05d311bb15b8e9a4023d19c13070652e3f92db24e16d0fc4cd754155d8ea544c2991380fee8495ac4c41934da74bd3dcf105dd3ced9a26d57b2d284c0b64ad76022978410acbc929fbbd6e2ec9c2e21456d57b8d409e711a5e2564e16c998dd3f865bf7f771f40f5b5e4c0bc845932aa620a1b70aca257c0a3a50c0b7130f1ea7157a83f41259b1b36d112732025765a96b1bef436282f099275fdcd81b90531967498e5de6555461f208c2033d3a53827b3f8e336d56d9248cda3b3a4d166143523c6f27a85fc3f2d02398a591c8a711402c8a7a42107ca0e43d2f71943972a3e515dd4230bd1e2f6704b47917aacde06f60cb5228fa2e87f84466b3616fe3c4b47987c4225b663157397fb6363795e988da5af6d02f5084307102b69a07bdfac26111fdfcad68e0ef748fe70ec103a76be49048e2f59e5f7dd4e1dec9d67a875a7276b9175ba394dcb0ca0e9e3b5b2f2341dec15a4c10562c3665f56ac8433240c22024f61fad9e7c5f72e79ed2be2a395fbe98d086535b7f8c287b79c6f1b135f09cfc6320811beb31a142868ad1db706eacb758cbf9aed032374cdaf0cfb340b45fab050ad22439bcb838aacb2329c4ff2ace4609e46ad0ce7081e32346ba66495e5caf3147066393ac43afa7cfa185147398f179f37c846377fb8bb0d93f001c1e82ad379fc1adacee6b39d43bd297c9122c132da36dae679a68eb0e3ab77a2a12864038e6da043b5e99513306172b32633f333a2541d4a3c65a3fcf8f1925fc4e435e6084b076a301f5bd1f3a437ab6f7952557801bdfcf4766c997ea158d8d19581ea956bf43b4982bbdc2fa6709638fd331a576f842c23433f0e2c054befd0e1418fed42ef498bbc586302aa01e18d7a84cd69edfca4b310d18168ee36405a2267682542a677cf765a46f44fd959404434b544683209277c1d1f98221162630a02781b1e5ed5bb34dd6b6db0353ee7407a9160fd8df78b181c70562d1a3f6da9f90bb762d59286a55ec2365a18661c8d9ec20178a4d4da7f52ec046f67ca18c27037a18d49c19c7aae7a7f4818e2f54647ba40c7b133bef4384399220be77d3836050e80dcda8b2b902b0d6f65803b004bedc06564d08be902d625a64fa07fad153d583af492cf6f1e3ee2f3d799f4026ff0feac8e310f771e25cb21541fc7806e7a5b38aecf613400d0e2a37fc12d051cebd34db631bc7b072851ee2095f964b68b4eae5f6c3a42a7cc1d1844cf5b9047b19604e4f78754c4f76070a18f9f8ae920e7605b297af837b4f46e3828b04c06eec5eceea13188276de869acaa5728018ec7e3889b51807571ac45407cdca5ab04feb20ac06fe1b5a3da0eaf17dc87f7e598cea064ba147669088a57e2e42de8594583e27c6116b3ec3f8114a7920cf4251ab916c1bad9adc57260119369116b30ef8bbc3fa312ae8369a87df2800a81273381c3304a5b23d04b3f28ad12b13c3f256a2f360c666f698e6332174f9ebc6edd4ff9a3e3e16bb718584979bbe0f5c1c753c912c04345467c47b7b7537c3db678e6f180f0928a9606d62b6122279c3b9490fa3f300c304e1ef2cfee74dc61f7ff6a1069a4f90e4fdc4dc5f50748845fe0396c07396085e08ba45887e74a5a03e9d53f69b2dc97043ad233bf55205c914d3a5881339e5ce3c9359efc9019739d4d8746d8a2c9507138c6c3d23a1cac6cde51e48ab045a28e0291a209235e9af1246f60be4b7b6ef440dc05046f694636b48731bbab9935620921441a217bef2df70e510ba20b890ca7a7a1f57e3c514f3900bbaffae35cf5bdc95bbc4f82b6467e470d257b5868fdd534109477881b375aacb45a6aefb7aa3494b7efc4140e7bd27d091262d621414dd3344dcb5593adc9cc94cae286277712ad72e54cd79d0d58b8d2a703f665a967bf7a405656550506ee943be54e9f9d4b43f79194b917b91303783fff0d81f47dd23550529999ecab285d3367972739bb49ab50cd7a48d0736f15791351ba64cece899a16da48a245ed15572d94f4e331c96a15373a79b491ad3c0a41abc82c6e70b23d095aa5ca2ca6e8c9f6291647b6da7c9e763cc1c8737e4ad79c4ad86a6551962cfb0f08a0bdb42926bfd12a36a4abb37d120b902e99eda4337407924ad20cb9cf7324d9dd972bd2395228f6b672a266d2839480d493422766aee1cabd1d327315bbc08db4bb862b2bfdf915da52807fc2ce29837b74e7708d6ed19dc32dba47f7280c25ad32a44a583d89fb131e897a3a68c5da43b092dd06c932294896ad46bb4b4fca72d01e5dd77536b396748ed6eae920cdcc5a9276ad566bbdb7d9bdaff7de693f67c55d37b916225313e964aec4cd9bced119327166ced739376d45eb5449bd7f781ea4a637e9defbceb91da3f3a673e68b5e93572359315fa5cbef5e12d83a59862579edf597746f5b4192e18a9492cd7b755a27cf7bd7741aae5099e50443fa6d1c32e7d659b8919a482369b76379dcb4ad619b765ba7add86eebe4ed433a53f9ad84bae05642dd4df3b69c8942af793cbc7747e1d33ffccd1b90b6a4945a76eddab665a46f5eb66da4b0ade826ba5576da4af987b993176a29f4ddb5ad98afad0bdb8a79536dce72368a20d7b662bee4b89daed4df6bd73e36d1d51bb61513857675ea643a71da8a79d33a6093b84801041a1a20d008618a3e570a3ba773623f18d8f169950e996851eac0785aa567891290d21a70b543cb52b2ba4ed81c6a71c2482c06903b88dcb731b272bbc02398083427fb0d29cd096b662ab13236a6c5fe11303802063f2dca9c448bedc349501b69112daa3079be67774889248bb6aa85790919c279ef770932f7c6f382788809b6ff08829482a75fa138e0e7350f08cee0ed0c3d4d3a3ef57146a62499964c97ad4a4f8526ca3c0dc78af34ba7ac5f1d92ac4ad4e27c15fa9e0a4715ecbb49c8c2d4210b235d740a16a65556e04936a655442ad4e2769a69b6da4b789c91b750c8cca39d11323357ef7d5e8a918f1ebed7a719a561fd82008e9d4365baa5fa7ce748173d898b8f601ee93003616d91c40d332ee89047c22df23c0e4caeef51bfe2dd0b7124694aef570a1735a7569d776175815d5ec4c8b9c65507270d20f7b1e2ccc65c58755a9c3b66a6efcf579a1599b7cf572aa4cb3e480948bdedce5adb2dd3229116a7a5e9933933340490e64831f21c4d81e848c883e61baeec49610d5776a6553a24a26cb09f713952a2308794474a542b4d9e9f328b24d04c9b3cffd980f332db9d295d12366ae2b45acec57e83aad640af9b20d5f17a9c642ad4e26c2ca4e649d3a2ddc9f36d77fab333923569c0f9d1d2a25659b137a247d2957d9ed6e1de76db86b5b853634300c79ae77c8509a95467a755faf3b2c65a85e6dceed8989da1ad190779323feda0416167da053d833ced0ca8a239dcd7599856b132daa4b5a76ca116838a68ab97204fe6499a091c67e439d42d59c4f70370a439799c2fadb2a2a20e2c68a8eb94d57eab1e35d720730b0db4982359939b1e0f2d6bb96e91c799bb9baeac7a58b155c4121f80f735ec3ec9d364d262f6bb5ad5294857109f31e9aa49b4789f5d3b7343baea350d045562ce9174e67c66be909028fd7985ee586e588bb33b27fc21593d19fc7a008ef3e541e4596162e80ccdac9936adb2eab9c412b627d53fbce09e9bb75324959d64dd2a5bf77209c9ba57c208e0d8e52b8366cbbc9f2f17903ef9b6701c031590af054c94fb7b26a42bfb0dba37571c0bb8f7ca2d540dd6829325ac25068a9d2cad0005ce6d4f0b2598d54c0217182173275d14e6a68093269ca681a0ea46ae373a45660d7c4b2cf21c4010795eca9606e849bd0de99a796a80d21bd22cec2a401c560979677f33bc2159b5250698280238dec8550e79a08995fa0dc897c944a9bfafaa3a44c311cc37a48be67ad16464e5f93ae7fcc843ae29170d598bdb460f5c70e370f54065a276ac0ecd79adabda0c583fde59d2ae751df8ee971bae9aa669f79dafb6699bd6554f0777af6ddbe7dc362edbb2a91dbcf75aaeeb2a0988962ff574d8d96277eebaaefb96dbdb41ff994ba5d23b596b9e5b6cb1457db6ea118e1d50e595fa44eefe449d73ceee9dbb2ecbb87b1f97855bd75dd3baae93599b975e76e7fc86533553d3bee1ab75aeb30b47d5bc76b57b0d6b977aa44eb6d52e829f11b76d9d5518c41224fd7a3a4ebfa773f2350f12c9e04d3bc69abf73260c9e9ba0e6bdf491e3bc73de5738ef5e178e9ee7ddf3bacc2b79e79ebd1f49d0001c9bf80fe9c3ebe11d0c47d3bdaf542a954a2ad555e1185adc999baf9a0a8f4eccfc3dc323232fe1ce5b07e2fef47490bc846926c132d3cc81e03fbc52fa47827580af1e0fca6303024c965966ae4ed95f295cf5e8fcbdf3a7694f5dbb76255a45d3813ae7f1e8ee5dca17f852d8f93b15c243666d760dd25455d992fbcc44134936b614ee4687c31be2cb3c64ced162fd9052e621b3015afca495f40b32b7b8f77d5d6c5969a514c404dfee02af7af4bf0b299d3b786e044bc9da9e9f413c236b1a87c12c65ea0a1b9d97dcddb31d4ac3677982f552a10435d2559974295b4ad0939ac2a4a3f0cc273c6119c4b34f0411a450824a702530c4dd376ab936914b1f1e53d9b31e900ffc56befb4ce6eaf9989f29dcd1ddbb465bb288aa5d4cc141611b1efbdb3b0b581152ab8ca47f26d23f8d72df90cea483600bc071d2196466ef4ecccc9d6b6ebbffbe538f47a973fdb0fdf4766c42a6647d1f523387471364efe321f3f7598f87cc9d4b786e91271e4d70022da4f092278f99579e0eefa4ea81cfba79aeca2ae907bedbf09c9f8783982083975848e9de39c9fa30016464f03264c03287e516b984573d88c8e089c8e0374c44969e8f67ed39c8cce03fe9994e3d1d2612febeef3369f7c2510504672d7c6d91ca6eef1f177a78d5c3fb770f3c57ba94acf1fbf61cf4eb01be3b58e26e3f0f7721d781541e9bc8376c2295e7edfcc85c0d49204e360094595cc193672665710550d6a8a578859e0ed1e2ec414399e7b78b0449bc2fb58b01e7cbc032e79c33cbb26cce9e3db367fe7410664f7738699a93e21604516b2239e826cd1eda2ad1cc97d6c31c9a91ebe7cf16845cafa53ea7c5a613789003633460387dc45c351f7903f0f4eeee8bea7bd4ec016f3fce9e53456121f3622173c64832b57e360067265d3ba9add4f02442c27366fb04b973df77b053a6cfcfae9f3fb367fa4c20f0f68dc15f3c7a20dfd9b7fb13f5890a41302377b83af54f7d14eadda8d9f7bea3e0e7ea86478007c37b0d65ea0decc6a009ec70c57454b80263884923bdb9f73c3753a9d44c354edd67a9b78bd4db86e1c87d08cda9b1762a1c39d73d418ea32f759b3ec3550fd2e74920f88ff48ff40ffcf7e1113c91fe7d07ef9df3740a51a8394d9fdfbbdfef0b47f0744a7de28b3aea17f5d4bd779ad3840a4d3d9af41b0ea9190cbf6b606e4f7ba7711b8914ce9949a406e07c085aa5de48b1b100e99a5945e27889894121a8268fb2888c8f520bb94a30e42acd90ab6443766d68238505c5e6db0198a36f44ff30116da4b4cabd7799516f1ddc5b85d2d70e3ce2895cc3ceeb5a278f5e87c2e04d26d379c86c32dd9a6e6f0a2d085610972ebd0ae22772c54d74b914b61513c53bf5bc7aab7556f64c1c234223725015805d77777777eace000d45b20f596a81869e6e7be98d47d0a5148eb344549a4f505a3bbb24da2428957afb4aa5ed9eb77dde39aed45e6fd9b55da5a5eeebfeba9277aebfee5a3b3db7855a38e6cc076798e1de7d4b90fb643822434f3e034c16b3d4c20c43f92707204b2dcc403376383e410213e47e76033e844527224cee211c55200fe108d60847cd24ac1268be6811a54540c732873d618f4f2eaa5a54a22a5481e06062c213f4131414f483b1cc346c309e9986cdcd143158ce10c2b006e3cec79db92c27f3c972329f122cb38c123cb38cea525f6a0a2a4d255263ea0afe3093c97032994c2693c9706260996708cd3c4368680c95e5a8a80c326404c5c09d4b8e8e8e8eb218159619064c459b00e4c3439f505242757e8c7e8c5e6099572ff0cc2b4c67e80d680ebda13534073060849db99be3939393e393c232bb383a2a22071a435f680b1d542aa33c6570aecc95c1c93493f88ce099497c7eb05034149405172ec29e3c3ba78e6eccd18d01b1cca753df344e1381a7775aa78d904ab5cd8fd1cfcf0f0a851ba65950d3332dd32e389d422959608e4f4e4e8ecfd0374464866e29628406100ca70c8e95b1323899beb453da895d11f4d31385ef0ba56475ee8e6cccd1d1918d99333453073a383693075d17fe18fdfcfc1865fa2245264b1da6cc84992e76d8b6b03357737c7272727c327d267fa6168c88843419dad25868c5992fa99219bbdea8370090e7690f8c4ba602c8b23045b266e74d6552a2b174378351d9f3058145bab44cffe180fd964c9b65b479de244f0a93699e1f33327c6600658b191932168d4563d15834962cc8e6d9d258fae4250920c502c893694f472fd32ae4694fcf7ddd94f633034ba6f695c492258d272c30e70cde506f3a78af439d8eb2269376d30908575b45fe9a6436c93b877bb4793aec498729941cc57789f9ea264adfebce76a089527aea6e4da7cf19e6a0a613edc2d2ed7c753f8df4e4514a3bd33cddc7295c950ecea79ac586a3e9e04ff6274bed3dcae0e6f138dd924ee0774d28cde361fa0a0dc7997940ba6cfa2c92ba26f0deac6696a6358563b52693e994b598e6efc15fa3f9321d1c6f91bc523d20f4a63b877bfaa8d9ee285f24e46b8989d23ffd8774917e0aa77d77c1104877fb1c6daa79626b5fbaa1e9d768a2d063f09b29bc454bccd7b4f9424f02f5b499af5964a2f46de68d690ed6644fbf0f4d3fd970750fbec87c3131368b3de5003c9df200bfe201e93278264eefd34749861f92753a85a07d09656fbea308b2e91cdd726b1c277bf28ae99fe933994cb7a44feae930d1a3ccd5ce648f84291ca516e01ce66b66fb1fd265ba0d8be43dca393afc2159935a6f0777c23c4ef7de5e8f9316dd4f28f0dd57a8af9c7e8f3a85abd4515fa5421f624685cfa0f44ea7f390f9149e66c067301cff4436e1efa5e768db84ecc95f78e730512829aca3d4225fbcb28d9a3098904aa552a348bf85d9c78bedeb4d659a5ad56bb762e67e434ecc35dbd1e1d89ff7db589fe156b5484f5662aee1102dd26da3dbc7546ba99c12b3c543b48aac4ce0a4c97871744ba5925990c95892e5451bad72a365043dc9bccb6ba46dbe4a56be64572b95d6fba8b99048a5d2bba3304347d15174141d85cc1da3e91c476503d2e4ec4d64ecce7db4473dba70ebb1bd147ecfd1737b6fb7dbed767b8ed47536731f9ea3858d16365a988589429ab7a333150deb22dab2b0266a95bed647204c157b4fc332120c4682c1babbbbbbbbbbbbdbc2b68f7dae47df0bda5101a5d4b4987d65c3ee94c62bdd4f0b6b95edd66ede4bf5e787fb76ae3b6fb87641ba8a8c35c81e8148f621cb2ca828ca5ca9f4e109529a164f7225bd496fd2b36f09e048676e0d9da269d399265958fd912c987471e1153b4276e8ebfb8f72dfb86fdc37eea5ef3be7bd61b9053a35ba7a4e520bb3f494fe7a4636ecf045ea42cdeb412a7dbe7ff280f43db5b0163391fb780d546d16366e9f61879d3455fa20a54c7f45228f51bd80b72567594b1b591eeb93b37befbdf7de7befbdf75edc895e8f936c5b5afcac00fb586b930e5bc9ea97f09d282df634a26394d26df3be93d7e3f3aefd3e5bbe8ff4ddc7e9d2838663093c670f627b0ed77f4b00470bbb42868059bbe91bee62dbe9473b859cf28070dfac0feedbc3516b91d21e60fa03c9ca70a4f08c25d0eb510a9d98193c77eec35f0ed87446babcb007d225696ae80e8d71d786aa24f529e146b513672261db26ce3671b689b37d2465a7dbe68d9b06a41476a113337be1b81df47a7cdf56ba15f043b60ba91568dbbed91630940265e3270507d703d7528639baf6d079f5a776a17aa102d5a07a5484a5c5cc08a39c55a29c9d23ddee48977519e5cc4e81276796c8168d6d8f7a74587de864abdc40838dd750c50047b2f4b2bc952d0bb3519099b8869741f505300ba3ad422691de955640b6a63c213d3b13d2f5fd2f5e984c92f49194919ca8f97b9052febe691e90ef5c88f2760c99f90bbbb1fe54cb9232d2555c8148baa006faa9427d92fda7fe8cf5475577be2480a46bb9ebcfa445be67a7471ebeb9ceb48a89aae4a8b6d8c87ec391f4edde39175dc5d6c816b548d462f689ed908d4956f60edb294856f612b65498283cb6a7c5cc0b8fda08dab23e7d929d4573016172a6ba327746c67206fba8a83fd295e58ce677cbc2ea94affaf30ec7264cf2ca899ac1072965f0a473da170410265d5ace6e776891900a0ca016b090b38f568b9c61419e641f2d16393bf591462a225068a44045397bc685238cd61f151886a3fdc9d9ca76cec4882660707e6691999d1e7ab9f121225d9d2b2c83188877d20b6fc776eedc8b8ecb1fd6414fc27667c3361664e68da74fb2e3c84229403e1ee9247647b2b2576c6146f83e3f5a58ceaeb5c863653b77d2fcf1ba7045ba975d8ad3b32d94025525ab1b158a740607ac67d5c1f07674b8f26df75ab870457a4767c42fa4332d66b4d3526089ea59c3c098aae73f6276eea4fbac876dee86ccece3d97ec32a9a9de73e2250ceac50ce54d912c156b22c746266cf48af99a485d9e5da070ab8a0ee39d42d5bc0d9225ff3041245ba261492555f6f38593e243021abd4666a3a2dc301274b76e9c972022bfb763aa93e824d6e8c01ac81166b4f16496dc8448b35d5e29368713e94d7a46a51b6a8013bb3365041c58d14a4701364052d6c4cc60a93b373dce5a74bce84947cf5ecf365669f315386e4d9e74cabb878f649937a867a767af6893373a64eab949e7dc2e6ce8c4d1efb4c7b0634b3eed927517698692493677296919c2339a7bd354d9b5568dde1ed8d63bcd67bef611217c4042c41e61654d3c076818165906015d7ea1a7a926dcf1a1634f68f96294cceb27f4700b9afb2772478e612eeab933c2024e7ae793e48ce919c2321e93a1998e2e4e8c4c0b5c5a54fb24f0e03f244ba34996532cb56241da9d3328591fd45765576124c61d947b2a7b2a3b29f30f5a13f1488067d980a613a4489b2dbac48656931fb0c5c5bfa247b87eb0be63133a5e117b44369e80cadb191b3cb1cbd691e127d6dbbf6ed52d336d24ba84d8bd94d8b59efe4cc8627d33eb616f9d703a2bd7b27e6436b2d53e52c0b572a8caa4b5a509daa8880eee9170450087dc9ed3ba6ed9b48aba86e496e5ddc7ee436758bba7de3e480b7a59d5689354ff7b44fffb44a76abddbe83ba35e3f63dd444dda214c6a848a6332dd99e6bee5e4a8a917598bb07a2f06904a7ee028f9c04bbb80a93bc9a8025df9760ea42358d5aab592ce330f0ea2f308cabf08bda42841857c18b98f1af0a70bebe744ba32796c6569f6cab16b2fd770490bec49e3423ab70100fe4927b1e10d5e93bcf87ead4f3a122912e03539b1b9c189816a92c2ed27585cc5ca2e2e86c4a3ffa12acdd15a6362ddac3c0f486e2b468afc234a7457b124c755ab47781298ceeb4689fc2344679684f531ffad3a27d0953202a44873025a245d4881eb5683f03d3227d624fc2b525a4346700e9a4111a02ca23cdf9fe640a265aec3ee95b59e427d9fe72240f094a83698d18c0b1bab8641a185ba3c8f6f5c5deb05d5a45c284f53a31f30da78bb5b6ed7db72672178b253e00b34b8f87d6b8d622187da2829b5d254b7e8229a509ca4f1faa6529fbab9225311e4d4c5a949740b57eab1e33ec49b3c049137b32bd7aae92b62a5f6558c35abf53fc9d931c7d0db909d279aab54ebf97524ad3a5042f65c9fb09a7348771ad250f8f3348df3e4c0a552dde5f233e2c80638a04adf2a3c5fb4b5f57b42b6995fb31c638d2dfeb3dd94a829edc570f47a6a12a06e0f7278e79d4d31a9a8b4ee99bbe913426cbf614d62aa95b2260aa248f14c644674407bd6ae45c259139e8bbaee35e741d9e2338d38064cd98cc5152ed6f3441a6616d963e816cb9b8a5b7972723b255b8ec2f5e8c7c348d34cbc8c8c88394f2c8c86538635a2cd983265b947dc4ebd12ab57126d82d6dc38d74282223db38410dd43f9dd3a2d56162be9a0a8962ed613c554832642d8f847d930528a5eb4a97cc34a75bcd424fec35181853f5fc275b91521817364d632b8da1304500e78c8de053a7d0be59baa55d6eced12faa929b8cdc74e9ed381d0c290c95e9f03c51985639d9647b2135774b8b76c4d42c2f311a0b0b6a82ed62eb473e360b3290d42b4bab8cdcba4897e723f55abb8a1315f2059aa4abd403aab49aea46b0cd3940ab02414d03c1f123c714a6c57b6f0ad07e9c31d2575227fad3795a250b57d9bb4be1e2b68b69b1a75552a11424b747610d48963dc56b4843b637d900459270c6b808896834a08d0ae73adc08ed2947ecf785df6bfebc100392653f77a46b0b7790c221d33ebb212d22420732958274e667faae9eeda66d4a8f0423d46490a35d27595000658b3485aa54a586d52a23d79289523b6b525fff62225f58a2a480c60af20ccf3239da49396d53295e5932eb2de57b4bf2add63e9b543b046b4b99b6674309565b3bc70d79acd20a50b4216700fc72965680420d5008b5a10d56687141f6a44b5aa1058a2cad00451cb207f6096c15a94a764ae72947b5d5eb65361b679fde0164b60790b977f4b35bbcb29ff662ae371469af6148a6b1b0b00cc93e277c4de066cb29024e9a8c932557b0e7ac6234a1dc570558ff2a612a1c1f13c07a3b25ab279d03273357d2214d7804995be42959932be928e938cd80fd1643ea7175ced9f320f5fdb13ad13dec33204ebe6c0b78b2f66bdf2929292929292b1f62b691b53ca69ca20083cc1b4166cd36f298721f150fa5b5e6fa5bc3956b3f367a4c622594a6744acd4ab44a9f7200b69861d807a9ed75b8727f5f6d8f203367d9fcca9c3ea5d9b4da9b69a48debbcaf049a4ea8d4880b12d50b18ab4e8e00943026e0e4ec5c8cae5bf4e63919dded441b2930a0fba8248f8c8c8ca9c6ec068dd1133a748ec6684bd3868486ec877094a852a0647da4d4335f2689629f024a967d36940d6544dd42c1f5e3e1ebe113bf937f5f8dcfe41f37e43254613efcaf73f968fce362b158ec0bff714242dfc7d5d4d4d47c31fe713b3bdf8c7f9ccbf77d9f8c7f1c0dcdf76d43434343df8b7f5ccbf7adfe6db1984f0ce87bc73621a1efdb6a6abeef1bf9b7edec7c24ff36979798effd6d3434df471aa22776a8e8fbb696ef43fd23c562b18f14fb4eff484213c50a7d1fa9a6e623d57ca49a8ff49d8fb4f3915cbeeffb3e12cd97fdbb079a90ae522c16fb9e8415fade43be34a19a9a9a4fabf9ba7ff4dfcef71af2a5edb8c876f93497ef9bf2a5d17cf5dff79d88e1a8ea81871a261f16600973439dcbd0d050e792ed65d0f8c298500cc76242d93ec6bd185ccd4e0d57c3d5ec64fb92199d0cce85a3e15c62702e9c0b4793ed5725dc8b21ae656868886bc9f630561b8c9850ec452c2694ed5fa848235bcd4ecd56b3d5ec647b1589e66273d968369791cd6573d968b23d492a330d6d2d43270d6d2dd9de05ea9e62b4d5c9908454a690148b0965fb11d0926a483b5f3876372a1548aa21ed64fb548605c98544437221b9905c4834d9cef97de1f8c5d0182d26148bc584b27d8962fa6e86353b3535353bd93e45b34c63b405d20c0429a8711fee723a1c0c66433634991ab2fdb7038edad09046d42d20fe4ad3ad6cc8871f6a7836ee692d279e78ef95a55b190d131ebc1eee653fa107d4ad4c4646c8b39e97e178bf779ad3ad7b7444614734e679598ce79dc674ebfefc5022d6b39e778dbc92a26edd1ceb59cfbb3e9ef7e6e9d695f164803cefe278ef1de952ddb347473747399e77633cefddd22dfbf3d32f3f9e678dbc5450b76c4ece504e91e7591f9f2ffc489749a75b5646664786c74382b41e78ef53a65bf5e868ce1cd5789f9e8d19d2f65a11ef1c90af6ad4ddf3de8474cd2130c87b12ddaa39f4c47aef215fd5674766881bd9bb8d6e55991fad42efdd08e9aaf72eb60a37f690bdd790af8ae3353d6ad1237a629f3d13e9faee795e586f00d65c61d94636a4b5d418fa0f6136e4c3d049a8b5647b29e62f0a60a9872cfbe121ac6112663934c230b447e14f46966532b22c3bdd9b71b32be3decc14a38371ef8571ef054bb8d5bd17c6bdb7f4627371affda722911cdd98231747f6de8876fa31fa39fdfcd877a90c65737c4e39f69ce96a56c6e2d86fa0cd8e6c8c2a1c55a0171e75b5547faad10d7fecb52880348f1950cdf1c9c9c9f139d526fd5caa4cc5a9381faee78ab27aaf088d6e8234bc1e27f923d2c5b4b8e19b3dec1d79469d4c1753a44f2c91f68aa8cc1723f311c9b9399a1251225a448d7874f68ab868349a8be6a2b96834d9beaa6a89a654d32d20bc22d53b6a956ca2e029d4279647d3963644bb97a9521dd121dbbd64fbce157b45ba9821daea64fa65beba28248abded6e542ab087ba194896fdd8c120db6f1d0c14c0aed4034a5082d22bea137b0bd42aa3579409b58acd825a057b459e51b758b7f78a7c2c5f4bab64dd866da632b4253b187a22345fe044b1a71d4c8c763031214d034195ea5226db7bac7397ca4c8a9c3ce94e071995fb632793ede6f178ceb8ef0a30fb8e004ac1baf6198e9e51b6e7b49e546af6a3af9984271078fef489b5d75858d527f615dfdaf9b1d022da4a89b3c3514543f0678ce91695a11d4cabd40e46aa83c9f65486d29667d427f616cc36d7d02b6ad1bee3825794edbb9782566438c99568772a93542b0ba22738a03599663213853e0ba22d29335f594e8ecce470498a336765ab94d20a9b93335f59d044b1b82451529c24d5e40599d679ef0d4b1e359756c9c208adc242afb550ed25639aab57ff99011c2b11062e6001930ad0608202130825b0440420f007cc50c2010d90c180052820012508582501e4003090f8e1e308550f122378ec70a1c3000528229551390840846900a7215e2e5088128e968a174400047003005fc7a5a000c1f2e1071ba44d3b117be0a1c6cd3070010b5480090a4c40024b4400020f50c2010d60c002149000042401e40048fcf071440f2378ecd0618002149173108088010cf1720981a3a512c40d01040000292840b06cfce0c389d8030f356860994d784c787aa63014f453851a3562614d586303841d1d1c294c4c6868ccc032bf65e6b7b89c604606060a1a34586404c9081202032d624424861f68c60acb5ca2b3c23397e8c096f0d31363c28c1939306460c810a1018e0d8d0d4a4a62562aa3995546476f80716189c36a55e4a2c7458f8f164442405750a978485058e694cdcc299b9b2962b09c219090d464938bc9e5250534446256904ab59c706794cca0102834340696a3a232a05041383232323830d84e1326900fcf133a9764ee887444666e90735393835249e68874248bc821e6a5850e5d67b4e1ce9ce6a3f9fc60a16828280b1cd793725350341b198d6ca55ce063b5b1c0c78a5381d7191dc8d70f263e561ee4be6c2541818fd565021f2b8c04fab448abd0789fd641be9a58a2f1fb1448b63c10818f5408021f29d1033e52a32c430bf2355f94f848b9e0235d2539b235611cf091c21af091c618f091f6e419730131dea731b2356f14f0911249c0474a83803e85a12f90afc993c4471a83dca72fd2a52a92add903e4631f1de0236541a257af49f4e363af6148ba5ef0c8d62cf2f1b17d8ef8d8403d3eb650ac555c60215ffd62c4c7ce6247ba5235b2d5303c3ef6cd8e8f9da3e334dd03f96a1b037cec1fe47ecf48d7886cf54d013ef64b111f3b26f7e90ef2d53c393ece2d8a481718245bdd43808f7388888fb36800477541be9a68888ff30b3ab2d5afc923c4c7e98304f9a22f1385e2f83895205dddfb5346b6284cab674d10bff171ce20f76714f2456d86c816bd11400a07e48bf2c04897f7be079a902e0da56510eb49c816eda127d4460ff9a2441385fea487c87d23f76dfc3042ba48cf2e7eec21f7f0f1b9c16cf291d25c43be2a8c8974d91a3351fad905426a8170047305c251957fcc5706c44412b4950551a067023d126862beb29f25887296434fe823008107842ff395f928f102435b998c03c2516b000c03c25195339c05d8dcd0d63d52c04d026e1070c3335f594c123c3db4757f80f41ca00789907e12cdd735fa118e9d6f8e8f5974c42cea41af8f112f30b4756578c0ec80d10163335f17c7003699daa302dc1471936f78e6ebc6e4e0c9f4dd435bf6879e10a087889e0184a32a37d17cd9219a2853fa325f96c2d096a536f3655fae2e12a28b7c323d8e97168c4a184478c326d37a444f78e6cbc64c941edaaa3ff48468beaa006e027003809b141e94705481408423c8eab11112659a03335fd5a7ca549c8942ff4351168f7366cd96688ca7c51e18b8faf4494fed19e23a484a01e3399ab650171861500775d04a7f88a53fadb222330d1a43a524da514abb4f63ad72c3d5fd17ce70756f0f25427520ed675cf52214323325f2fad32a23f5c9f50303582fb3070470a4451f1048d40dbdd01650035bc8eb719285d469526b8f64d515fa2133cf4a89b4cab8723f69eae914d2d5afd70332e77c3fd322daf4e80be0486372ada74563cd63078955a41d74d42a6dd42a479d037952ff036d0860152a26e416ea20fa12546725ca15365fe7bcaff9d218993a611d9e3a33270a79523fe3856ac25097ada8beb61cd197167bf2487f7ba40b0b2bf4733edf39e73ca5a1a41f7b14733d7d69155268634543f12dd67a0dd4b166b0e73dfda5f4da6b298b8bf661059cfc7a542d66374cd19405b40a49fb28bd2a408dc77c225feef2b8b7b7ce7aef01203579495e00c7929cfd25ad22a777c41359c31ca9d693acbda18314dea03d33998d26c8a430a302288fe298b291425b9aa64aa9609048dadb238512d448da65d009744af35366dd59d657cb56ec87f800bc95aefb9559db039265f7da2ba50267adf691de37bdabee8b997419ae5a1bfbf5e2515b699e2c5d74b2cc32732d5d428294b294acdae2b45fd55bbcaa148ff2096d761860e69ce138249332d370a457753c268d07805e3c5d9a61ed396f386932665f0df84466912c7b6e669983008e76a645bb2f31d9fe9e40be2c1122178a6c7f59ec1da4dd22dbdb22b60ed2da21dbdb2339484b876c6f8dec1ba48d43b6b745960dd2ba21db5b229b0669d790eded903583b468c8f656c88e41dadb200b0669c590ed2d90ed82b4b73f560bd27221db5b1f2c487bdb63b590afeb2351acbd42b6b73c760af2757f248abdad82c542be2e9044b1b759647bbb63af90af1b2451ec6d14b2eda109f27585849e90edad8e74f1707bbb04f9ba4312c5de3221db1ab7b74890af4b2451ecad12b2bdc5912e93db5b2ae4eb1649147b5b45b6b737d245e3f69608f2758da435427edd238962235de1ad1d42b6b74090af5b44a258ef568a6c6f7f302451aea504c15c5e642fe84bb4840f6270c64d334c321e43468cd55dab8f9d5d97b2e4af928f9d5f977288212ee5000670298920e2521280009732478e4b99f3a52ca2884b59800218e0521a40c7a5d4b1e352eeb8943c7818d1e3081f3f2ea51197b2c7a53ce252fab8943fce09f10fdf9ff0f7117c7a2a751526790f3dbc06e6e126b8c66960938798c63d1cbeb1f7e3fe0f3cd2a12247464544434241403f3e3d3cb11d984e0ece8d4dcda521325520a811c916a4449d053833fd68676e85d91d193b94ed57051046875a5459dd0fdd1aa385b00aabb00a93919191911912454ab51a036ae91a64abd619b84055a100e36067706cddcc8aa4c1c2510bbb90cea02cb4332ed05a36fce00cb90716c6d0837aa84707c4d1ac4de9a6d7a2ac2853a940302bd2b470a4373fb00f7c04ee818dc03cf00eac031b008f1728db1700178133ce81098089c003c043e01776e1f1e28cf7e6dadc9af1d28c9688965fdcc3b4055317fa2259f62698c2601a2359f63cd8f7802911c9b217b1907982479a826cef83fd0f78a42fc8f636f048a318290db23d1018058f540723ed41b607001e2910b27d00f048a7c8f602c02325c248a9c8f641e0912221dbabe0912e21dbb7f0489b90ed71e0915e31522c463a85916a91ed67e0916261a45aa05da060c816bf66dc7566463573ac652a345f153651ec2918e4ebd2d0880148ba505d90af5b53f3856c4f7fa4cbc5edb5b1b11c16e4ebdedc64a147ba485ac8d7c5c1b9028f746d5390af9b9353859874552ce4ebeae864b1235d425c215f17068b024cbaba26c8d7ddd979828e74694b90af1b8b3121dbd31ce942827c5d9e8962cfa3846c4f716ecf44b13df64404f9ba3e53c8d7fd01827c5da01ec8d70d9a2856ba466aa42b4533235daafb1801480a7baa03f9ba423790af3b34512c0de4eb1245215fb768a25822d2b52ab98ccfb810e501910d8a66205d50d8bfeed144b1d9ed690ae4eb16992856baf0ede909e48b0ed923ea02ba826ccfc9c0313e03cb380ac3b80b8c3a875dfc78c64998fb8649af78bb100f431a344c4c6ad4e081871e7a10c593131f7cf8e1071b36582c2080404149490140000470238820627c854baec2ab9390347ef1131ef9874fbff83bbeefb07621f0ed4c769bfd26db9c30ac301a1a34686a4c4c6a6c6ad4b0b9e181871b9c1e7ac0c911c51c9d93131d980f3ec0767ef8612766c3468c87c5e2e90102881e1f1494df9f94a77cec0b0480a000dcfe0a0960e80651100700500082b200846e0c054194ed6f918a51eb0887102a452d231c1f3b0bf11742e0d1e6ec38700b8f5eceae8283c0e348ce7e030b008f3172f600e011e7ec00c0638d9c3d05a3e09195b303814700e4ec2c3c0691b3dbc0a31039fb0f781c40ceee031ee51539fb091e251639bb884739859cbd87ec3ce05162416a216737c1a3ec42ce4e038f120c397b884739869c5d4a966465c75893acecc79d64659f8141c9ca2e03a7242b7b0cac92acec25b844b2b2aff0252bbb0a9b485676122c4a56f614b62159d947708a64653fe11b9295fdc338242bfbc5434856760de730801192957dc33f242b3b09272159d939bc00c9caee022b2159d9517809c9ca0e03332159b26bd7afe6dcf6d29b88b354bdd0b49dd99351b2cce2084099ab36abd99c950be2b1ea0568ca6e767a9cf40ac463d50b49ca3348da986590ad59853cb1a74163b542a1f238b93ccea01665e9137bc942592a8c569848540570bc47f948babedbcfa090bed8539b56397dc4d298cc425bb486a306822a55165514c99672d66f7c2267a72c1546612c20bd51e1a8301612ab050976595c582c4f8bb6a74f6c0fe489fd9158f49999d1ce4ca151012e417545b637349265dfe1a11b20647b5bc4e844aa21cbc3052cde041512404b049b857646b22c15c0ec23c8c917505015b02a88e4ee06e810f51178b254a94e9fe0967d9e8e0aab3badd2425da48b05d265833903a9d2cdd72d328d0033a5983cc8b688cc59d61d09ea82ba70b13a09afea4956f7a4d5950209245a3a373b39164672d23912c98a44fa7d255dcc2f4e427a21e423bd5e4aba5f7df6ec25cf47adf519cdc695910f39fd74d41ba6480603936545eae5c9d48da450972a1f4e7272883e8d7cb894e787bf0f7b1fee3ecc7d78fb30e9c3da87b30fdf1ffbe13a61686ee2c3fde1995d84365aec146e20928b6e829bb44f91ae2b4e961fa98a0660c2428b2c650691edb99abd66dcb7118f074937c1ed53824f105c68bc1859d6c89264e547549dec9ee1baa39a70190633874b791291aceddfd19739ecf97899c3dd4d9739ccd1172e73781bda3287493ba4cc61ad67b4cce1acc8fde1b0c5c175c248d686696ec208c9da9e753691ac2d4637c12914e3a5c5c6e9569e0e4a5beccc69c0dbdb29a40b0db60c52a59bafb6d922db9ea1831b6c0ccf47f7accbb2ac6bd7cc54c28da30165fb9c6ccf5d3c360e65a127b60668c667a6c5588b4c18294bdfe421d1bfa0d7a37f6b0d7760f50ae9eadb580e52d266febafb3d835a6ccbc323c489ca7a228fabd6421ba3692160e6243eb60f908fcd7300242c02423b134a31891f884a758050f6090b12a1ec13966cefed4c8b344e00fbab9f90fa201ec8f76302987dfb68676adf503bb3d23e8a3b569a66632d5a3b53334f905994b55f4f4785656b5f6175a755b6ecdd924504f1c0769407e434fb998f7ed6cfb295903a855ab4e3079eb01fe2e9c87ec39598692d616c5a658b90add827dd41305550b6dc8eede22a5936f7b9ecca99bde6ac9fd9278c6812488193269cc5ad01976c4fe574a9dd35e4bacafebc1d34cbb07ea727f61a2c064a117c8f5c4e99c01cf4c4a22e9918395d9ee3f0ccdcccd67e5300e9b96b613872ac4da552a89f503fa17e4abda65e53afe74cdd0453ffac006f3d75586aa8d3ce497dec25e4532a95ea9b542a15a29ac69af634fd691ad474c8d3d19d4e2a16108b1aae92452b461d75eeeb3c8f476cc64eb0eeee2f0616a5b891ae48a7554c1446c59f5a0d468b813daa9f078943a873249d9594d2143d13f335250a2503089eb48c76335634a750fa531f418d14a1660c3563a88f2654ac847a095542a15097a814eaa9af5c7a3c50e14a09f5540915ae504fa14ea1094f9816659f9c4ecd723a9d4e1f456f1b674c87258763364bb31c86ca9c8efa486f90ed5140503fc1c45019e992a81075c24ecc8c0a03aa0ea8f4cb36b6491032440300000000000315000030100c068462d1602c909551660714800e8ea0505e529d48b4204691103286208300000c0000000000800100bed418b07f358df66c33a4ad9c81b2c03720895d4dbcb5fe4e9164a4ab296484c07957c2d8320b44e39fdc522a1b4a3177e5234aa3434fcec69c8d767a7cd61362d530b2d83aedeaa67a5354f412a27871c315b4dad6fc92cfc5f716b30e80188c1f8a6749a98ba5a50a96b5c7e7ef97812c19ce5ede2714b01976197396af328f05438d210bddd940649f9cc4772c5da6ecf39208d2ec155ecdf263506584b6dc4925897c47cbb2491a209a5fffb2ecdb1ccff6d09c6564e574368bf1cbb2f2ea080bfe2a7933f884d925e7dff794c7b71180f2b04e38fd9ed30050c8bdc583ad411aad07685a1888622461702e68fec751cf8bf82476aaee26dbd298d5b4061ac7b2dbd729352f920c89f3b6ebb281a826b080be092e9589ec625f371abd75bbb6240802d2962ecc978b589cd4dfd68ee28357ba400bcfd6c3af8c763ecf62a330d2dca19e096aa547d961a46da7d337d0d54eef909e5c6acdce0e8a5a20910cd8fa7d305d762927b7b3ad6c57c659ad6898ce4a1869816158661cb865b55d601df1b25c9f3640c257a3beb9d0b87cf77c64d10ad4326e862cf637d5315533fd79629dd856f1bc7692676b6e8a863a49df07cd24d7aaa8cb6bc749f1f59f822866da4ff0b6f5b2e61f70e9c127bb14e52278af1d1286fb8292667c5fdd36a82dcabd98b9abf6ba323b7171c10f1022b5298d6c8472363792b0a8c7985aca3eae3d511682cca96f16750320e2001de3e944e00f14c6414fb0f597b3d50739ecb735a99fed8a1f07193ba4b4818b5e30c161df95ac082c3dab7ecb81bdd5692287a9ae73ecbf8713b9bb3b2b2d5f79d18c0600cd05c9b80c00e069aa18355769841daab2d9d4ad43ed6adf5ab2d6164ac7bd74bf15e920e162ecbead60a7209bc3c8a9b06533fdc97f049e8947ad3dee894d28e2f21810eeced30f7ab669e63b6a7b5d72f59b3d0ea6b3cc4f9b2e1516854b981f7327c43a385e79bbf642e9674ea7257706640e92633c4fcda59f9e440b5fe42b7a44a0a46a6e4faf6d06d77cab6abac055aa514a07fa5941fa522498ca5f217aa5c623db0dbcb0b253a350668f354f37e798380f19888e7b15d4d6f752a8da2ebfae85a7b403bd9231bb44b0685d1746cd3feed192b1601c42f069f96589a04c8b729d357744b29d25cdc954ec77495e49f53ab89db9d14f5ce33460ead4123d5a96426bb8b780ed581f9f9e69d85002b0cb9cd9bfafcd9fb26699f2d20fd264e45469d43169eb983ff140414499c388e0492ccc228f4c7b86013b459377e00b0438e1c19229024218cb8d1619d9aa251098119469c8be08d518b9254ba393e3aaf7050f83cce83899bf383159a32144a1ec7c4b782da672dd6ec068993bff97409f2e074cc368ed4e1c9368570ae48b768549a261d3983eeba0ad879c4d4ab1b21c1f35778a763943370a148bcaaf38262b7643371af598108e2f16e9289146d2456852c42bd2f47b941f173b450bb5f2bc9f235efccb98b60375a7e8c15a7239ed6aa8c93460b47fd02e7bebf2f0773cb4f23acf81a816b2680dc4a8977c56f1349a9edf571b565c2debe9db2d8a98ef886f3c81e1834bcfe56105d7700586b0ad692b5e57a68263787a1794597693c93144c4378ab87deebef90ade9029b517e8e49960deb84db46a663a72d6189fe18ecff64913cff26fde4616af0e5f8eb55039908c8b5598b1d9cbbfbe69478ad5227bc3e576f2b3e0c4ebee94593793c254d2db2162e106a987ea80daa7ec42ff8a97457bac2e317f026e6a4c59b0cdcd51e7163e619bd099e5735068a95af53daddb57c47142d75d302403520734c8f5660097405759290afeacea28043d9006d8aaa5f92cb0bb7959d5655600c717a794a70733a42d1e4a488d61f88ad1319eefb1999b8d9c6d984be840625784a4602689d8e06dc5b1ca08340729d267c59bca32427126bd237154d9cbbbb9cc696362b0dae4c9682de35ea7fd1309390df205bca2ff78f20c283d098a4109d8fc43ff1cd2a3731f5b84eb0561e132253720401af158544166b978733c7d9a948b600b52bc822768bb49aa8cfcd77201b448845e2d97eefc44486ab9ecce2f42af964b777e2224b55c76074f5e69b190f128e5b230388513964b776ce5950b943180592ed63cc4e79566c6fa7b2456f30ada123106147ac1e218c010b9a70502cb2e7b97d84e4d56d4aa872dbd8f4809e94e2cdcecc2bb15acfd84fdf6b20b0eb14c340dc23fb640df7dc4b2fb58628ecb2e0a2c21b994b5546b28dc39271612465dc0978fc192b422afcab8223355595dcee2cd9d74dab794cbdb70db75c9e443e2212b4f1090e539483a4be3dff506db38894027b550277741477a414f5613285d1f251db885ab3d22e7f98381bcddf0e23b73ed6600975741dcd435c85cdb6742f1380e05a6c10d039bdd393b0343b5814e56af094639e1c33a0b2cb88e799184092c3221d90a8abca3dc7fb4133a42f4d0a0825c404443b14ba588e3b78b696c8a0a41128ab50f6d9286f0413fa7d82fe50073f1461072f55580166eeb0ad111b7cfed79bc997d5af65f32ff149e9d426a4eec4cded14aaca696c25b5977ccce3f0fbfa67a36b55d3bf171dda4e74e174807f27052c9350b4ec7c75c21a422e611e7aa53f225c5a745d2224ab68103783b422ca7737aa24ab3cc6e96a67b44b6486edfa0098fbde65b7d863e4118640ed1365a41fa5689bbc49c1ae13796723a21d56c32317e84a09e604306c7d87c1d25daef4bc1fae54c41529454c27ab409b0889c80d7d904e8fcd0f84d6c93970f9e47901657adfe6f73171e3cf7142cb4c7cf2018f707942c7532201f8a2895f6901ce65d22b6437afd33a31958f4b02e85616996b1cb8509b9248d9c2cdca9bd3702d6cea04ae8873e97c3645fb5befc35876441edbd19a8906aff60a29040174cd038fe035c837d13c7fcd4b29b398816db16af51aea3e799dee9894241c762a7fb04e968239024f8a7dbe7cd68607ff4e52d333633aa68153140f1282f4a659809206a847b5994212ce561243adabea58738004fb23c24dd86ac1858bafb611150019ebb2db0648eb9924af1b7d82ca58bac780d2d1c1097cae2a261019fd5da3d16b5e70df8603b19c6bfd36892b5127f70ac80bb73f6808dd5a39f7f811f6020790b2e1a092045cbd4c08ac54ab565397996eef39b558553e52514946ae6b87ab548ee46b59d0428b48d88e1823a557e3067ed6f92c5a01a27ce0c94c2d015a80813ee91b7952e31965f597cb9e8241a8ea7c7fc81385376ff06610e7dc2114a71794b1f2ac0c83584b77ba68dc56e3f1c4a92c3091598f4c4d394849d4556d0810fae8b6b5f2da7b5b39b1d9c78ca420a5651e100c29aa497b6bd4997891e5c353b7fc12037b8066ded73b9c9ef38fb07ab4ddcb4b43ff93651caecb3af24a18c17fcaeffc09506b542967d17838b4477e9a2e82036df2be6f616522d01c4d34464b0186dc3e4f5cce45abf2013aeef7cd89dcd6b37d8c463ceace01679f22fe8d31866fb15ff7679e7e8724686dea360f34942f8c1a6eb471ddaa3766e685cda864222a045102b4e2a869eb2ec8c7705530ecc14ccd57512fa694e1a3585b386246f0800c97fef8be9d1d92e389e679df9965cf7aac651b8aabf1e25de0b7d94165bc3743d5bd197752dac60f0a3a4989d97c4fb4b2f871fd1591476bf3f47ec07a19ed1d564b587d61231419a806c1e9fd0d057e0ecbea80a7edf204951233e6afb1f5cb13cb26f535e253e3dd2769654cc0b8a55f435fd7bbaefd49f12bfa7fa4eff29f973a2cfd4dfd2bf27fb4cff2df57337bfe573b4825d7530d14a75ec27cf37fdf1051f218be10b568f85a7e53da7315a6b7728f6da9236b3f03552afd002e7a9735a2a7c8185ac3127644424d7ca50e7cbfb21fd3cdb2d2c4f949535fc66c5e32dfa37c2d85afe07c3f2d76aff7dea4cac23ad6cdab6df3e7126d5d1561e6dcdbf1fd205876fcb9ad3be371ee2da63cb848d22546f09b4465893af69f21ff2a6a59fd9ce5c186ef07618dffe0ca109564ce1b2bd01e18def2741d63719003c482a4c0b0c28d5810a1fe69340105d101a639f68dea2a8462f15a9fb8cdb52cb99c0046ef533505d500d7d694080a6a67a34b9ff5a58b307407b9347f93790e6fe23d6d60c90922cfabc7d72241fee1350c457ebef21fb822ea2caa326bef6da6f5d433bd7e34d7b66b2f8c14f7b2ea2604fc24392f0b5c48ab69e8c67b7158b6f820d5923c3c5d94cdf62cf9feea733803db69df59673a0bf57b60d0fdc7a858f355a02115a212e4f343d9fc21863f06ffbee102d96b871c7848fa1cef057005232ee5409145c96196fa846138b120564ed4f8cbdb22bc3c329d9fde6d1236976672f938defd58ab81581a4b4669625e76448dc6f6514b66dddf555f43409820c474f97072a3c41e348ec2d1b42c7c391a1c08a5d01114395aaa723f6940b0dd4554ec1729d3b0be4be1e49e64f41996e3d1ba4e01b49329c4a027a130288ac4297a31aac7f28f1f0e024872cc93756dba29d544e9ccf9534750dcd9ca2c28f602e9fa32482d1309c7cf8881325551905bda3f3530e7b204b4c9a5b5897260c52ac705b3c33844412ef566c6e0c0cb4ac665b1fa10085637f8a30a5fc0d1980ee52ece005fb6280764bee74574299f983647b7bc14807227f1bc7d4df3c31fbbda8f5933d73c4ed8819c741658487ae453d0edddd9d249d5dee1dd7102ab5ad8275f7e5a0b5270e4315110963322e05dd7f8251d83fcd373c34c43af0ed2a61401b4b930dd2e9857b47721906c090ba3ab22abb532147be7a0cd711a7bdc56c52085979a1155f2112bb93af35fc4c0d9aa36a0c9b71a575a7557066ca362f1244806ede18acf74e5d25175134643432a3b5d77769b6d5a70f871466fc6ed5df4d57deeb92c56e77f33caa3d4d422cb9e8d6f6efacbb7113f78d13dd49c16288e33b4d5c8e2d20f194172b2d0da47fb37592fe368e682c4080fbf0a01b23107744b9d3abafc9d3bae40b8d42bc01442ed2ffe21b91f5641b221c1c11af5dbfb186f4edff015122273d32574c521e156e809423ac001afda372998fff8f02f99a4698ce0083a593161bf818a3af5601e03ff6b6f0d17f9669b21581ef8775a648e09885dbe3b3dd2d8dec27495d05ba5e32d965a98a478aa56c1074c944c093c75845eecce61609274ec7d78d0c811842523ff85567fa46963816e3bc4d985da9198a95c1f63bc62779c2b43c9d7aab9e74a36b310f2b304dd0b12710f4febca7a652330364e77dff2855a08c785003cf23545ac8ad0036b2c64befd28c4aa9d8b0bc8cfa72fc4a780805e28b15079516c2f1722ac6728dc0db3f7200b34e8bf307d7e3241ab4d690bd2bdf136de75276162fe5c2178ad3ec4c3296cd7ce0a5d47bd3f780fb49804d8ab37d596b7e6cfb40fef5a351dffac1d15f6dbae8a56eb1a89ebe324a4bbb307aa3ed138dd02fc8b72e7e31ca75269a3dd72a481931dcc33623970a5fa89789bb29bf3234e3c6a383101fcf8e70c26a0cb4d4f0ba00a1f87908122cc591e448f7b9a08e83e6127997350803d11354ee01aff09276eb49af724de6252d638c9ac9f9be10f624bdafcd90797eb2ccc79e490fd48c8cfa2a3c1dd3a09de2c84d5e0ab5c4ad4db095b49c90e6b6dfe77a54e4d5b43f868729f508e5daf71cae7ac11a2afc35d237be733200575dfe0309f3474bb35944ddd579e2f91ed58d62fb99d159950124bcb12ed6878a6ed66d432a7a63b33e68888b2188132165864937f55f388cde0f7ad376eac701c42608a293613b76e1cb886cc30eac0212124b9bcd722503c40dd386c42a0ddfa565a6ac6d281c0c9a9d41da26efe220d3bb86b94e9c577b219b2f82b1ed2870fba4fb88fa8b63613225f5debea36467011e48997209622d7a2708d0a10eeb620bdad3acd80524f93cbc8e8c5186308fe62bb4686590fa043f13b14e595d2b8df46d0d0df8e82e66556e43cb2394ab24253ba5ed86888d78670024bcbfc280709165d8e8165ebbdac68bfbfa2b1474ac8b7b33282e0882982951ff70d12d2d2abe2be2d4d4a3de1344640fd580a0c127aa26475cad6fcf530dda734632fd8b773048d6fd2a3103244b2ebc2726dd5ccea21e5d2d185cc322930bb2c294c366c95f4b9e2e2a06611f5185cdac8c29e07e0d0f53340043d1f65edcaa890ebfbf8912163d0d5c0e1c8ec3f0ba8674b1bde970c84bd5c9670a68b976f9e8f5929594afeeff07cc212945048eb1ab969b2174a50b8eac2e461203d64d9336f846061d339e2d105f82934922b144a91db115f627845d253e9b6de9d130cb65b5f07ece911c809ef70334fb695bbcc24416ffaf310dd0435046252e070a8e615accacb39d2121109432295ab608aa8df1b31020a015c9de2e8386353375d8005b6c88bd4aa66eb5c2e9ebb26ad1e50d68587014386d4a0f21febce4e8cd30222e9845ddd369432d8b6d966783a4beabd32593e55a898b3496633f7ae470fa2365db19251f790458b839164afdb2fc7ebee1f35965097fc645f9fd453d11363d7140de974a281e5516ecabc2875ec6fcd3a9bb46c7fa467a6fa012841d515a242508e37494103d5020f361b88366aefcb88149c464fc88679db08cce16c1127028118f5953089ab451be3016d8cf60c87786f8d7081ef61fd47161543bafe7e2b24f8c5bbfb645bd48baa56480b6deacf9e4b9f0c432ce938cadaba32cf7799b555093bf65788c2bbb774b04f8e7c67a26fa3fa77f0cd5bcb8b4dedae244ac99003fe36ac3310ceed8e756334144e6445222f52cc596761346c728454bf14c0848bcd5609fff82647830c6904c2483a920fe4e0e92fb56af181417eeeb3b8b817c4edfccbd4fedebac185843068477c8a099c84a2499644a5cded1f7b58c8ebd811a46bf53d6e7cd3af84c057b15b72c07e2d9bfdda2c64611644cda9ad9534309ede8146e56fdfce1993731440fa23bc382b9ac39cbf565eebcda93e272b39892829b9156a596caa45e5bc1e917950abc16a32b060627cdbbb6301bfc484f981ab0d119c78b03a56b1688f0a18bd6143cc2929705f6a284ab7f56c88b7cf06abcd1b013ea0fbfae5f101f3f3870eb86f733df0846d206fea24c9f028f9be1bdfc46c3bd6675e8d1b17fb0f620fe07144439fb906f86b8d753d8b429e777100500540011c0ccd26d0071e9e01d99254cac2266c02c067494e296a312ef584464793de54c6fa1d21d480bc3a00a785dd8dbdb64f43d37591837cf2397604789c88ff0b7e5a8387c3c69c3f20af99bd46600cc0a9c3b9e8e4380fd3ac96452319d6effcfd2290146bb75669871e56e4e9ad1ccc6579e17ebfe1279dcf654701606056f8da7c14826902304af64569623160c3240d8cb45c62a460bd69da576a66d03a688e0e453daa0e65fc20b719e9f19a5a669c41637e34db7ffe658954209eb00645b163abf6ceaa65677eed1aa3d4090ddafa969442222a8b299c0410a7fd59ccd5ae1721b69fcc6a9d19aa0f9b9eacf24ddbf80d3f49a5fbb3ccb263a466cd237a2d40ca5514d09fd9c435a779ac9d06580b4e754181eec6e4e50e5ec5731549946faa233efcfe495de2be9b73d8ae583e1d8abb3b16f5bfafd108fef466a939868a5d928f2dd2464bc203085f96a3f627ff0f4804fe49e9023979b5722fa72ec949f60301d4af016768e2a18db7d55ae90f60359dc7542074c7c01bda84cdb15359db40216d88811432c205401dc8d571243ae2975b5ed2bc3963f36f0bc4fa15d5cc191acea6b1bbce7b1037dd313bc94c288a5d70aae31ef9b121aef803706b7f3df2c15ee359d667533b240163843bed1bf797f23aae99defc52e57e3e886e11a0523b6c6066fce8812e81231534b33cd353b8df26de3909bcdea5230e28c9996cacbf653c7d98765707c53b0cc78a0f38d9750ee608b4f0bd04da37b3d42aade982f62497290844f1479f4770b3341202097f514ad5592b722e079855e1b9dab9de0550aa1c63b3cb2d1ac68af3fc6823f15824efad860e855b189a427be115d57f6f28cbf24691a8b457fdee1dc773d78735bd0a525d1f1cd1ad872a62f3a7171cc26cd02f54692925d267998a80d30d647f580909f48f2eb920a89788f1ff443ca83d63681e168c1ca629844d020d1558aed816272ee96b10cace4795d3a8c5b82f1478aea0dfe89085d41e971cc76847f1aa32cc35ad8349d0a150c44d381f84c3ecf28609b05c99b0c60220245a7041fa902d1fd6c71f23b5e794158ca331fde2642934d6ffd2efc86b1ec4b7e2f1bc579abeebb28d56f5868eb545a0e20486d1e78d42f9b041eff1110b385298798047076be1f93e1cd8e5073015ee7b6bc781ee110ed870047cbff808736c290c27bb1357809432aa40d40b24a55832f1f51254af309d22d7fdab4b28f5dcff79df52f95ad8a336f38c8850533360ec01188ccd42d2071fa1ba2eab59bef9fe835e7cf72e58ac598ed7e5f3c086d6210146ee1e7a45f4bd31bd7c71dc95d6ec3908eef954f293835340171fe01fe141b80d33993b0727819f32d6405a534a78b099aee86bccaedfa6df5c20c32991a0dabb1c57ea77a0152dea83f740f8654eba2c76a87f3fc198147620d340b6b4347ad7b7f5f4dd6d695d023839f399ae78e9f8c80724e675a148c8f8d4bb84665ed494e479def63c774488ac5c8f187e023a1343daae3efd7c3de3700791f09e590c70c832a941448f438c42be3b6d42f74b298f77ef2e159ec5b6090d5ce423df71c06bb9aa20b363a1c4fddc5bd7325a2139f336a521f695a35e7bf060e3c205a5a223415a19a9680a76c1f96f84abcd61f69a4d4949f484bbee082724bcbaf50c6e0ed27b5531ee4ba2a6fc983b0fae7f74e268e81fb521275fa5ae7c5dab610cb2c0d98e5c857b37dd3f43506b02c1cb31f4ee930e8c42a8fed4cce9f32e2970714289641ae65b7b2b36be51383a63fcd4c69273d622f048095e99c087274fd636b3da14d51b537093b91e6f3273472c6658ffdd844af9c096cf34e900336ad08cd3f6c17635f466b4fd1a25bb1237b4bf832035c2fea36e3f335485c116dc4221b4e4d641d6a26e8c930ef9280bd0773110f83b2b5c69c787d2d2a1e20c5b825bf73d630bcbc3f8ab0074a1b6973860c56915def1651d70bcd1d0004ee9d1018fb88352766d86a3d86eb7bc7c12b6863519c6ce8428e0a54c80213dc3c4cea00a5209b72fcd79aabc9b0c9b85422970d9284a62bf26e7ba38e9f4aa64bc03252bb358b7c174fafcd88ae2f84132ea12dc5a5766726d8c0e14324b50093a594ea4220fae54459aa27c9fd89b4b046f957acd332d8f1100adc76e3d29dbe4759f69f9b66b7c4011f23dc1ada38fe861690901c448de43158d45fa09c4ce9120b030d7e8ec5b0617e0cbbd5440538193466aa49b9cccd8ff3d030241a850c4cd14f26a03bed662167aa24a100e989e634ff389b8febafd53c515c58744d5aa6c72e2d82e9f344c6f840cb662a8eff5fd92d1d7488241079c767c5e83544d3caacc52e30160cb8cbd5b621dc916824f14e811a00a793f9d97ee292f3d404c57911ad338603a9bd7df9e2540012523eebb0e5687f3620fe75acac518b75b99c427c0360f286c565bbad2272af3c0d5bb28d74954fc05071f3a85b474e6fe3076f4650aacfe0803fb9d5c52d61b1d82ca826189663168717dcb6d0c5c1d1c50b52884f73237515e6d4ceb058db90301f0029269f095ccf443f6e94d81f22b937d1a62184470e4c8394380a5a92df5996b2564b6e7ea0c533013aab059fa9ff599d09413c0cff3b893bce29b9680aa5b51fbd61714f23401578ba4d5ecddff9fc04ecb711cbce0a1fa7725becb06e13f5b4816a9789f1d0ae1622df5265006fd251ba67ba16f0c21bb056eb40dbac3f5685d27d3e3a4d30067cad94a2419cd84ac85b9480aad3991acd56e2f4541ae7c8b0d65fd14f98675efe4ab23a87dd3c8022cc0b704561fd79ef3bd74d1fafbe8156233cfbded177215cb12a580c53d5a8b4bf6af144fd1f8eb439bdca9964d98be081f97305365186c3b91119fc9614de7c1913ebe3797bc6d610602d5cde0e09097e4697bd668e51f049a422358709c0ec079bfff735d8e89db40e3cd02d887b2062299f1c7eb672397fe0280119e74d5291f314123d8ab924c61238b8e728fb9b157a5aa46482c24b19ca328bf78482627cdff79ec8f7737af32990b6bc52319703d3253bac404a7604ab07ffac050dba28ef928d681e84cb5eb9b70eafa393e06f48fb24047be5bd123d7aec193ae8507a6cba0eab083cbcc12fee2d5bc9cc4c422e0a42a4cf0534999bfc0dbc744daf3af90395fab09cde5f8b20a8cb7621ae9acf35df5512c6506779838bd7c125aa0548f7a613f4d9d8fdc35a6dfe8fc1e38c02fe2d0866c69ab211bd260de3e1e6aba6503886ea1fd62f8a761cf256110ec399613509f001c6147fe8de893e88dab2e018294f89bba12af0fb9e6241c12d8c2a935f35795ddb4064f4e353503e7b81bfb902932effbdd5d66de136bcb1b4022c2d2e6bf357b7e960e20732af3812c3ab3b20468fa302e80e995e8a40daad0ec9053d945ebb18043af0d1c669eab2889f9fbc3b382bbdb45eeacbf39f0be38ae9b3d788d396edd37915da563dcd46b4052caf0bd6b22cc0e4cda6a169d3cd915da6c498184e43bebb339d1c4ca06b0e5231028328fded950734811ac2e71cf0e72af76ddf78d89207571fba3f4e541128014b13016dcb40ab5ebd95827439963142ca9b07cc5758e08b059947416d96b572c5923715ea36615c80da85dbfa381cd3c8647d32cf0874c32c9e4f3b15eade7376e564b7a58c5c13998d1614e6d6d2a05670d51f9058cf8516b23da098c69c407c834ab17a6fc512d463fbf5c268e3080648daef3ff53a59343073791939a52bafd76159593d2be66f6503e562ef5f048cd9f5a973acec5c329ae0ec862ec4dffab4311c4ddbe56440b06d230503cd2d66bdfe749d1b52bb56e8110de207265a4bb98eab6fc89ef577110139fe6232b7d7f30076108c85e2d8d2ba15f2d237bd792163c8f3799136d93a3df183726ba9a552aad294a6f44a29955295a634a5575aa994ae14a528add24aa774a52a45e94de9b995884e6e00ac9353adc99125a4895c71776a0a6349eae48d296d6f5b22f2cd88609402a0e58ef5b47b40b5839aff70a6eb50b3323f7a218ce27c868459ccd6d0081c8cb74fcd38707f713742c53bfc99e82271cbbcc04de4913333e63a683d8549bcd8e1ff7e0819bff158d17a7165f0de241930a899487d286c1c6c96bd996ae4df31376aafd2667fe9ca2d6d9c9d39c859c2d79a605482b76605b1c91276db6aeaf7403be29d44c4936016109bbcd65a88d95a42a952267e79fea3b644366ac66b2b5a0bb4a44d201c5b2196f358c5fa0d60fb8d66f6b0f300c55a8daac191b79e0d4ae3ae3978af1ee960f3f9f5b5bffe3eb08bd27c1ba7fdc5c677c9e93b7e749128c8f3838ebfa5540c22f5f06b73fc3749edff3f99134481f96f51490fa29dfab70ee6ef12f97ffe193c8882daafc8c4b8bff1c6f0bdcc69c0f5a016e04b4784200a36bfd62143106ddadee063f72ad5ec7ba24e0c9bf505cd4d4282a8f44917d9fcd75841972e28cc8474ec20aacd7f20f1c120dab3d7aa378b17b323e0d01b3d288123544310a9e55fe6a52c88d4cd0925da8653fdfd45e1b7ef29cd491f4582c893f82beacf06d19299576ba198ef3e88c03ea2dd5e81c364045132d5a7c93da84e040ca25dffd38ef5573487341e661005f559882050106d859df3c4c150894c101d4fb7d87fcdb89685362419f8050ee2a57216a18ea4396ad744dd19449859b24add8df65e6aad67f6594f0f222f87fb28b400e04d10c105bf3b88bc497a8d8c46f5ee470447992e2742f31473065190adb3a0a8866ac6a122778843c8ebccf32431d4db50881616026b101d8f5d443584a8c00daa1e22eb778ddbf764aee5eca11325fbaf9b2a47df8636fa12e943a10b21f11ee96d392b9e72a63ae090ddf896f7346df9f21fb745af3ba9f42254f529a9f8d3903b79d31576b957d27dcee21c1e1d75c5d7bbfb079b46bef4edaeb7bca769cb97ffb82d7add49a517a1aa4f49c59f86dcc99baeb0cbbd92ee7316e7f0e8a82bbededd3fc086a80e6278da14e45d500369ef384a61a2ad32d48e4667dbeeb68fe30b093207326c2524cd8826bbfa2694fdd5f05a0c6f3ec25d5d8564c7132435f4668f95bfccec21f306b33c153095bb58e6dd8dee3c18fe230837567d2db487e0062765cdd374d324bfb7b47c886cc109cba32fa69f14b33c37af477b4ae5a2aa7cc01446b1d00f1a2329c7dbb830312f63b4a8b4126aa85e35aa845bdf6255c01c6bd5e9552136cef4a2d2a19574ced87141a3d36cfb5b0e6fa4da59ff5c0ef75926c08ed12114bcc52d6a43f63b6f91ed60a64f6c43579bbb3258d9cc61056f10aa0222600ec649896e7999db1b921925137af762c09651ce5445189018df19ef663c52c4249c3b53aa0606544e355cfd5f4047adb25e4202fbeee9df70c84c91059038985a220948727ff9f2abd921047007c8080ccdbd1b7523c7924e0b40453f220b90823a13402ab95b3f3a2a0e2306e0401a0721d68e0012f12f8a819ca90048e8707ed4725009209bf7f063c6a191ff33c0e320c932eaff2af89162a054a2dd7fac8b3b4912bbb49cffcdad031186fe1a3388a257b3ff57fcf5f8a09790fe22d50e7b0fd8cd54feeb7490751feffddffc175d21cb69b2fd6b30df677bec884053feb5b350454f42ff97d97722037ad3fcb4a222ef8346fcfdc1b070892cfccd84f9e2e9737939f081305108328f3fba3fa47ada8fd182d63bc933fa99635244af88c47e9c16389950b6110d212f0dca871a2cd1adf415294ba35fec368e68e35b5ac681afc70638df5967716f0c63fe57060db741e3a9fcacff4b2adf7c637d0811ad494e066c228b2c1f12096ed517d0241ed48cc3cfe0df39425fdb9e61a4c1480a3c4f0cf5da7b37f8ab953a50f933cb90d92f5d9f169160ba1afeb21d9f89d2286aeb2f3b4f00c344dfb603106b53c26d71e8de9a0e3c21e88cd2ad8c74ec2731558f6e73fd04d07a8883f11c4f735d174495d84ab5d75ad88dde57961d8aa041e6b6446652fb8ce13d3b2b4a69a83822c40b27934dd53b7b7ad9f510289ed1ee0a4ed81341f5f282378d99c403887c45549e5ec681b4b8822ffa89d63cbcd0a7c56cf100c29e08aa177df943f1e1c5248356e5051d6cf4a2c465d30b0208d1ab43b0c9a7d91f5dd7e331e48ac77d2ce480ad21826a5948b37040fc25d9ec91eafd607e6ddc10d2c10210a9dc874c9eb688c6368b119232488b97e1ce93669347072eed96066f0e13d225b8060d303ae182918bda55ad55e9795a23bb693461f3d5b812fceb7b8679efa961695fd4face87f6862685697d3e8f7d83eee93bd6eb93fbe5b5d4982f7ef70922f84d5c9998c579dece02db6840f8f298c8ad747572e8435b7f5b2626308ab640a6944a820d8026e19847a7f6a19672a611103dfd219c9a5630c36218b85f2a84464da1a66c69df13b65b0a44420f45c832f0756b490388d83a96b40e3b5abc8fc5ff03a637a065a73d94c8c86c715e3afcaaee1fa265f52a2fa11d21bf621a7359e3cdfce88128afd57aa8c8c8e850574a10ddb4ff41135234f4b8170074e44081df69131278fe2ab3cfbcf8c4e0f0a45e22bf3da5705de7ac3031810dde011c77415522524cc8684a1b8527220d1cef917b639f326edf49f4adef6af425d76860d115b0dd2274ed1b323c53a5603bc74d123fa733192a8cae791dd01fac0feaa627a76d52b7b8735023b63f616af0b3fbbe04b562e20f60cca28a7c85896aa0c5f86181ec6e09fa082ac4c2f9487a5164a8b59a97dba99221309380131698be45c2f9707657ca954f698ae0c233027ae2f677512710bcaa146bcad17f592f274b5e3344b9c052e488c3d7b54b1d2966a16d3178a70c90cba80a7a9e5f9542eb9c19d0beb771e101e7d7df0b333d00ca00b19065c703152d06ae318b8b68689b669f27f0925d70000dc5ae1d4fc7d137eb2b80d3403646b0d52b8fb5cccb7413284e46a7a235f646561e903d5a0b388a59badf1d1218d53bc5171aefbb1a6158411fdbc90e2e022f0f1368bf7b9cfe46918788860b4e53a78310c2339af78111fa27b228b24d9f4a59f8f2f3014dc0290a43c259aa6f5c7c7c04ab0df65583f150095ee5c6f75e829869024d31be7d47e2a20484ff29a7b0249179eaf84ea59d845d04afd013d031b6a1820bc4d3e2cfc9e5dc15e92a98a72ebece95fa440dd804b5b4b5e8b3f76011cc39f17fa17e4f35dcd85dfc50df16fa6190b92483fee39d5a18d660e90bf28c03212b8d6402f01fd462d287f4a0668e93a8a6641aacf9caf20ccfc8acc00bf1e8b234b48d43576df7c8e31936559fbe7a2c39104cfe80d89265b72a79b0cf836d9dabdf7de33f329e481eddde7aa80733becae19107460bd78272f1c5af59500551a3bdcaa1b0f2ed507686f1bd8c804c85d1ed8e909e5ec2ff352421a15c420e6d7354b7f02b8c3a006ba0a6e69c4f9260dee9be0c8c0eac0027bf49779fc4e9d301639f64f01cc0358faf265d89d04119f3ec7e8ffef183f764df4692d3c08d20234ca94dc21dfac48af85bb59e9b83d6a5d56f836c937e00b2a1a21a6bb8475a6cf91e127fb540a9159430fb146b24fa426c28966d6aec4e29649f95f208b5a7185b535939a5900d2b9513ca96a26ca6c8768652b6ac205f880d45b24d8ddd3995ecb24c81dd1995ecb34c89d50985ac24bb27dca60ac835842c756429adb22a1c2a06d767c8d090d62798c0ba700bbd41033ddcbffbb9b1d2ddd77db8b772ec87bdb95b09ec937ddc59b9ecc75edc5a39f6c3bedcac1cf6cb3eeeac54f6610f6ead0cfb606f6e560afbad77b8b3526e56c4ad95dc60bccb8d95b86f25f7accc4de61d6e5ac91d2b736f056e8ad68b676b1c1b564d03fc51d5d8b1e372aba55def6fadbaeeaa235bb7b612d18a06e86aaf5e6abfdaf0479709b1852d369e57e56ad34b378a3e772ac43732cacc476041e9aa17b744066e9222a75e8d029a0b6cd3ab9b423f373548d5ab53855942015086a31fdefe1dd7d3e3c46879e15bb263cffdeaae6e7b7563ece5aa2aa9da758b70672840d7bdfa9987ddc98787abc19426a584c623bb52a7f50a3286a2236a7941babc30251c377ee8c5f97622235fbcc7cb5b9db20de40c208924ebd657e37a484261cc51e6ef435caa11fa8642a2366a98d2a8f44fdc9696c2d24080fc5304df47803acb6fd6cde1f1deaf625edc42dfb09279b3562150e68ddc9dc4e3758fddab5c87b2518529e66c1114e7fc8c9ee822f43830483436ceccc7a067bb03d4722cbba5db674b0a2f173ab86af90b05bcb670bd284a9e961c8b692848b5da17ce7ef241f2c2ea707d43beb1eaa491dc8b86d17dc4955ac4608ca8f9c2bdf7c1d4120b41feac7237be44d1afe940eab1f08c377e56afd464e7f641cf5f8fabb74a903daa04a41a4c4f841fb99a95607bb1c33be2df973739d6b009ac24fd62ce9ee010b6bb71c28f3909659d809cf5ccc17cd9a1a258c83d06ef6caa1b954d62f455d80fd4c2ea7f01a00f814e2e8c17c56bb0713931dd5a89cbfc9137641916e4c5c2635d574352584a3fdd6374cc580466c171b08cb29268e2e42134079c6317ccb8c2bc35b4bf1c46ed75ad03172c424c3584c96593be93888ae6459962bf430c0ca184c4373d4ec4547e9944338cef3831491b57ca0bc03bf8cefc599e9ca1864306e046d39d4d601921cabedde83649730cf1ec6fc60b729db31403d08b01fa05600d744988eb6649c0fcc1127f550a8c968075d47a6e5dbefe4007662c93e85934fb1e31497b40069fc267f13d810dba0198cef82ccacea131cdfdf5448eb44bfcfdc630a11172dddfe8c0d0a4a937b19f320feb2a9b4c54a73543acec86e4fecc7567f402cc2c8f0141163064416c470c06f48c290480e04a2b6e79abf0c93c17aa9d29d7e877da539d9886e20b28c856b4700079b1003d4c71f271e168c9c79509e4a444863ac2e300daf0072e80c7e7930c25d51074480c96351694c442572e99b515b8b43046a9158f0a5333f30604599a93451ad05ac031f3acf88e3db879c39da49c6ec68a22a864c8ec672415b10f0e8e2c426d5b2b49ca92725f1f88eac07968e0f490ca69509b4e003bf333883a91672a1c2029c5ac9321df5b2227704d2a01a6cdcf29e294b5a93a0c94d293da3bdb246787cad32d02f2b91e34dd69aa7e60702f2b32798f7240cf146f3be300886e739d5f724a921303c22d9306ad4d13fae5ad3a33ebc69b6ada9c5bfa17343881364e2c805e6a0f2830c9507ec46cc0b67d1952a6cc1d3e7888cafecce37680b1f7a583c4ee2f0722eb1f1bc0bc3ae6936b054de0d5091e79a2fdb986cdb56de2f8cd7287c87a6fefcd815716740d07f4bc66edc92584b9ea53ec5a8cc0a3cc09bff815d535e654e66aafc78e095e911995e43ffa804977e44f181a5499a29c7ace3d252a1fc47c8f58a9d1a034446c1b0f4d1c9ca2f2b15f47e20b75b58b2b602e0e28fd67e4b74cd60d6bbea728128dc2eb3be050a9377f8f2a3f1fe2527355a5bf2c76c96348d96465fb2e8ba3677b4ef115c6f66a28339dfc3f99f5726324e6c11413ba4d17b514195a1f5713049d12c8fe2a73cf9cfefaf3fb391528b2f0e1ea726dcabc3d0adae3de1c2d9b77d608c1bf9f7133b00208f71a162c97fb37c824f5627bfe847138b26444d345297f637b2a7ec3bcadaa181f0b69cccdded093d9bf446ac872c8e1bada757bc0bec1fbf1121d0b5e0265cbca501bfcf8999bda5395e64b981b043aad87f75bb33485bbf4ac291161da3cdbea5f868da2f16b66e9e13cebca39ca0a4597bd0b975b9e389d6f880f92ff04d627c3297e61b055bd18f5d7df3cdbc709880bb56465d5b7d2bad82d7bd8af5a9f40f2ef7372df6c6782c40e4a099193c21cfb7ae4221667167f27f9304f3a2bbf9bbb561b687b7ee9d918200168b9fd37615e06bf1fe342936212854ed0e739d940c9f428d760c102b2a8e63590a4ea557ad1a31828daaa8f34b3fbccedf9b3170c67091fe2b48b32933335468d1aaccf9ed0cdad9fc886db1baacf95df1f73b160a4272ff2f19645f7bedf1d9872828dd858816aa79a712290d07903f90ef0e3c7e0fa9e47f267e5b49e9751797d35a8f7a09ef850408835a7d5a03a849a3e2f1ec5f73d0e05d83c93dae3efa7e7aa1a71a5b3c09d06c55a9bc6d4492ba5fe3cd1193921b9e2b2dd132a01d6b4cf63c4a115829f0bf398253ba57ac85f2010442e6563f114868fa661d7921b063773e511213ce062bcdb8960702179a5296479cc155da0dc1684c4c71d2a23cc4daec7f25054daa78c175ac75c0a0a3da66fbdb244f1d175e85dc42ad7ef376865582f04e4f25c3b0b319d2e3ec5c47313b8824cebc8351b19aeb5268dc9bb3c277bd2e61a09981f3cbe46de4a1d4e1148b3711912c480015ed6a77f5da7b5a614833ff70258521610209fe8fa06203449909e00965fa6b11be497ac33bf238e4c4433e4e70b67d8e265129094a058843381505f7e20f766ff2bded63252a42edc633af74788ac10045dafc8c28ce0379ffde30f1326d36e5fd751e6bdb537a50375385e876c43f7e9b30e2983f0132d1d5230cb4fe71c5266c75fa939a49355247ce750e1f99f45bcb7a09065a9468f45b7a542c9e5487b8596218f997c1b56f326331bbf7d24880bb478c1f328a54f8b04da217e7b8a5bdd9f8eca856bf39b0b9bdc34c4ea161bf3ba4a3416482173fd96493d9a98c7228b1e4261a62172c3cce0cdc6564d1e484537db92edebb47e0fdb20ef1ccfa777be405d416dcde9c40a4a31b2933680ab4e9a9b2d1be3a899cd0ffea25b92127032a1e9d50cef46493cbfe2440562451b901037f109099e6d88b4261b96a4b3ad73d4cab2892cd111b6ae4777e3e660862ebea5663b24511876ebe80a298a5ccba853f493933bd3bb59a46fb773513b8b97f182bcd4e00e81bbbe2e12af97cec5224756523d42c85b4d9f746d06f02cd6c7fee79c9f7435d32d41a5c222ef43351cb24488c0b7dec0d149e9767162a990df68e289efcfd75e6b680224b8f453bc19c2535516a80bff4a661010a355dcd094320b868d40aa722c7f5ad3d19efbcffe129eb6b507757e0c1fa83aad407ac2c33d5e82670788f4eb0a8df96deea54dac7a9fef8389f0af103de3ed3cdbb4c3d38a6430b534bc5272afe4ff8f79a2faa0e14763883dbce65ad2fdad6a8b12d759ea7b2a009a485e59500b0e120b0ab1b6a9e980d175cdc33e30188595b21cb01aa6c78859e102d98006b7433c1fe95a75d5e03af58f6f87912dfc9f4bae59bff36736481fabedae077f2f43705fe801040037b823caf537db8bc628b1512f46583282c92b6dccd8bc8c2849c0e96fbb80a616e8abc5c397dee0794c29f3178155d26eb61c5925fb4ebee46a8acb4a0bd410e9a91b50f2c2706ad72cf9351906194716e9567bc81541423aa829e3b225cca3ee8b71292a42612d601dc5a7dc17ba349081bff1e6df820ce12c1c0eedbefd79c05a67a5e6aa990d8ef4c70adf1933939cb622c12cf34a4773cf783a8616fdb8f90e19c190d3a6f2b0f3d2d79862905b4e4d1369f624fb1fb38f60dc387c1a56049b93dcadf2244d784fe63ec1051f0e45ed571b7812ddac0aac50fb65f501ee5663aff452cec4f01dbf9f9d2e2932aefe73d49cd4f754836e4a7e91f60a00f20405ff0b10eb8b6524e689d6bedbd5aff0bac21e02b1e848c8ff96430ace9f76ac6604bf0a56fa0cf8638e6288d3727e6e13c58cae0fbc1ab1ce330941042c0aa87860b9d00a3f405f9e52634477e207b37d11410d4d18ebe301b735abea59fdbd7ae04fc3b8bd2aa94f72dc71a0656e03a417503f0853284752049b32143d8d8fef789b0a24cfe106f0e40ea818d4815ad0a51b104a16e599dd9124f81d570470075fa79c19486cdd640a5a30cfba60b8b2ccffdc41e5860d0474006dbb9ba35f48f05db0dda5a9ed4290e294c16c1897f4962f11a69e9c80e169c7c7fe5e192a01a180a3b416527c130011b0b2c7859ebceaa2d4bc8e6181ccbc25370b63085f52703eb0502c6061568d300ff8b4c70e46fed9fa1c54c80c677105ccd83c8ac565fd1e74af3ee965cb9e53a4016f9a4e8fd4c7a54353dcda63f713af149e256e3dfaaad469c63a0a58795f44ee54b234a00abc96948d1246ac7a2f697e473951cd830408b280a507ba8fabc0fc40b6b946905307d0e3018712e2129e28611d971a74357e0fb0490a084629bf4268b488df9edbd2b352ea2c07cb52e253c62d55a9650caa6bbf9b8b6b4b653468616092839b8f5e086be06b1c8b6dd0846d44166e5e55e9e0180da7db9d94c855272ad238c4597d7389e990c03275e610362477466c3ecf401b047336f61e7b02d11207f4e920c9a55e7f616e8a474a51389bf9f8d781ae03f5d3811d67c733e538b0afa982a03745cb685fd6fb2cdf4a9f48ee9878858701e0bb2ee3edf66038e854d44abc1d44992e900ac6dbc67a239b2904342644248e760d3c6cbcf7954e8f0d25d6f869deb2a611d7b8b3843fa630dadb1c184862f0881b0d0b7b3d469551f4127c233de961ff17e0608c3209980879e5a09883f06e35811c747ebf00b67e0c32dec7b65df8171bce5e52f619cc1d373bbd3774e4013fcda377fe15607515f8ac9544f81f7a52ad25eda0c726855298e626efa6bf42aed25c2a0c7668bdc082de1c2a325a6423a0ebf5ce693754581c1e58ed48ba116d0b68c63a6410b39eadd0b1f907c5cd461edca5f41812214f3100c0bb6527c149b7f03b147008b02569409e3c3c184efbbf8c3b1d0c5c14c5e250fcac72b3ba33ca48f957e5ce45fdc2a92b4864e1b3dccdd0d57c762e83992ea5ef88275b1b4c46882d0e3226a640da132cd51da004a0667607638774a64654829cc64ea2f1c8ebac6f7de0ad1dd9402cdd83883ca69d65d596f9710344dcbf3f7d964723c05ca37512884e98bd16fba0e7c20881b09b341894f9077603cd7393d387c851fa56aa0e42e49e3c7acad967f270c7d613eda55cdf6d6bfbe24e510fab2318540c1f157f38ecf22833b13ab9bb1278136e080639caa7ac5ee7d3d503cf23bf4ebd3b2be17b1f696540619b23df8026fc2a315806b178c5d1bebec2e162dba4a8c9f9125dc0269d1b5b6abcc3e902f82d6c91fca498f8bfc82392eed16f54c6ee1560079a27f9a179138e8b857a1dd8d4f875a43980a45a58983023c9d8abb807b168742472e54e03039903f96546f1bb710ab6e4c24955212ecd5ded05522eef1ad8fe84972f8a738a878598808ab6760218fa3772c0c69e20288971ede9e562050de3b09026d59b6cf77fd64ce687bdc396124b63836bc9dcb3c542fc5934b80c90b954a71c47dea7e55decb096f0db658e67d51f559ae7bb1b48540485b8161ceb28dfe403e85bf5888b4636f60138aceabd6a5ee77acd3f8462675d87c3bdf9ce23405cbec618e113a79dc4d678241452c35d52ef239ccfa6767bffdb12739ce1f90ae366fbb0fe201ed6c36cb610c69cda8edeb739f4f6d6a67b406016c93bb0830df922c242d46ffdd5a253677f608829781e5745756770aecfee3388f3e8a9c2843f5e9618a1f31c0cbdb1f4b2539dd0ac53d8277052fadf24b0f41a9cb9b1341639c779dd00e6a33472bc011ca134d97b7f1d406ff80a2733474f0313776b2de779ea78ee280a5b522586485004d6f9d659ab1e0e2c68f9153ed2cad76d276d46a6bed149e49553db8b7f628ac09b8fcc2c92eb580aad8ca00904e260218cc4095ed28e4ca56934d69d6ba0851938e723b36cada03b0b45432e0519e53195fd7a0a7a0ba7481e172b5efd3e9a60cce4050699d55b44fa3d6eb608c64cd1ad8cdaf493c12ecac98b21e446c56784b74b97128c62f8eb80192f8f6d52f8a2481521e63a9c2c298778ff5702dc41775b40237cb86bf444271e68bc60dbb1ead2cb2a5fb00253b8de5dd94b6a22ce89866c1a1c857a79abe9d0a281422d70587afd65bda63e08f7575d16036ea27179679013d462abadd6beb848cd9d6a288d01a38d759234500dae295df1f026c10156b99c90053ef08bc557a440d5d513e5271489b1376259284b40659e40318ee0ff0394dfdad931c5f6e91f9a1b7abb698fe23f1233d40eb911f238451adecc2575c547b097d5a7232d7380bf2cf52d10dfda80dd9b46a6e4982422f14612eced42c1fe686020e62c9ca1e80b1c38cd190b78c29aca0a66476ef110f13f4b6e463e762640b3e78f431d0d4aa27798281e115a93c8455468f6cc6a096bbaa22b337c67a1749360feaae045ce0b2cbb73c3963048a0c6270431aa3c72c74dff9db5658b8637abe7792c7d13c4d87be70ad26305b4701f0b32d87ad2972b1d69b8fa92599499f0e5a96825bb4da13f1c07008edb44c0d5b1569025eb6d9f59cfb590c197468727b3c105c09bfdb5f1dde6931ed75d140919a2b6fe7da23d4a57cfa3cc421d501d3a3ff64fda043e4662217f55da35fc427db60c3bb03b53938e251268cdbdb6882dfaa828c078c80762673dd216516941543b0dd4f1eca90b3856bf59d563cde9d32a00d1876fb88bbfe13b362774ebb263482f5633a272653f37d8672aa8f17e6b277780adeab2ceb4ff2fa8d0f5b4e7dc15e82f0c090df320de3ca6e8f200943765078846638027271437dcc5195704e87ae611943dd1b50e0edb4bca4ac071ab6b714631c289a06314f5a09d6ec8cc4e0a9706a2004a545e941367e6b8ed22dc9d856f0ac8230c98d8efeb18371f5dbd1aad849fa0b6e147d6f0b4ccb2420ee0d5906fd86639c2c8d222b3a3fec758d50d4ff2b8b331b04b74ce58c9de6c2bfa33d2da94dd573fd7ac03ecde02f69a3dad15281541e5f5dc4eadf998091c13cb0a2502b189b921d7c26adeb3239f3b634754fd5bb15116a6973d6ce6ef26f68000b7c3ad2183ddcbaf5c38493e1168376834efa2743619e14042771746f4ac3dc002cb74dcb1b111227793de00c30fb49b0a551395fb2dba3a7923efb9c71894e6920f5bef8644dc4cce2d7b1c114bade4eb50f842097b9d156933a5fc66790a4f0139e907010748e251dabc7b7d4a9e49eb5eaeeb660bad27bd74da1b4709e1cec5988d9d1fc3a044fe0a3476118e9c223df7a380046b1756321de87f0b7faa4e0e60c7adb6fe101bf96ac431663cc85d2947f0961e6d342f1a262478aa46f2100449c7a1989e0a1d1ca26ae7fb24e46ca738d5dd3e6ae053871a5c46d4ce1e479db55d2cc8f598bbbc97676656620d3a88baa9c4510f7bff2a457b28b0d4bc1ee15100ca39c1622ba51a4cd0d3b33872a013f8d6489418202fb442db528b4220d80dae64759deb39794c9d9567f1da9291230a7a3e23701b0df40b9120f3c3ce54f14c0c4b77c688d2c7628507afd9923590e1058d6e84e0219ade155238c826885146151be243826e808dee776e6a793984000e763697a4c5fdfff0705598d7ed933c970856720c4a9f790316c7eba011d01349c1cb26ac4f8bbcf8f1ab52b4bc1007f156cdb51d1db5b0917ff7818e09d60c92225290e702d97f25b0923778dac8e008e88252d888ece76927b1e9bb94e23828879b6e2d43e7a29a215aee68e5605e19ba18ac3e636b8b9ab81d3432944979955923e440a32d4ce42dc54427f00350d6546639481618ece98cc8bab02593d9411b76a0509efd1d26da011db98500e3d1f3c7b71c8d8378ce60eb2d3114cf5e00df437dae6aa16465dbdb1e71d6491a9f3fb57559e72f806da93ba715498a96fe385782031cb8684276ce528ffe5a34fbed7af93bd1c4a3711e7394532645b2162694700c764a1e526885618b34f3903d499c0393d486bcdbea11424db71f1c17d2eb0f7347f8190c44f260108ab33d62b80e510b0bfe1bc8f3aaf9dcd2a063b6a7263098e9d7528e3a3913685efdfcf2d1220315804fa63bef2d3f8fec3e48820124959248bb8edf8e8a245e13bb269d249774d4679f93a2d0579704ec47e6c58e8022e38cf81aa4f2b095a0168a26150ed1baa733c82e64086bc71fec74aa8ae122565f0e7b0a62bf43d59792fd30cf7ec67dcd5e714f7d0dcdbefff1e5dfef16d607d06bb73a2fb852fd826c8e3cd9b24df2c7d895247d38cd7917715c07e1b3f4be00b6645710af3220c875d1c8879498cf9dcaca69732b869e99090a226fc47fbbdf96a57d39bd922a6ebe209d354ecd13fb5b5dc2da16e7e4a8ae17a0cac236cbccd871bac5cb88e747be1b5af6abcc64eae63c735393b8ecc4edc73939ed4283f18fe25d4df36d9ce953efca89ee84e64f9f240a15a8dd4201bc423ebaf45182d72a131d23c7264e85d49aeeb5978ca568060bcbb0106c7d034629d6c1b7e4e4aa200a0510a43a1bd512d9ee3a9da25ad35c448ae63ca6e73e8a31b2f3806c92193e495c0c9118e748e0704c9985fa66dc8910aac5526c659b7ae97b61f83436390379841903e46880a155eb99f0f2eaf883d868aafd69f267c78497e1f88b78fd57930e02568e8280d33c3985a2c08fe7642cf2f15b5860d25606e8aea47ac7e193975736a4d1a738bc1d19172b5071f341a50b989dfc7a52dcf1b65f415c479471c80d9cc1dc02c6c839e48169753a9f200fc7c2150ea5c184275d978c4d37029aeb55482ba427853646ad427668eefcfb34ad2d0c6664f20a1017a2d747664ff2b96742944eee655ed3e6f62ca03db0959db14934ba6cf1a2b459dcc28aba00663cf11617999f00ec13dcdc14d784fc2d745f919e721f11a0d8e2e37a5f4a7f6a845c3115c514df815bb8f804b059007261d24abb41b0f9a775a7e8953631ccf02b0b111e98039dff592647f1255b9d9948ee1864a94001a4e4ca146bc21450b99b9a491959e3a370606976406ac3940080205744440d3ae7339294fcdcda966b9deeeac1ac2588967842662f663975213d019780cb69d51903013de9eb02f55ea901786c648cc86e46fbeab2b0d5edaa64ab2c654ee4e70159e7d2ff1275103ea3ad3bf284eed14cfa73be5b8496bf75a1ab6198d0b63dd981d17c4d616f4d4789d9aa083bd5c9d61cdc0f143307cca787e73224dde069005d3d120ee7cded38ec3069d9cea01e60914c9bc81831631bd1fe59841d13a6e6f273d7f56681e48fa48576f56cee314766e2cec1523cae8a2a6317a36835df7b82c304c46720a49c5288ecf87e4d94939b2dafe3916b59e28831b8852dfaf9eb9b8deef17bace1ed823eb6037cae7319a4d7ce99918de70890f4fefcfc2774046dfe9008637b5620150086368c4329bc20ed2836be30f2b1acc43cd5b19d0b4a36ab0113b312897f87e09a3d18bda557f43883e8b3c555859757853e2be69b2c9b4ee316e70dd8cbb2b115559aac95039f58ab390cc6689bc8019abceeff2b05a4d388a635f5f60fd9b82636a18059a9cec9eb01419f0c7c19054b8df1904cf009462583d3c38c253b5a329a9293297c57ef0e0803b1d3d67d5065a8ee3c0a91d620763a328641918b52eb1483b6d1f5f70dccefdb821f9806eb946b607391b97efdf66377ffbee9c80b76cf2fced498c9f35b798aacfd18fff3a2a8b09842dbf35a5a16e01bf30a937b9be3e78279fc65b29997ef4aec2d87b31b24f6add28db69fd1145f997434231df521291a8bbb7e90dab2252f92612abd1bc3ac88eadcd7a5d3d3b185d0167f34821401637f843af9d765e262e25560e392007cc41ec0618a0edb1b70110be047697a906741d3be5f3ff76c456d48cfb63780a0dd709d57381f5a39c3a351c1c4e24428708071a64a3b38a6ce3dff6d4e2c2980171d12a376115cb623f45edd5cb5833b7d03c3b1e1669341cc497d4c0f8f30cfb328df587d94a30ee19900ff707244e43920473485302244f8810c66a0b8a4cee8cf5337306df3a1f4962be8cb0f3524736897751d17679e12d3780f73d806b41a8661951f8ab561dc3402aecc28f279cfe6232239abe20c4894a21909d8e4d01811aa25031b28e55ce8bf30a0d9cb81a3451328d6d63858f4beb6b0fcda0ae0e05786ef95855b244134ad87f33bb07ec0f3b3c8278fd8fcb6c0da469ea8782fb9b79ce928f0c0a4fa025752289299f0004c0072d0a8c74056e479e6ecc379a18d5b3b6f3d59488ab26a8e77781faa0c868829b3d710b88cf2d4bbdd0b37950a811c69e12e686b9b26e05330362f8c496306f0727cb71108e06a87c416a5f2c6f30c3ce80b1032a95391de69b748e2083889f38cfb5eda1ae499393323e60e04d7fb19cc75629629ce4dd2add8142ba16f7ed1721c251122e9275102393552b4235659a1f69733bc1048c9a63a819d0aeec14a8c6b2bba0aaf39a138a90efcbce2248288c08502f4e875ba97c91ec148e8e5339064673b27a055175f7c6cc0169a271559d97642b22dd31f9c1c36fbc070ea3251814980245782117aa37189b239d3f9207a16e674ef20736db283fe737db087531035026d5f8b838cf5584a0fd0661430f8016258177c86606248df2e76a8dc81f5b51faba1a4e91fbc125004c6592033e1472c4301f659bd6de84048e9cf615e47e8cd42dac8cfa732ba4e3ae64941907014bd78948b6abff77535bbe6873c2c5a5b538ee136ca4aba09d2544a7ff7def6dd407fcd1072fdbe86a00ac0584d22b6b8a6f056aa60f33684711b7b99db78b8516ca8f25f1d36e0fdd8063a4b8aab8a0a0d4dbc64904daa39ba54bc79abf0053bd4b5776122f4bd44c53fd5b65489d24e1fa387450e669b5a0be05fa76d097683f61c03e1f3b6a0f384a958c9f49f50b3d76d8314776890e05c53085a6d331baf41488def63288896e83113d89f301dfd029d5f9d0e955d070557e69e0e5eb6aec7964dc983c300330b61b1654952207fa041c5360dafeff89f1c1d1182734a1a41d00d4a049c6f153c7886ffa417f1f43b30e7bfebe773def3a2b172479bb1d916d0401e103880a6cf0a26cc38595ca16a9b037bdc995137bc5895f3903dc5e9c31dab64aecf721dd195f5aace2ea8a8624384895a0a06781f674341a6ca6ff0f36703e67aa243504d14c074321e23e7dce90270c0ebf337657071c9334a7f3c0e2d713ee46ca5e59c61917ffe1bb898565a6487088e89302ebd3199520d0096bff7ab23a508252ceaeaf20ef1b4bbadea69eb72b8161aa86b0d3c86c146463cee3a5b0c79f720fc35cccd90130b12d49bda83a3bad8758c4b01514db8108f9322c66cd74e14e0e2fdb7c08af65a6956fa6e40e949114aa35c7ac768e250626dd1717314f0e14a682218f87b97a1220c8b5212b9ad0559ba8773c1989f66b7933ab9f55086d61ce96ec0ce8422e18de764a0902e06a0a7f59f11fb613bd4535fd4460fae71f5651bdd637e84359d0663f3025dfd7f74753cc04946937a11848fe5f057d04a23c12b50b5d99ac0984f5e2399b3f9c9c10907e1795ee64899a8d4337f721cc61957a7d5c48796019026735ea2e1d760a498e20b09d775f90a9c41051f482d0fbdee50c63f4209f14452cd8e1b59ac6f0c8b1aeae1f8ed2001994f2073eb7c126c7419677447a924f38a2375fce4cff1b8f823be0d78e94f37c6c57c5cda87e0f71e9bb991760d0ee4fb23fe1172a3ac3760952da83b448e2dbdcc857fea6d785032da3e1a50634bded279803a2229cd2c8af4ef33c3e5739994298b2e99e4e5066b4349f9d8ff757ab328acd842a4dc9529cd896df54c2d22e97fc67a9e5fb1d880725173e8be4048cb8f5cf593d761ef5ed8fb9c742bd9a0ff0de84942643212f933dcd979df97df55ae4ff9cf1b3bcf75c9363097632900219749d80546c35e1ebf5c00b7ccd000a1e1647af94ef05c185d896b916e2dfc1b3dfd0e8049410cea9f8ba145b9aea37ec1cc45b36fb88713c75301a5df2c5c53df0a1cf1a09b8bb8fe5557e90f86d98390bec6598d46ac5e3a19ced0c8ae3fa11252d9cb90fadbad4fc50e792eccb9c1896b9eb40e9f3c1c5485fea475ca6696503802cf43a15e1a629877faf6c7e97feca563364c8ebf830fea6bb6a7904247492231e965d8582f1bd3127540cb3d0548de6026a85a282bcc9de5802991d78a1b3b026a74432640d2bf08820546239dddf4689c6fb9b576f4c17eef015c4b9faa1de7becaade87389e102f4c1b38fedfbf81d56cb73ca51cea15ac37835b17755cf43096b25fc04cca3a1bb56019e84112dfdd5afd45163351012f54b94f139400666b5b02e517636576cfe34124bcb6922b5809ad582e6fba8e1bdc85e8c8e1eb946f3387370118c1f253bf7437a4a9262f2f7c830fcdd6549b82d78f1f96c1108d7882cfb872ea19c0d5969d898df1e3b4bc3d9990e28d522e5107ff001766bb221e0f634ab5a1b02a410835df1645e6375e0c87a69a846b88884e28c21dec3f1c7088719b6f5bea0ab5db126c5f00ab0701a4e642bf2452f465d6cf926ba34294c28f3ea29aa3006a5685c6ae531b602d7305b8b0325bb12b25a2748e080cdc78a71713a314badeeef6021f4b6f0875fb91a54cb3d054504c2314e5dc425a4249c19ce6000afb840046d885bb28f5a1af80c661647509bd3aaa3c5effc09b1f6fbf7ffce16dc1a52867d90497a84749c3f9d608fc676f309487f3eea7e5a332d7b631a1544f2c1fcb436850f380b127822ee72a7c40fd199ea682a654253f869317d97636f111f35f17c3517cec6874b1952dc168db5764e8b59032860a4b22b74d264d3192099549b70d8c5984aa695dfac6bc77ccb6e5a0e47d5dc6971be592b918ad254f60292693d379527892bd9dc5c85ba797c2f03503524d82e83799b016b6320f81689cf4e34103299e4d7dc1612ed3f885857b14b4eb5c9a33eb1bcffe5038ae245c115480d343e9b602cff2daafb6a2a1bab580d281daa9006a1ca59ea02e87434456b0f1f5588ca4e4f64dfbe1f12aa884f831601ee0d236d9ffb17694a27a43840e4e3ca97b22e34b414719a48ac08798ce763a900961d31c30f6d8694d47a0393f81ef0065f7e591455da8815b4c0835bf56d9214750f718e3aa92b7fa0a59b6a0e2afdfa168fb55a1893537bcc6089b66d0f5d9981ae73b1a775108a8edb40ecd9b9d54c3f8a7a3b1a3cfc2a9ab4765a771bf0955648fa66b926f050e5f80600d1728644147b418216cc3e9fe687911969e63f2b85e7ab44527118fb62507d0652bf754f82eb5bd47a9e572f035ebd1f01cefafd812d704de7206dfece6d44eae2a3aeddbe7b5950521e409a42e7ef1f96ea910515c8ba1cc88976ac816c191722c03b53964318c2bcb3a61129a720b16a10907a94811840aedcb513eb8d6a233cf99293e5d5eabad7aa1226f10e661ec997aca55e2eadddab4da61b7b12d1da513415fa2dfb4e603da1024d7dece08b13527a1bcbd79679a945035dd942d68d2ea019f54de43a073bac043a3b8662c0209c285f6575f684435e30c38a44fc5b2e0bb4ed32a3127d33808053e63379772c4ff0e4ead9d3b404da6ef4dac257aebd471968453d8186fa4139e3c57d1ea83dfe3388d23c41515b76f7a2636d5bf8799df0e98abd14d6b32eb3766df734aa127ca66cf77f75bd7122ac6cce3856d0c63f59122e53a056cb72466e296b5649b036969f460821b624cfddca0a6085ebe093ae71368b0eec6e06b6d405ab18c45297708f052ff5b22d7f713a0ee5cffe684e1e40004729d6567fa362bb41c75f7211a877c5ec091920cb62417a61990b16a811d2067a72f113d7b930db5fcf766f05e3c1f91a73bdf1b8da9cc16b640cd13ed2c11a742ae58c903a8c95ac3ec65235866c41ad5247d9fc97c7d200486eeef58c014160c06c5d6df4bf676e284b54f420afb1c2f6584ec8bdfaedd22358f2e0d7e8331974de6ab4e79e79105de81ecae3e261ef8c70293b4497cd478a9ccdff711633c2fd336d6943e967e7761565cad7b0a28b3f42c564a758d0dccc4620673edc633ba731434bd760015efc41defed8c00830929cdd6fce7fe24c29a80976641f60bab8fc9f7df4e201f6c5244a227ba234e31f452590b9bf017b670c5c17d889073c688ccf7ad265e57a118f22a0d8720888638e14fb57308fcbb64fe232ce1aa0d4e15ba18e1c1fa1f78f491dfb440f2875da3dc5b8b637584b2444eeadb26f508950c46995f0bda377e929393930897f046593bb559a535825652aa97d030aaa20d187d7e43403856e3013847a5cec0912bc02edf04a38ac7d0d4ef70a1d1630e55fc2415129fade4c47e10941001bbf962ac21b1e39eb9a68c2e75a396e267b15ca042b37bd9634c8292d0020b17e8470e6f67a134694d8859c97963f1b2508f8d015a15bd1d870a903382ed8c6730ab4c020e25c3c284754ce08d770067b824ce19c827085bf821d25152272074c226981b2a8813e751cd013829b9c9224bd11b113e3e82506946765e02798b99a352c2d9803cee05df40b4f012f400624952075849dad402ee6235a039c10a86f72e173bc83306656d8265230c85b3e5002c126ed3691d64ae680b9b1ac763dd46cb7fbed39bb83f5995526ceec53735a39936e4104687326f485f96286109215d7bcd71c9d6f30bfc7e6a609fb059d87ea32b8c0a166608c46bea7101f95e10a9647ec18414410979fb3296741ed994b131d0596ca54573f21c774449f425cc72a938f8d8c9460491b55e9ac892cc47c07b8e23f50181e77377774c441fb5e85359a107cebb372ce23d10b1f46316f3697958370b3c2faff2b9f1bb10ca115648bbde778fd992994077961e1e66e8a5497b1044a9579de34cc90c1bade9a03e5a9bfc0779aca5a8028a0d53ce5cf4f5e253b6c3ade4c3f4ecb0bd2b5660fab3b115eea0a3e06b564ddb6d37cb503f00f7259182222f6cfd10de8c38054571d232d863789cfa60f29348c3c9cab2121f43c22b810db877560df0b6c751ae800ed1990d327196dc7435286ff7d3f8d2bdf75c259e8a75dd4a27137ae0aaed7eb9ab74fdf9c58386a4935ab85476b5c6d22da326559c0ab621d0d18abc4c3c4eaa9ce011ef223db6f9ffbbfe8a1ed2b12dbae92f53802a4cbaf1c72144ddaefde6af8c22d93f0ea3609fda6da5de04d2660b49cb698f57efa83ff2992a79de76d6298d555bb7957fefd13257fa18b0677e7153df1d444ec57b8bd8b3d1f4d40938615275306ede222b5520e41a819cd4ae323662c1649a5ffca0aa0e1374404a14b66977d962ed315f805959bedc39b61a697514281345624d4c8333c1571fb39f73caa045a58f2378d5c754cca03b9ed5c189132ca0b4d33abb3a9d70b6db38c2febb51d60dee8881222d040c6a957944da8195b12076e2c20c47f68bdb1901734f116ea07306c87f5f5ac0f99b9e895af67a9054033a79f043903a523ef8f45297c498ca8f952d087ecf92cf7d61bf921c5da16334b6a0b6844760453cf7db18aeff98259c4688dd31b6e56cee99398a33dc4c32616771088cea63da383129a5fe9eb72683f4d40c887128ff64e01c07c58475527aa018a2dc90fdf2fb5ad91047f4b38a79936cd54f991438e7c142e24777c017f8f151a5888d5762d9d3218acbf3df206d310ff37dea37a077395f1b7692b3c250e97ae1c43d462c40f03f98e8beb1e910c717cdce032ac3eeb4f529afcd68f44044deb9a32c1f7c5ba2e52e2c974dd268742a89cdc28e47060f5bbfbb0262df3420ec1324815ab18d82373d669e5ab697a6e5e72a4cdc052134deaca25a5bc4cb69ede3a5912354f0b411b461e105f229d72600c6183c506e6b4345f5c0534a996ea76bad10928c6de65a837230c669766e6bbbcf1ac50ba62e7cd2a548f23a37e4ba3a8a76fde009c6c5cd95b2db8969a6d16b00314c9ee6c20aa9b41021b3b56c8c90b4416bd69c2665f4c8cd96ad971a2df29f9eccf943933b0e8d3303cc3c9204d8c9a74986e7097ba0b6d8e9cbdc5614090c4cbfe08407fd8358bca723b8607f835d8fdcfff0eb5426e838e1f4b52971d56f07d9d9ae9798e433956def8b20248927377743f00d5d22d89b1c5581da43f1b5239b5145e0009afecfe5157fe03384f9e72f14b3a23efed4a63e167ea33780263c044d896c4bd22af94bb32259879290e6ceb1803357c3b90a05c2f2f5dbbeafe1f384e0f296d9336b172e48ab8c0708a262c27ed7394549d45599d93c1fd3b28d1be0770800d2beda2ece65cfa23343287568f9f4f70ddf7a70a67b253ea5c0d7a6b49bb7fca049b4bdfc2931171d02c5306f69be225c263530e5480594f0c5ecede13b5549641a3795541a522718bd2da12087e732e08ca3fd433e2a1cc02d49613951552b432ef6e718988ff1c90e8a5ec817d24e98105a229c69d7ce6394c276a341973dc961a5140d48ee335f58c663939fe179eb5d791403345a2ae56608618a32ebdafeba462a4dea0523d090acff8b7a8e9eec299245e2ebaf5b0d242e5ccbc02ebe6b22a34ce2cd4509f7eda46761fe7776f725394784b132efbd587aad9ec13868c2bfcc1d9450c25cdfc822c086c1a09355bc4ac565e01c0b2df4a822951fcb2d55cef43363a98dcd200153c2a16460d04f78b91a7d29f38cf9dae2ff369b80cd5517e425939df4ea3bf42f738d1600b7aa033d00125be557d8c6fd76dec5dbe0e5bbda597222087799fbb5df0e0f0b9e27c059dd27180f9c4104d68b4d90a8e862116fbfa45fd2cdfddc2ae09da86f51a041cbb6d6b42265976065f8c23402472fd923762d18190215d6b670d21317af635a6451239f19d24926874fbe2cc41d7b6c323d99af45a961f63a2438bbf5df05d21909f92459bec35fcf791e22f0460309d835597e2ba48bd5305020fb93fe5368164a8aeebe3099d83d9d03be6388a216df0cab8b222340ec513bf7cc1a27b868524c08f680e0e450665799d6c7684046d7bd5b8902e5f98ae321464a4a697401cc22a29f303d6e97b31974215a02dcc8c643e3313c90090e856d2961c25eb64bfd8f1f0006f1b3d693cb899a162520c60f8abbdd63bf2b21a834939cbbf027a75afd860e8de1c2592a5f453fc3a5cac3573c672532249cabc257da507f0a80cc2b24594d249873cb6a6cd9eabd76384e5b1c82105c1286e0992b0bec8336439ca02dc1b6d051c4de526c8e48f918d2efb9d56c9d9cb4ce7d4dd0d1b7cdee8c068118fbec889be8055a9057720c36520af453ca49a318eb08cb150817fa5a1d4298944f31d974471da6e7ca13bb37abbb3a08dc8994385306f781f1cb01b10ca5dc5cea2b343232cf970a4ee99aaa3fcb5643736bc8a3b34290872f31dc473a9c9eac31c87af20d6e71b2142ca577366ecbfc39fea6d450d971cd25998857cba9e59ae034d49b2f4213259976ddbb89b69a3164524fec3ff3f58905048cffc8f3f1527518d2add127c74f82d6c6e1907231bbec662ff8436ff50278472e164cd7d48251a6be05db95fdcda9a1d7321f46cd8db549e22d830ba7e24f3a4de74791597f68b38d8d4980a482c9827d009dbed5c95c67f0e7107e30a213739c5108a9e5cdf3c3089937bd418f4c3d67c482a0712c8d2332724d44a964b081012d3486e669354a0556992c3b842aed9c90113b0201ee1021ec6216cc08038800d581843c383766260166e800bbb19e295e5558c43d461491aa121f8c2c9667af3f51d5399b6efae32ae0556d9e4160cd6459f92c2ac60a3657332ebe082aefead63014bd95757c3d724a81bc00f66fe6fe51f9cebb59f3b6338a3aa091106be54e74af27d59944478e54898556725187a80e05a292d30c82447b9bebea127290c3c3777e168c9b20b95ed79f3451ea7024dff5723c3ba33ed20e29042ced342452a659b006e7a808931cf034be30981874bb615b5df28e1dc812a093a5337953a5d0886a5d27c6abb86d70498b0b42c1c187a371d32ae646481a2cf0ad4f3d358828b4e96d14457c9196f31d6c02a3535ce87d75565494c68a0517741269328c1b27ec402d475a6d15d9d80d4ceb84367c25b5492def889f733647c3ec92ee6fb6d68b1ca752f1bfa17b353b4be383bb43687a775d08a70d6af9a79e95b5a404466a3b88dca60fd9af1b04e2796ab68517f186748a01745c66c23db4ac9aef592e23507995d09644a12a53613278d081e10d71d181e3285f8143dc366a00a77c8cd2a3155593e16be0e745d9074a5c59ed7308e81f02b048806d2c210c350f7406f4dea75e11586e8ff19e2a47795d73c6c3bca60f7b9ae4c3db6a792bf1a2544bf734488d3bc444723459570d4d3b9dd5422fbdcefc7a2c421eecf28734f3196f41b89095ddf73a75c448aa392493c35af64c5d20228dbb45ce35bca17781c7c8620873e6a2cb212405e2f756905691c20018c44169b037cb6d1cc1ebe191cb528a3543874e096d7f2f5d6c7aea05cea0a405d3c687119008219baa6534a227f45af7ec4e90245b1b535290be0026ed5f3039b167ef3bdf9e3e554996e9d764db27c741fe65a88bc2e4488c123473ada883c733d962c3b04e66d7a9ddde021103016373ab032e3c0277068305709ba3c74472a6ac938ee63d37d73eceb76b9d75ec863db2d3e48c536721324b14fde8b93ff671c17872372bfb25bdf140da2e4aa29c1d3d8be01124218068b336339676a4516ac0a1469486649aa3821473c45ec1dbde4a990094ea2110c99ab1461373da5e2468a160ca4ad3a152144a04237861a2e772138ac9c0c999fd12bfc5feea02150815a004f7b6c5074ac672d985dfea69bd873ec20566ce45cf8ef86e31a33c1a7df84ae4d533aeb0a0b62c485b2446276f60e04c4797af7a696b54dabc51e9fc4938eaf98f0e9bfa6031f8ec893ba48218398d86b631e0474e25388fb8b8ee21528668428589f7da6852a2d7775093d71812ead9f8822909f1d1c71bff3f10b342a7b74f1d70303912a83f626b677fb2db907b6acc9dce78c8128de8866716f3b100bd3d18e749e3edb9e33032d440adcd70f46eabca259bfe992d725b6a71bf582458f85df33d0b2d306c56dd7936552dd85d603a16298482ae1200ea32a04c10269b39439a48108f24137808a6690fe7e516673e33ad28c4940b801cad174728a63020cfe48e5f6a1a4974e8570252882d422aa0da30622bf0e584104a1f4f4294f474af5a7ab3f9b3cc045ac888a1f14907ded21574383c8aad2d52cb04c6f33428398ce5b3f324338d6bf3a6d0d7bc9cd147273fceab9142f540534880e245f1f0ddf42e4f186062127ad1c0323f37d3c1268505075e33dade2a80043830ea59f111c63883abcd9de74a78c1ae9a3d27808491e4b7833eabccd83412372188b7c85ee3d37dcd0e4e4f2e54a98194eee1013fd2acd1c0e0db2866cfd90fbc8a1415df4555c0efa55047ff60960cc9dc1fd3f9a4c709df257a76c0819e7de2ba141d8782cc7214bc6a7c70cc9e4d8d5d8a8677ea1c01479ee8074f113bcf9dc19d510299f8877b636f11d4db75b33eeec846468d0ec513da1225c44173e26a59b24fd2a200514bf2b763a795efb5e1e32840619e5878dc063cf621aeae4c22410c84717e4cbdc3d1b2ff5b4c9e1a815880d28340855d0454b9e082820839506775e6810ad1c2dd2e317c1de3c5e8896a9689c15688c3c17cb4d83e08120f25893ca28867e0868104e0dd66aa04165a814ab186f8873360ef834c831a83f09de03c8726f3f32646f3316699ee4aed49ba28690ff759ad0a5a376083d7283c4d019e2f782702965cea3e6d0fb388a8c1851b46b5e0905044a855a4271388a53be84ce7351c460139a7f26ce60cd9cad78997896a0d0045fb09f10f5ed8509f02a154c7a887d96d012e676f25937ead5315bdf85141bc0221aef0375f0e1e26a7f1f9c8f468d66de0ee6dbd894451f0e1f4f26725e04d2a7ce655dd11dfd7792620c9f68dc50d5673c0bab1ad6677396c48991a409eb8aa2a2d9246ad5841318e0872bd47899f3a0313283a2cd095501d2436f1a8b35824095829c099e1b4655c400dbd50e05350cb03235d9d553bc473f44b7bbc6429e60fc0ca99af9ab1f0b5774bf828ba9098e8e12cdc6c1c9bcaa268bc14a62c2433b78a627e8252b54c0855cf2ab846b61224320375879be251eb63573ef5ada986d311fe5b57db3cfe51b44bbf152255f383484a8738097c22ddd0d78bc131293c6dd4941cd41d972703c0f49158d372fc0fa2f2891b94ac743594113f22121651102f0e818d4616132bbe4eaa1bba70afa318e32ebe323d81ef056fe6ba08c9550e504b51f540a3b6ae032da89afacf57dba28444ede50f8bea97cde7c5389f8635ead6b280b297ef380d47d4dcb9a9e371bad7daa04d18298b8795a778aed2c12914645cba1418888a40090bda5bd4d1d29538701e73b9807a3849d022b27cf56ffbb6deab32602456eb43511901d0571d1c4602a3566b267581c43754c2511322dd90d4c06719588673e632745c4e45564651fb9a251120be7817d458840fc68639115e8cce2d2a92f4a4a0c80079de3b00a3d8ea0bc4af39a619a17f2d3b6f4c16b3d170c1d1a1747549009207cf292f686765eeac6d43a7c4fea6800e83325a05a3010dc08032a09ca615a605cd5a403e8df0a884db5e1b6a261b60e740008a0be5991a84401124512917809a0352751b280be3c7bcea3508717ee9cb5724f7f5b6698b605a4ac75b2f2aade2194754a342bd7f0e2070319b184c8bdb7947b4b29a54c29c90406d505ec0551405285245398c2149248a14715a840852a506148134e68c2094d34d1c4114d347144134750ea61e15ccc96ef2e9b5041d29d7591b92b26bb321eee7b7e1731dc18775f7a4e720dc610ee82dce55c80dcbb731930c1c4136e6e6e6e6e6e6e6e6e6e6e6e9860828923478e1c3972e4c89123478e1c39c204134c30c144432a7952146c9e94144d54d25a6525a594927ae933aad69f52b5d3537ab1374561f3a4a4484a29a5a452a46e68fff6a6a8ec862d4d3fd1365129a57b8e774a524a2929c7a092a42fa594d2744ae9cbeabbbe6449fa0aa9949224a524eb4f0a4eaa922c6b454d586badb5d65a6badb5d65a29873ca98a9aa6ec29a19456544a8a56d79357bb9392727a29a7977392d3aaa47c4a528e41634029ad28a5945694529c4e8723c92b5e9e54432a65f69448549224499224296320a99452522929a53fdf849a21ada34b184ed124e59ca669a295c2e6494991aca497d5444dd334d13a4d524a29a594524a29a59495be94a4a7e0a5974b43c9bf3aed184c537aeaa78be14b9f8d344bb520f99423d91e9d8f6f551d0e957e92f2294952495fbbb20826a7be41094f689e96122fb726a3d93106e8337d863f89505b6390a2efdb106af61782f8b9992908298410ba6e2cddc3894195fb39f0d3eef9fd8d1f426a4173fe9c3cdb0ece757f45a47e0a4cc26ccd026a17ce39e79c73eeeece9dbb73ce39e79c733a2637c5416d2c986d36eab34d8b9fb8edca2a40c40d2dd3ef999f05397b437b3b44104fdc068439b9b6e676381d4e2c315204c80fef4012244384f0e81d2de3c412468200693c33b261430a75834ae6f6ebb8cd3751c645478eeb0e0935a339ee123773391a3a7f2e3cf2503b403f8b5a6239da690e16d11cb79f1ab93fda98d9bd5b2d3615fff6dcdc4b179d0bf14db388ab1fb489fa4ddda0360acc96923dd1a74825e24d08f5a4b8215ec30bac1f8da4341e724386d0b8216dc91257e3422aa9ce091c483ae046396e7b97a4436e48bb254b1e8d4b695dc54103977d207c257a88ae3f9af868de8fe7a388112915316251271af2a897a72858510889f857466a266f4fd9919748632bd98b4fcd1098228436437f1643cea655da05a1d1d05e29a166b57241f562752f421ae31afa68a4b404d3d03f3f7397ba41f54cd2bc2538c62bc234b4d34c7689dbff82708c67e4c3d00d69333822ee199193cd3e6e5b1aa99450e1cbe877f595e14e0886d2c50e313448b008870f223dca11a42f0c1e0d465fd8307f37c4438e456b7abd5fa25efed1a0a0600a0add35552ea899bbfeaa277ac1a63d9a9bb96b8320b725719b123f94dc5925a1b2f52248a8dc44cd2835e2354ef9c69f1bf122b75f8a34d6cfd6c9516b61b6942235a3149bb3bfc8850f5fc807804cf7374931eed249c58f71d7b9fb4fee195f7efad90d1e0de1dcb720f84e3c0483de4b207ef88f2576783deba231cacf0826936965849a359bea7790d29bcdf419765b7b7f125a7127dbc9bfd6aef8d6f5d23eb68f1f69dc98e1281a376eb3cd7381dafb3f93363159f1662b4fa61d5b6ef5f172caa586c6aa7665f4b5f6c7af3a42ad5c925b3d76ab9fceb16bed2a70a395d18d35d6cdddfd5dd2b7ab3907b528b5c76efcd7ac2b660819ad8b087afe95534f1f5eef41f12128daf642f46d5688be415440e652213846def8b3d92ce5b668e13f1b29ef02e1502ffcea5e464b97bcb21e6ebc3217b7b16b6eceb51437e19982d964a88d0816105d221081e4b646040b8c6e7bd73e8729739b6b6e5f7edfdd5a2da1f2f76d35d7b5f66e6059d6fbf3d7c9759a0e46cba4fb2a1ad49aa6e29eb29994f39df4211eb98a6b1e15f1f9ead609698c723f1b49146e0efe96a85940b0f95b16524fca166a914834820ac3ff97ad1bd2a6eff01dee358d39d7e1284ee73bae3ed79f4d35d5545b2128e6cfe0fbbbc3b8f4cec653d87c3a1d159f14289c7bb78ea6cde09c8e510e93772a3e9f0285ede79bb91a4703990b0d46574e88875578f84f1aec8bfec995c1703d5608be2634f24effcebb4ee7f2249dce77ac27b9f11b67d3bc82dfe15c8ea43d49b3b0751ceb486ea250abdfbc8ce6ab5a4fd2583312fc2cb525d735207a4a940baaa35c50fbf2bb9f6de2aeb21bb7c78af715cf178c534b881a0ceb09d4071e5af1a97585cd7deff1f48de77198eff1d09c04b9ef578c3c54655ce5fd8a91cc8ad18ad17d7dd3dedc960d4e95b1fe357fb52d46b8cc7dfe6e455b8ce018cc6c6d3c1dd7b2f174cc7fdf42d2551e8bd458646b2cf4f9e39ccf97bb9df5cd9df0dd9c8de47b7e8b111c4334e5cfa9b6e8b83e2c5834f69cfd702ad6b261fdf4a2adce8f3a28bd71f9e324d916232268e69a51d2be29b9426ef3e06a28ffeeeeee4e3bebcd3c344b4d9bd3af98f85738adfd0ad0b7c6c2e29bd36cf6b9315c577953a0d61a83c271388da2fa24384616d32fe850d9179c90db2c2cc8aeb0b61f45f51c6f18ced76f5ffb396d41d5f34f12fce61186b7bafbd9bfc9f0892beaa5bd53940e6c3d0c1600f605cbc282ac8378ea3ae356c306d9ac2de1929826259c8ac3f1a640d69acdcda6e6340fae8663b41bd23829a67749d8cc09b9d363970a8e5ce96f30fe28bebd12ba70db67c1b517d7b56faed9cc868b77deaee6542826eb6a565c9d57e1f914170aebca62b8667858f12c869bad74be35510cdf4422d7388dc3dc507cf53ec4f0fff9b4aff8339367ea4f3cd577389d197f9783ebee709b99d88c93e850cfc3a35ca86a6513b835098e51b9b8edb3b64411cdc2d4b7deadaf5fd56a75a835c7834b8263b00c035aa8b51e1a8ce884dcf6cddf6dc68dbe58e63a5f38aef7d596b82e79cbc27577b7990d17b77e5555ceccffd22e5b10e7617eaa95537db5aa97e115b5ae31af7df1cde67dceed0d829f90f04a792c18a509d37fbab3dc5b7387ed8af10bad4b826970ce229993a556c7ae7a758d3133b7955ec1bce182dfa020f882e20d8dbd7727448cfcd66618227e3f2808be41c42b838897ea00728b0356db0cfcf05d305b9bc1dd49fd52f7b394daf38faf6d6527741cebf06f49eeb478e88163f04fb3b26a700c6e14f542df7d0fff9e871a3628e5d2784e7677e75e923cfe4b9eb58c76f7318befce3d3e3b5b836970df62dcb96fefbe3d0f3db024b5247c3bdf3ab606d3e0fc1d77ecde619bb1e49c532e930ed8bc292001cefaa91ba6733cf4c031dc7bd60c25cbc962b2189bb524b79fa5679b35969285694f9f9f7e33fdf6ceb7f1a53d502ff4a5efab3de79abe5ef3a9cb7a7955f46ab7d5e0188dfaccbd37778abecb1adfd367d2b7c9c2481007f7d4d6a8df5f8363b48c3e556d07053526c6a7af2eeb728d55ed299b35d18b3f0cb5d3bf06c7709b41c0d690323a95836731fe4eddf0dc9559755b8369e8b7fcdd871a2dc9f5e670a7c76c29520f9bbbe236257a00b9d369eb10023a86fc4cc261be7b962421e4bff7ec61cccf60b8ef7d6cf17a16c219eedfdea1860b29f8144e08dfc34020bebf458f0582a215101e92f3ab35d9ca328da374f8fb7ada4c465929a9ad31176833f764348d9ff31130e7b434102044fa9c2e1b1e8d6671cefd646d688c0602a2502504bca5d198fbe7a25f7fe1c5773a285df4e77d99f297f17aa5593297e4f2f7d7d052651b01d11fe3c49210101278cddd3f660bd55d8cf703d158ef506da1b6eb2defabe1c56d76d06a8b09ef39cdacbe6faa0a897b0d05e9294a87293da55eac6f6f249372913c269c9ff346926e2408dfb78e7337fc60c7c2cc7f0ffba49174bd0de174f3a1201f7ea49592a497a0f44f82947a792f3df82736fb5413f878a25edec3307ce9b96aa97d503acd949294d59b9bf794dc2cba1b8ee11eda265d5927b9597c6c1d929bc5c08f8ce4c68fe92bb185919ef34fca2639e9f9a575df374c43fc8e658932c99975d66941d4cff70fc75aef55ffa6b5de513ad06eb3a7f4110ad25bd5b7c891400a3a9564a38de5bd24bd6b2cf4df8581318fca01c24b51e96a0bd5a6c22f497c25e9254992e27bef41e91fcff8100a4cc3c49424afd65ac3f8df451be6dc361451982d45b23b7792c6182915638dfedcdd638c14151fbbff74afb250dbcd1cfd34182db32efa4cd1e7fa1e45c97f6957ca7795e155af76554edfba6e5c78c5f8ad6c0da641464f944a1efc8f217d5c21849036c64254774d345e997be79e9dad945458a899bcdca46952233a709b122798b9190c4f510e9f73d39d77779230a98cc9ddb6652028a82c729e9303155fda295a508c263ba8f7f8a0389c5953bc4c41e9cce677b2149f99f088334e4a3d6553c43a27caa53349f9d1664e52547c6a4629677439292945cfac3f296a3ef591f3114548c553f2af784953ca9fa69593c9abb83e7f7255931d5933b9d6d5ae87e3e15094c763419caf3fbd1db28382cc068b3ad45b9d87d40bf530d566fc28f9b95cabd437b11635ada7c3a9aa897a2961aaa72f9da41587b66691928c52caf8f23db2bada4f97d4a15ce2d75a6bfd583f7e8d2f51d4736a4559d357d78d4bd1093b728c51c6a453646136cbf42ea9354bff6ceaef2549a7cbd5f751fa4849708a4f3cf184134cdd109f78e2892ef428e20f207da3f1ba9f229d5c7a14f10798ffae1ed356801e8006fd8f9ad12fb5749b5993831a74958ec625dbf5c257bbcccd1d0616733367299767301d2371ce512eee25f95ecd7bf9f22e237121433af47648d285bc1f6f87871e907e3e6eb33fff7095241d7a4024fb7e3891c6faa3f5218df5d71f6eb32aad0b792e2448103cca9e0eaefbd12c401a0ed2bbab35461be32bce1c615a24a8520b51298158451a3aacac04e91ef7cce322a8d97f4a873eeec5e39146cc18beecb3cd00d1d6a2a4b3d921f89fdb190c6aadb5d83de8c0cd9d9ece807f6324f5f12588b23860d4b19e311f44515710540eac83fbe81607f84d8647908cd005b430cf7591252d25e33699ce4f6b349a257b1be6cf5f400ff3e79c4a6e3b42e6b624b729e123c79d4c49eb62acc1b95f7bf2dd956d7ee47894f021f3ea9d2cf31160c3e77e7c7e585c9e38e8cab6359626bd0df3694c8e3fdfd352269402fcc90e080fa1a0df3c74f213c57572792eafdebbcef7d5b779348bbfdfe88b3f5e2ffddc8e2fd744c0b681105cd827b8b0550fadb80a9411f64f7fcdf23d45b09849b90eff49f7b1b918c2ddce0761dd8eadefd5776cd60f3321744978463ed7421a094a64755ac69a405b6df691a92eda302fc265b78d94dbb5ea87ff43cb999a61f0573cc435ad02ff3f9f0aa1e581baf259cbf0b8f0330e12f23c84dd011dd94af36896aeef23a43612f0b31a6ed63234ae02217ccfb583abc0ef170df32ff28346c80e22adc88d5c5d2f1af3294916086ae64432e0719590818e0b9f9180df10b27706e443e8b6077ecebb71c72dd95ac87a5e698c6d0f4c49caf9a6840c805cc7419640a25920cc38c7850f81b0b2eae9e189570cdf8a6780f087eef07df090c7c2efa159fce10e1e82d2c6c66f540d801f485d98b992841fb9c9c8cf3c190dfde02a12aefc205f669fe9124bce29b601444cb378e8853b24524a293fe31c3e34269f2f07f4d09894ff5eb894527af1f257628a431b921f705cf936e4c7d863bea7e53fb6d22c71dad62a2c65ca952eae7cdee1847766c9d1629b394786524af9376e8cf23d7cd695c08f80ccf0b987efde83d26d3aa37cdf80fba094b65518e1755f0e15d410109e0f8b4fc15cd0c658baf8dfcb8b31ce0925694ead77c0329cd6c69c36e6409056113573d72585cfb9cd7c5fa960c55330174d25a0dd8fb4b9389d72911ec2a7969a149d3cd9dec125c3255aa3b581da6f65abb1c940f5161d12da6baf3d2b03354bb98f5d38e8eeb30f6cf0b94a0377718edd4ab33f619a168e2a29db28fdf473c59449b6a5bc22f2a3b45edc25addce24b56922fdb3f315bb3408b03b5a7e42da32505f075f0dd4187f04949511445bd44491fffc67d5745bdf98d72bf6cd03b815cf9327ca28d456b0335fb4c764500c2922bbd7cc94d1611357392fefbefb9a233822a7d4b2f491b998fd0be99cc9ed6dfb8f0cafe421fe073ff6b31c29d7970425b88f4b7e800cd9f2deaf76f7e6abb9b29bf45bdb4d0b4a85dfe161d5d512e1c29959e73753ef65ba23843de8e258a56c496e83ee74d019ebe85869a4c137d29b3961d93fca9c5088e71f234726c8b8e4bff64ba974c2553fa2c9f5a90a32a4ae9377f9c74484dbd9c3c5327fdb1d90745fc93b61d1454e9853e4cb419d378b2621421f05e594bd9898874cc2cbd2910a38f9b491f3351dadf42c331e877cb8e9b49316bd171b397246dd171a56ff223fd284d1f5f92b2d61839e6fbf9dd2459fa2d34959ed07868858d3ca1b972854d269d9a9a9a9a1acecccccccc4c75918b5c34801722e0194953777c7cf8305ac232353535353535333533333333333cc4c6880889e5aa9a9a9a9a1a6a6666666666e6f653021420043e34242ce346a3d16806fb9a0fd3f00ef6c13f3808ada9a9a9a9995ec09859912f6a86d8ec70231f30ecfff391486a1b7d6cea14dde8b991bb917323189493a8314070fbdf8e95ae2fba9b8d8dac36fb5c9bfd7d3617bb9235a4f28c4182960f346f6c48e56f1b03d0b621813b9d50260140980968b735e62e842dcaf85ae5db283ec27c406d3e6842c99d964a77c7185d62b42ced0d5176848e07ee4fb2ee9e491b5d0fa20d0c7364c9fa464fc63ff5e56499e5f359b7f80cc6cf2694cf2ad0671d9689432a9fad8031bfcdf9838704f04ee553c8f02b04305f86f92a327a3afab9527eba54fc7c8e88c6f2b129b6858d1665b2208b83062a3e037f009489360825756a06dfe925221a4b8af52d3e7318889fb90da07ce63c00facc8540c5678e04d79de0e326be376a9da69f2ceba50c774cbd408ac60dc6e162fdb3f8782a952952bac6ac9f103617110505e541afc3e751a6b7acf930d3d7f77cc4777af779eb678c1405e99b29f19372518c4e6b4e69413f9f291d3e3fcdcb4793cda648290628e532a9197c639a734e13c7345d2b8d4df6456bf341576b18cad51a162f21176b71b586cdcff5a261f309307fb2fe73bd686c7e0d8d4d39df7d1371389ceb009dd39c2f512ed3b4de5dcb6615b82957c637c5422162fed0e25b6bd1e2ad0bc5ae3466d9178db9e6e837258e9871998b226da459b227baeedd0f2d56c8889a25e59da731e95b5c323d54be5e317c3f9646cad5ee0f1e9229dcafb0fe51a162a7cfc50db3bec5e51a363f5e92366c3ee8aacdd3b0f92ad7a7a9cc9f939bb35c2c1a1dcd2245facd52cd20e16d9ad694f38539ea66645a303c532e00781b3806cabb4f408331bd7b1c1a4b0b158f0c1ee9f17c0b1429838a65bd4789e1a700fce84e3fad8a6118f66288e1b100b468f1d6a3705a0b394d930eff93b5acbf50527e461ecdd2e2dd471f1d5279f7d295a05cbee4477710246a9e8572f1fd9f19307117f8c169c0adc0044ec659c0c9a0bc94b5a27cf6b90108c0a3e07819191c4548cb7ef6463afc4f3b04cfb0fe7af727ff9c68162983e38d3ca51dca5bef7e76502c75446d71b912da6408a908e03389e561a1614e00f66da1a9b87716043432400dcca8d827ae7323a6a15da1cde03e73a3f9eeff4e18a0ee041cd27aea889abdb030d65f0fe34b1a732526b43d254b50be67d0588acf7a880c0524304a99f12e8afd01e59db4e255fc9ca8e8f3441f14fb464f0402bd7ca23782e1edf5e27adbf2c20b6ff496340b0ceffe39212f8bca0286cbe128a231f74ebd911c0d19f24600b04ee68dda1099c78525ee8d9e92b70407fff9f585ac82aeccadb8328f0ae764dcc5414faeece3de7309115f78c885ec70ec0c628b6e41427be75417a548455252d984eaedea1557f16f33786cca91ea98a8fddc2f98fc18f6ff7136a81e95bff9f5e03b296bbbcdc6caed7fecf2ba4889b2a738bed1ed88fce8eeeee607610014d366e08f4083d111e0d6ce3e16d0d80e3c94580734e6a26baab3a35af6d118cf80060f6a86d5829ab525cca3886699be35cbc441d3cf39ae83acc38766a136684cb354efdeebca9da2d48707f2dfa1dd1da695f7ce1feb900d4bdc248900ff902e305b4ae63f9cb68e890087ac95dbb025687c01967beaabe9ae1d748ab29ec6bacdd02fd0b8cd64ad9f7f2c6b352b0e2184f046d4389cb86e99478d7910ffe93d2ac3df5730ecfff3a93693143bee1aea06954bb9406bb1a0ba7f1285f03b0890c626db588905a8f1dd3315ab950bbfa270388901126270a185711f5bc6f0ca60a6a3b451314e245e1a8918ee208d79a3acd066f0a64fa8ee9f731312fed006bea0f83092bdc1bd12ca853ea1f2c307d2403a48b348db4a1a43a243bde4c5ca0a86fd03a9d5ff8d44b57f5ced2e105b7abb08f8d23a6351e7701dde3caebf6f3f98d131e3c5a38cb2139964e771d949cd23d7c3b26c8f2822d55fd42c6cf36ca443aeb988045c4a776f4b7ae4a3e9de1e7a4793dcd0e8508f3a8663746b79f7cfb53da28450658faefb0f18424089aa7b970f01be1c222a7f164351548a48c5e6f9d9a1be23a549fd549bc9682d931d1e1f52462ace3fb96ad6f2a2d24e4fc9e770389c20379e58e92ef5263b24db1aa332cf0f8ef4140a0ef529ac6706377e9d23a8e4edf6944109d50c0000144314000028100c07c4628148301a0807de3714000d90a6526c589ba84990c2904186180388010200000000006044a09220008333aea2f94acbad547f4ff44499a8e529fed56a0decfcebd4d08ee32ba130f7114556f7c90187d7fff8bfce2776aa0da5776c8ae4207ba29901b475cf72c003e223734a38768b89179cd95318b709d9a2b8ab48959e6109c21c3cda873e9afd574afe5513efdf477ce837cd43679948dcaf7f2fb8e96f28c82109e966c4477cec64c5e1f1498aa61366ad805b65c39de71a75bd05bc25cd0aebf7c49f988992139e79184b239b447e16e600a91c2655bcf96c0b117f4fd230aeb995778668ba59dc9fc0d6004acd381930d0f3d5e9aadea371f356e7ce638552f40a604167013c58ebe5abc512b0a06dcaf63768eefd8709c3bd3aba25e52fb51a8c778c09cd3fd29ce1dcb2fd40f25dafcd994d9a3ddad40dcc7cfd33b1133b51d89b7def6d9582d4dcb1cd46c7c9c62d64f4d538306d99fdf649259cf4e4c02f81fc4efd71c768716de75a13c71f660a3872dbc0e3b139465a5b3811a1999cb58c70333a0e460cc591b99c758321f4496ae8f35ff0cc7852882ddc990d5e5aa8b03f5239626ea33444c0175295d574c535a492fed009f8662e533eab30093767d93ece17cc44e9b9d62c9fdaca7122f6d94922263f6ad330da76d7c5b3189b364716cfdfb8ee452464748587cf858a61d84c1df372dc4e9a9cec6e4a5a55c2c9383e500e8fbd8fcf1dcee641e046916f7660c85feb51768e313a4c9d334560574f66f29db57bc5dbd93ef6ce4e2c43b76d42684fcb7874aec75891958827ba04d9f15d99957b0086473706485f392757699b1682c8b3e2b335752925fe49d49ce508272dfac60b44201cfffeb708d559fcdc8bd791cac53df876021690dda7945095f79c2daa06c5342fd91bce127d2f9cbc83e7d156b78bae157237832ea046df55306ed9dc2525fc91f724cbcaf3a219df19a46480f1c8f9c04f042c8b1892b3aacf74acce6fcefae7f721065b5cb38bdeb3d693964f1e8130d40b0cf5bc1b68da1eec3e5628502d1d480d4c4a40f8af8e792d7096c86d1826870d95931f398488481ec0d98965658fdf727fc3599c862fa253d271f63a8560933641a3a5d78b53ce0aced643d21f00909118d6411552f51397e36205ea688007a9f956f761d1bbbcbdb3807370700a033a9c65af41d4d93cec444dc6c079f4b0a0c12f12c84c0ec019726657398af543b525dab04a21a2abe735d99b1223209ed2b8f63fd57e282e974479bff2cb67abdb6a299eb9dd390faf08eea9be68812786aa3523edd1a26b7b7a5524739dfe73839dd698a8bee80877b9ed0787be0f3fda6a0984cf80c858be780a3419861551a8498942c0a194a069c8d63d75b84e43a8d14a98d93a46161f144367c0fe82f3fa5475a8804263f7443cc50e9b319399979b9eab71d4ff637f3a74365bd8b23adf0575a196ce0fea3d59f6385ae46a8bac70368a43da1d211dc2fe441cc4cda0304923fc6e4c9bf18986716481ccaf964393b76e0796aa8d9c4bac406c0ec574dd1a576724186b79c09015ccc9d2ca26fc470ff87f85f8acc50c948496fff54369a48e59a68948af58220c0122a949d44c245ee162d452d1955accff33ef3bba3021b3fb149f9305cca064c071764a4e610f31171ab353fc429d9e09bcc8403b33433511929a97782e45fd42fdfdc46674f275a81c5261ec74ee1c0a09a2916690dfc40d7879f4a0c7d8b46edfb322d21b66c3529634383e100119d9b0ec484fc3ec588470d107a277f65d7517065a904460e1317743044d157694b91c8f5f06ee9242ea89c3575ab0f1f3e013fbd9b08211c4439a723806f5ff14dfc2722110c8c42c6afc89a29828eba3d347a22d509884b29d070cdf64004758d2c6ea58880965840d620b89cf508471819e58b8111e0c4b7b1af1f916bda5a1d26134e7c65bc1d778d3a16a10bef8b060e5df9a708f88ff2def22be22e97bd66258f4fcb7063e6677fe5bc592f7744f73d5961ffb2901ed713ff5a6ab777df0f8b70e8f411ee803dfdc5bf39d66b8140a97ce6164704f5bba5200194b348e69017fd91adb3da77487d0754742b125ab69b3bd35510871768c087b022374301f0c23532758e33ae7b9a1bab8fb073814ef1a2d0843bfd08bd5250ed3d90f1e815a7291668867501e47d1a134e5bf13cb5281105454a309d9871eda1e846eec61b39f594f182d89aac7e17acc73341d5698848f8aba00cc8d56cabf61f55232247f3d057d5b37f160610d56db5c47621f52e84970d058a60fb5150ecadd2d88da39f6a02118380755003c7510591a3fd96c250b38fcaa2ee955ece958242e199413ce41f4e21d60119665f1c8c685a0cf4c7294bc22db377a8f12d48e9d74c267e8a17a7b926be822c039a8c8c8fdb7350c2a24d27a0e12eb4e7264273e3b8715df244c9f0d70e81a0075810e62bf5729cdb9b21af8c68d5ddcd46e97b51229ce095af77607123f5b6ad742f58f44129a42125ee4e450f56650a491e2de5e02b49352b550851df15d973983fa1b71340bd9fb8c91902c63fa59d5d2d00b8482390a397e9252f3446d41831816eac11d5d8580c40cbafaf2976a06090500ebebf81c05831b0fb3d67a5bbffcde00d91af3e07693f08513fa00643e5c8fd85e990bdaa727dc09acbd36a754a95c3210ae4fa9b3473845539786bb8f08a230fc88658b445851b809f7b416d2b8565484f51489ee287d4bf749c2389ddc4e70be05911dc1d481e2494589016fb17170a5431420c0efec4ba11828224d06cd1a4a80ae9ca42d158631a8048ce85ba99bcd449e06144c0cc5499814b8336b812cac9f2cfc10a0890793277695fce859b87773f8d9b06a849d4e2773a36998910136f1bae2c2faa082e5333e121ee427f912bec4f28301f517ace6ba8d706fd6b6c227e546a56322e557325a78151cf48be8764d836a51e1e5b078a9b28b1536e13b03e414ab8cb6088270e362bb1c2a144405875d43e06f339d51b1e33e97a180c8032b9c0156b30c7cbfd77c28286edf4784070fcabcf45050d75ce4d15437426f589d953ae497aaf2eb4b5089ed932808119eb35b0327b8919cd0aba8446cb040b8c8d04576e9d701fa1840255b0ce2ffbb21624ada601316430a56211049a220d6d80270e7c03de695cde02d7d7c93e01267a2a0e8e1e803a8950701db9b5f4f3d92551d18286247daabab38e3cc691923ca6f1ae9fabed2894f29f208910a26e3cbb2b2a1461ab799fc008a82e414fc4d275d3fc9136aa347e1c7741eef4afc5dcf1af5740f2e4f3114853034d94f6e67d201cd851e91f39282945e515ebd7f2a292874ddfe69454e9c6621181719f1deed4ae019e39472ec7446a72deb127c70e1cbbf88476ace1aff4593cf366cd17626d625372360411571e6ce1116645f9142c84da3b539b7d209079e4f04198c7d756cb4aba60889cc3f6f8ae3abc3f7e0503ad1ab770ed7327c8bf9f2b86d9a821d6c7c12ce95ad734830ee2a4e30d3a0bb8a0e2cfc2a9cea519246e896909ff2fe34fb219950fd8ae804de66d53ec1a29f77828171ee8c21f92c921686cc3dbf64334d4e70080a23eba66fd95a7fcdfd8194b248467c4fe1ddab8fe3ee320bbb9dcfb393d436ff4235477c0aa2288b7ad9364dc3bb17c49282c244a8efbbed788856866ebccc9badf5ec4c7c8107adb5bdf8826fdc1eb13acb0e49306231f71cd78eb9d480278f759a73e2c429749cf3637fbf788e8adaaef3533ac9a93e577d23f8eeb70d229170c3f056f05bc213d663fd1731be5d7e0a377dd1343c668a52e38ab7d178895c6207690e12a125608c1b40001dfff44a84773829006c33e4483b770c1eb87d8ed2928088c10024fc2a54423ace9da27a7b665a38d93d5ccefce6484379ed11d0fae8c4ebe870d98f5f26a6c12f953e9334b56ed5d5419dc815e1337c93b7ca92136174a056b45bbc6a3558b771e605b9e5f135eafa23f580c40033f2c123a192153a54cf74df39f882ab163e82060321bd304f204b75d3cb1dd353b9576b8735358db5590b31e0681c92f8176d7259ed40059055188ebfc6602505143264b9a615ec45c8283f7b055f82108b8b660f721d66ad8f03b760b02f30753de677f2d097bbc61cee64da0d7a0ec375e27c04e8df0ee288c6e45bc03703af940c2c2cfde45aee45a9b8a4be060aedbc097f293c6df35e92574eb2b0831320be476c6937141791fc79f709b8d3e0b6121b044e617fc5c84f1149db810fe0fa7e7cd3a80ce79441fc3e2008817532cdf61b46da524212dafc95c1ba86b9340f5e50e0cb16e3cb3c8c987b46a2780382a8968c3c061f9d40082622cec3aa8171f85be8c1fdc4303ea3fab41e8e593a02bc77e685f86b00452cee656933479839df34120b5aa485c9c1f6db9925367c5ce8ac6e80fe248ed494e046b0636199cf3e2793353fd7d6a246023f2fd70b12c39bf24304232b36bea1b26b38ebf7b9b34d20e38fb95cfad931908e16c5560a0a6f2a2300b8113cf4fa59d2cd35dac9a4d8d9ed82cae97a2a5f9ed2386576bf47436e2c10a6e5a1237e345962e78833ffbc46ee5278fa3cbc545cd07bcbb195c487dcd98b6d437c83125c4b588d6da3d61cafe77fd0476e89414ca952ba776ab3eccfb293b29b2227a281a19ad839243e2f1f161c882a56c8ead0cc02448070a21f71124dd51ccfff20007b37436d2d49215ffd2c63321e913ce22dd40f3a1b229409d35f60dad7b4ef0c71624ab7409de16716fafd8d00398442c01cc578b275c47c5d741555ba51cf73819a1ca22bec14b740fca7eaba5d19a98da3fc8a6ef7e02ab7aca94120ce46e276648a2b57eb620bcacb7cdc35107c14f9443d082455db9e53e1be6c15bfdf14a8576de1365548a30a44d76c9b0847d459686e9313c5c961da0b63ad4cd471bb55b265c7fe7f2cadec019c0bfc1201e35f6f7ed6ba94f0363d9490f33b9804a65d1d8e356837af725ff84212bfb793eb45aa3c6bca868e2fdf7b1f5734d55cd0c7ad63943ed1b9d908f394f66bb81bb9c1897b50463b1f2fdcad7dd2ebd6e71c3249dba905aa25d58220237edc3a111b2da593acb325a301276e4253d6c73d4ff86ea819e3f715414ce9f7360308635a3ba44ca1310df51ffc6bf56c0aa0ad36cb31eaf1788a4c935101f596b10f431c1fe9f0f8ce8043ba12b250923bbfecb39735ace0b8aec12513ff8caa2fa82c1e5209fbea3839b56123a21300129d403097200c720f2dcf2ee62c4a415c8e121950d9d445dd0d36a8a239d3f02ee654686ca93bb5fb8076905badf8612662a8c9075f219439ea0db6866dde4a9390fa5824a7f95676061f5a303eab01bee75bb91d5f0d988559238bd976caacf827b4815741ef04a5ff2c6b1397e4f448b7a8b5c8eefe083ec0f800d9646066037fc0f3246e7b71de2bb1d9c7db7219035a50db83e6f76a5e325da2cc5c206868a08a9a9632de0c75981d1468dc370f8fe5473abea38bb71ed3e581699fd0c2313358f9dea7bbffa187989363a21fa2cf03be630be41bbdb81f044206c91488375aa530847815ad956e4299b298ce03b7fb7bed9b4217b25f0e4a5da9b0d2c2158348e5a9b4611a4047009c461e392d82981f9abd0fa0ab83671e9e117f3af7f8f2f5f9588ecd2e7c4a30ecdf1db5ae1f602bf2ae594f5d14ad3ac437c4cbfe7a1785e2c832da9da2ca4422693c5fd0bdb98e81d55abce672e0ec7d031d57cf59b36750e54b13f04af5b0c716375097fb764eba298b9d365737a291b36ec210b270fbabbb24c12b82919075f484c9f78c84657b967ceb972e11a60225df015d119ebb5638ef9e31e03108c4786159241f47299f1abe2263d7cd8dc2208c0a8d6f5c0c6b0e2ad217b89cae7069a8a3b6a369d5e0eb48b23c26db67ea92825fe0c704d181fb16fb32f4e0a6de359f409888d8087036d99d0c7f4804b6e35a62073332266613f0ea14ed2a341ed6fdb7f715e127b8596b26e7b15ef6822a60b9feba9e7c03498ba92f3c8cc8f699ee270a1b3f41d7c43fc9c13a6d2124d30771394bb7f89f6dbb8b029c6704b5f10f89f8acb56fcfbb25f45302e0188e153259fd42b4417522476332485cfc0bcb06888f0f7a2dae1fd91c4a231d20383ece020402a82d69a5c25a93f66c0da1e23e01d454b34e3f8117d70443e6685309bc14a634f93bc2959d7ce2974c49e696fa7e3a4b2a1985c8f15dd5d39451307e7c5158359f7c188d6e8237cd15e9e05af38eaad5366862e71e78c8a042e06795a8f2eb75933658d53734131cda3c2a03d61567081db858fcebe7fb405050b02d4b1937118ce927003e9721e00ef4887bc87e529f2efcb93f8079ad5218f377a40fb4371df859999d87ee47e078acc9cb78df83321f45990e2b7e03e0791426f8c0180996b68f615c773ac3c545c334ea3060520a5be11d0686544086ec366fe5bb7ec88d5a1f76d171d09b4fb658d26b11caf41384dea44da68c6af142ea9d7300d062b4bae11649b80f3dd1439bf419e91f361eda1cfdd58c0c68e580e4dd507d1170794f3fc1bd5ee0f86501cd20c4dae49d5a32fb29905259996f6857ae0672df5c3d593a46c522faf89f02794bb72a6854d17a36717af0922da81d231ba8e82c83e84b959eb166a7108c40d1ca5b98dc36a31d8a94833fe32ccc212e1780e8d927b4fb13546603231ac98d456a3a5c78333aa8bb8412f4f1cff89c7459af1d1222099ededf7793c2129de7a43452461772e9acd8a810bac90861c51d08c82a648659f71382bbd5f8e141225687a93466daa4cddea301e82c38ea97d8e3f9a36121b5a169530be1e019d3fd0416d5861c5e5ecdfa7f084377c1cc5feef0c57e92835b55c092f06906b94d2092e2833b38e99aa8c6e2a2598210a2ad31ddbfec22b6b96276ff4ba2bfa7fee117953b0dcdf913d87348348545ac35018c7bc685497a33f95bfd0ab8a457ca292eaf4531afb01c059f06c2d58b5856c76a2d2ce6c7142d9b5d3376fd30cd98c3784d6f6b5d3386bfabcc6f02b4177735bf4a6a9859609aafde6117bb5c70b2fdab0b0324e5acd0e7aaa4f5528686014b6ca6e6eb62227f22133502d261087ee017e69a04c11ab536f0aebf5b9508a34d384d6ad08e94e322f0a60e16f4e3a6044dac26dc1ad05c1221904ab40cb21240c467213e8f7f6fb9658e543d9f790137f600197d9a4fb279df5985a2bd42dc21492c8ea9bdf61e01f246ba2413040ee2112ccef879af48dc2cdaee23dac4b3841cee3a14e28a940f67e6ee83bd9dee9e8f3522b2de17d1f807813a321ea6530b8cb4c54461970e8beb8c8d9b38a92dfd582a523a479864cf43cabd6fa6ec8f440c286175cebb93f42cbaeca2857639b71cd8df1c78e32292340045512328bf148db619d754c300488c067a54299493583be16e161a87154a4fdc47a42d1f7e216714444e50f0afd8be398efe21b1c29f12d99372eac04e35ae653d7e560f41e6f2cd51b8d650f78f1c34864fe3ef7fb60fc12ac4143648c0441c078bd703d0c371c877f256f7b7c5b1f94c3687fa5a3a1736a12e13d6e4d558fe213a791a12da86feca102b43d33047b02814a5d18d018b4eef192ae5b56371608e475086af08cd27a53b36b8a06b18e5189172328967faea839b3ab10ad5ef150a978d2154af5c8fecfd4283f85a4d9acb8db7c21fab6fd1e8fb87dc117d3aefe3e8b29099d1f79cd070235fbe2ed2b771c94e0943fb5644d5f6dce96aea79662e11013cbb5d50c2d36f6fa2a84c6a1304f3164b9f099e57b6e583474cae40c465f561ae2830ac1708bea0b4eb12bb57375cf4f605d8533bfff7c23e10327185a603b31550ce49a90c032c01755e01e69de002f8dbaca4c881651e6b130d8ee4be4ea74348d32b2e884a2b1d6e0b0f91c3c443c7fe5e2856335a1ac0915dbf5a8b88e53c02316603a06362929b1c22d917428d6d91ca49c14c050b4141c8b531790185c4dd5758e5638b98a3365553bb422fe13695e37aaeb6e5befb2438562f6e79c7569b80c5f670d8f2f108e129718cb94c7e32f1f6e58568dc44a7f1c1b08f19753f4fc6f1fdca0e21431eeef26ded408019cb2d67884b0e9c01d1e1d7af1e8c6e713ebb54e085fb801227462e53d2d68e2448850893a6b7437cbdce687bb4544eeaaadd63ff175fd6ac280f12b25f6b12878fcda87cdbd4a2503dc4616c02b8548bb629439e7452bd590a177809e7a9d4fb4425b1eae7c655dff456df28a9bc89d4485438d8141f1ad7b9c4015b1bcbf6e461a8bdfae55289a601d9e8878c802bc464bd72d960d172d199dcac3438d4517fe163c01c11670517664ced1922301ae429742e5ff3eb6ee854c0e17e45b7d469d0ca9134bbe5ac9a60ad000546f8c4c0edf3953c15c4564d6103fef41533052b6ba4a192c32b2a0b41068d05790f0e27247bfaa83d39ebde71ac4b34a23cca500d08ad3d9c5d8c07792ac03ba6f3562e4ffe469e0853e4ea28faed4127359f7faa9cd4d06446a23d10b2749ef80d27c38b0dde0f6e36a0a2a4542f5cc29535ae2e24759788d2abb27a80975fad80679edfaf563dddcc20a8306314738d24b3299fa1338a6d6ad7a94c71757d7511d9954387c5bd880a34019311af782204d5130b2423710bc712339178de0f5f801183cb4c95a428e5378e265ccc1243d7a60874f9b85eed7caba761cfed13625d035f718cbdd9bf5d743fb3d87b3aba82771638ac825b34c4706b9d114700d8de090bf5f2cb8615b355217f48501a9ec96aad68f4c6e7a9aeaede0311e973c33682638102436816f34ed021d10d99e199b3b8f857005f8730cb4a1bae7c5fd5926c76bbd32f9e7650a706f006afd6f04d6a4d8fd0cc90806b2ffbdc23a8e695854b2cc7624d3ba20d6ee1132d5444f13f48b0bb35b5809267fe16e6c75f13da72e40bb0f17d53b8d0b2cfca53b07939c0b07e36ad1560d93800873a40807a64a50b6df3996657f03a3857fe5650528b71938383f7372ae6b4c2f29f8c5242401ab18515b95306f6975e50071ebd21e2a3c207d4afea62eecac2a2086cb6fe9fdb880e47a168118cb103d59950fe1459033862716a114a7a372166c91d077c97e220522082013f9194c34c663729bee629b285367d6e98b85e0ca0c26a40ec88178769ab1f0670d68612d923d6a8ecc747c500bd75b304953b8ab20a1a9c73711e4e0625280cce579b9614e5cabbf3bcdefdc1b4c8d784cb63b1eefd81ee7766fffed751845e7cbef3cb09bccc59a01f0da25274f0abe3f33ee881e0f5610d9acfe085d4f772dfc73699a2ca5f0285b07c8775c9d576e459cc8663d19d598f6f8dfeea4f2ea3a2f9fcb8b95959a59ed29ebded56af758d348c2303f566a1348c42733f113ac0f8949edc25a5d50b7acc7b5cb6a373fbcc6c419b825c12c9dfa637f2d12b61547ca83ef82d898f4c0f5faa02697a175d6c9850db273246fa6afaa01186b84339e49b0ee58457a5167f0df84325614f3dccb78492fded8cbc5462cafa84db2f86791b2a5eec6898f391226b203d9ffd06989efcf9327de4f9a8d94a8c626477c777219c94a1df55dd5ba3bea8601088ffb4f89b503c7b40866595d4cf201e950a046f47574e1b7d11c287ffa9e6a8009688309ec110bf4ae95a9e5d93962e5ad7d08878a3718bf3b0d7775f5718ac0abebb17033805f57a2f31bca8115a9097dffe510b0502142fd3e6825ef669737908ba8e2528aafaa607f51a4014a71be4ae6c2d5eaedaff2ff39bfc21a5e06ace6b4547bcefa83df1f2b098c6e6defd4207eddd1de3469165f46ba5bc47f9e5b0fd98c87507c90a751ad9805bcc8f7abb078c81c758545488b62f0d5b00d86f4d819391af48cba1bd1b11e168e3bcf5bb41e1ed9997be9eebbc940d894f18b4b295c282ac2fccea65f045f817d834bcbe2b674aa827744945b14483459c8477a8c27e96acef827dd0fbb7f98caf3f6667ef366bc0a3f3fee916d744a1b2f49c37519ca5a3cef8690f18453f31912a6df75acb90c0a4b43d2edce5687e61c9590857b12c1b42029e0351b8c7baad8ba4e20e3e06524c9cb069e9dd0ae5b99f83fd5386b0909ded53ef44a2aef64afa8e27e2b73e4f87c0a89657932252a6069c7d17c1026490952628a67276015175a75a01311ec3c866a25e0b7dafccda38cbd6bc2fa09ecdd8d6f1e13277719971b65620186ec586dd064600dffeea7edef10e3dc017c96e8507e11cc47080063a6099005a960719acfa34cd73c10dc32257b76b0dd46bd76e46b2fb7d31d4930e0221e4ee63ba21366502f64a607d27dfdc556397ecd8acbaca96a82568d04d38fba0322c6d79e4a7d300d579ea8f816f7e1886b6d99e7c51453bbfdf27f334bb6ac3fc3aab8237bf3ddbd9c49f252a88e905abda70cd4ec0378c8cb48c0b74589423e7671a88d956d198ec3571312a7b372217af74cbded46ce0446a8407ea66792305b73eb2aa87ab6dd5d2e217b76d1be808ff1e31ef6fa226868c52f1e1459bd106cba4d8b02948faab8cf7d9d58faca65cbc657e5123ac235958b24717f6573d61c723e9e65b8e5c686c70f227511aa42876a570183af588b7e5135505307c55990e36bd5ee8f049a07b544ee483d1c1d682be80466dd1d71b65e70f241ea4e95f0dea3613f2619b099362f4e3772e44b7be2231e29acb961d0d7bbd604fe7cbf5be06a72e7ec19a25a5b47ac060a7c0ddceef421b2227cff13a0a1b3fd2df88a0c005e116127c2ce6e2294e7568078fae3a38469a0e9a44783cc72ca8f89b3118840f34f88c60206810ea7464b5bf33b30e4f385bfc29795371cd53d0da598a2192f09bc9dec88e8a552dbdc3daa92c148e6053192e4ea6012a5ef123537db3b369848f473ad33ea81dd88733f120c4ab5019f9e3b04ee79059c05bdc390df67bd312d01b55f95aa40303551c171e365662f05dc7b9e63e92b5ac72fd44cbd325881b6c930ad2735e912302b199b1c67b5d789f107bc58c2d98e225fe13f27f1403570e847fd2a98f94f95f8521a60dff09fcaf022303ad8895e06866f0e19b2f12a4fd599534b93fd959ed383d73123d213c29e2455f4a3ae42064a8261c121d38f00f6ae6b58ee984903c9654db5bd72484a70a38b06dcb7bdbb084986bc41d416c1e96cfe44719f25c2db3f2ced7fefcff45744060b7c66d7ced31ecbdebe31839d51a4178d8ceda6a3702192031a24cb3b7b4007ded0edd440d0c2658cd7e4203c2c35f78fef68dbeb0172cd19167868d83b7d6e8481b0790967d9a312a9fb333cfe5fab1b5aeedfe06479cffada79adb5f02e23232e26f033de5f2db7aefecb7fd45731fb222e995a3dd1a2de22df0078f80c50bf4b0dff44d237fb774fd082cf044817e4d989a2a4e587c154f38d419c585b2acae3756be6846dd51413a680608adeb827f044bc399a5dcae5047da21c4daf1bb5d9030cbd599a60f6cea557f909f082061c4357ae1f7fcb42d7ef0c88b931d06484a74d8c655e183882e6d79d9a4c86561e0adeebb721ce4b6c37a79cab021e293e50b8ea3042e4420303f9241fa63cd7987cdab28ffa233c834cd32369c70bf60ad01004329862e621090daf40dfaad21948487e86be43f35f5d49a000de4cf16a7b6c7dba329f4b9f2cc6b725b6c9b391ffd3c1da29e5b412148c7eec6fc74e282303c1aaf7307740a7a8cb68965b4a1ff19029582560d6d6cf7e2657d8a79a922ad763316ebfb7eaf4bbedf10bd18d1a4ddbca6871682f8e39ddc37e8d30f11fb14e4dbaa1584698a19c62bb4128232b283b83251e0b4daa6848a127d578e5fcda00546ed9ac08520f7b51284b4142789b4f29b8082a038beeab4b908a2346df0a9e21fdc5cf12db33a81365942d5fa61238b41bae7fd52173e816cdd3b004af3c906ae48afee222f180fc0d815a5363fab28423b76d4971d08cf08b0f243785e063d3d5cc3b0b410aa208f14b9f392d1237b52b86bd61b10a20bf5f81207bc17637a880e25f6137fbb7541948354b5f03ec127347026478b57f7057d078d39f9743860cb3fdd92233325caa7d639b5bacc3da08cf90d288deb817df9cabfa5a6aba4fa6111eeea48eea3ae1b14087d400ec764323fdb7f4baf66f8d5339a9f15d6d009677a160116db8ef2856896d775170bad5952f31fb620608f76c375ad4f271062e67d4737512bb93d6a360f939cf16d6d876a855a3f9fb67d7464b2ac5255375e50f56c120370dae06c24b66b6d3b75d64a8128e0e377f7b0335f0228b73a003f4d7214299880654283f74bf7721e245f5fc0154f30ed1ed96096523ef37417c30542cc6d84fb60d8e7ef44da5ec10b9781788ae24e20e9edc73a444d9cc9acd1952fde4275cc3d08f25243a2b29188e618ddd84d9b0f47b899538cc608f4547e40803e1e5933fa5a78602acba840298e9f2abd216c928c9575f106b5bf9042bf7c6e6975508e900f584df383958321f7048269f8de70db7ec0473c8af8a662a1f144393ebce27468db8df66e4b4c178232bb83e090579cb8ae84326c5663237c892205a1f784836d9a273a1aa62c64458c2222b403f4fe6dc8fe30c811ec06c3d69dff9a6f6949e56ba06412dfa664402e633669563e20ba3dea362ccc032fb2cd5e6f1bddfecb7b8f885cfc136b8c85f95126843e4ac5b0ed00ba1ed10e5060676879093f3ee57c21bb0064e0d2aec3553c29b11800053dbba2b2e1466e05d09ad1907a9d588557497635df944a4192ac3479f562ef44a10ec00b345f3a7a1b4bacef885dbc37294038e5ec50ebfbadcbaecd7841ad93d4d4e0fe5f0a684a240e169f78049228d975c79eb201a5f9ccea94a1f21fb8989e50e45549518cb1b0d239e7672553227f6ba8ae9efe60d02eba0708f203b75612c3a19220b9856b3b050b500ee1a67c9e52487d021bb5037c00918227359e94e1de64296b7d686079b5d02ae4168a98644676602b1387d042d9aaeaf9d154cbefb844a28ded0d6ec87ea6fc97bb26255768a627beea34b87637b2cf520863b572fe666e65d386c5901800dd272b6d0cf973e25b5c5810689ac139b69c02bd5ab7251672bd001589dc8e0b9452f894121024cdfdd8714f13928713ebed2daf6fdd63f7c74113e2cf691db9614f9a192ca7ee7a93f7c704032ddaf8b5646cc376a33d1570505e58a69c6f3448581105ba07671795f0c0ba88ccaf92d73b46d40de0398456f417aabf8a059125ffe43647e777d10b575161aaf115570812065abb097984e8575d2fc9bc88da0ab29d295f6f00a8c5c96957e5f0e537b9814b6ca9ec80ae32d7a6d33b8f5421ee7d427e578977bc7b5c29881ec4812d6239c4eaaa4b31ef1879fffceeeed8de3e22cef7a83ac44f2fe9d1c2613a73e913e6f712b90364927b50edd0c43118733b2a58a6410e9bb9246a9f40904f6e49834eeed20741ee5e767a127449a074f53c1494655888150ed1777e0a32c7f4153820dde82daccea473434a02a690f0696ec45fcc0e098028dc707d32b30ba451060582b8c2eb1203d27172096ef7247f1c33bb1ed4c22cb7c373be236ae689effffb1309f8270da6df34661e7589f3ae96a8f0506d7a9686296deccaf34f1cea8be34550b53b74ddce0cd4b889b80313a288fa07cceccaa06e40f3988f5f2015163e75f7e67ec52b8eb608f94abf7a833eb2210ee3be02b5973224128ecfcad242fc4e51dc8938cfd2452b8b520f9ea6b508c5c0da7508a13a925c49f52495b9234a601a9ab0e06859e9e96706b7d758879547cfdc34393b8248b2e6eadaa77840f26470cc8edcf04ea3cc70b1dd4067d8fd7bb8b5cf466de41bcfa51a5c97ebad6cac3bcef04c1bd304933494d0841be61739a4476534878d29a2a1aa926dbba8bf782dcebf733d956fbcdf7242da1274ea9b7e6fa91fb88285c35e0517b854776d368902972a0cce6ffeaecfa3e97e7b8c7ced054856838bcaeae9cc766f831a0fe26ff4641f1a67e026ca72ea105f0d444dbe0d2779ade275214259d4a39c697862e308fe01306a87d12754a97e25e2b91e53c6b71c02a0c5b4cc6a2068a790a18257721c57565985402041f93fced48bb3d4fd6adc2688aa1646249407eb40c2b554b3a33b73059013cd48056108645f81f8a04e8fc6bf347e5d9de1b8483a8503db140c7798d4fb3240becef75083bf03f822d457862492ec0875acb1faa4805ca47e5dc7057a66ace656688a1ade4cf66e044938f72b24a8990892045de82883f916c3fea91a314d1355ea40df85cc8c9144f3986699df62d62d8e049a01471b896b23f822d399a825c047248e7d64e32a80583f2e0b201c4417fcf55ca63009b33c53b9ba143cce9cc1a8b4bf4ec65493554d02bf64917003ef094906f06b3b8a1af1106fed03e38a16fd99ee99baaefc6bd1c451ac2930bbe99bf9cd18c4317eb1d3e20838f388287fde4d9a807dac6d1bd2e480209337e0096733bc9d09cceb390c9e240efd0bc51ed52e4262dface06832b626000d3041b91af93634f2f3088e429ba9ee15f2459b300965abe42419944d354ff5f51089caa5c5c26e439283a9a9a8186030fd9b2e15fd81feea32ae3da9e6b934f8dea80798c073ceaab5fe31e8b5e0e4b469cbb7527aa8d613d0bb943875231bc19b41ec771e3d8ceeb3abfabb83b166547a007ff400d982381f63e6c83bb2ad88ce3bdfc39d5b17b31e21b2c72b1e227e381b85cab8929895209853f3ddb28d9b2a6ebf2c858aca5308a9eb1f289e5ea09d0865ad9881ac204f42eae996d05dda6e89919136a935de6a83416f4d232ea39536cc0dc0794ad58090e6d14e21cb9ecba335956df9c8882434982679742d3a1cdea3a12998231094c763591c3562ccd8d38fed70de5c9fd9ec93c0fa3ac1ab3d260308f0c2c84884d660c48d2e600c2966b208f2ad9479b794791813f901a9e3c616be8ad803ea209dd994c619186c202a638de5c2397676d19214b70610a28001bb275358928000101055eadd62744858d1307ebcf821684fe728897e88dc76f61195f703613df60a2ffc45fb304fe06a32daa5a5feea048ba04e6b58281b214792cd9c7902113556f22c9526e6e3ec608affbf18185660d572328ec3c7462bd75c2a895994a95ed1ca9063e956dcfdb8512886a318054662a72e3dcd1fb468dfa19bf89a050abf6063573ed7fe4b1812e59a647b84456d09a9cb6c9e40ff3bf7fe097d9143ccf74637aee346733667c043c93af629f4ec859554688999f3f7c4628bc12761a12fd7bb6dacd062ae560f4bcccc9523f2a6943ec0cae60762e7bb8092975fb9a309017c5b26ee67c6c7768b68d2609cf37e83290a8382e1403a807198244d622e5062fbf32f8c218d9c163f233b56622aa147a20f31bc8bf5b7bded8ae26bb9680d55ec7e3d19a280d2183b130604a68aca8b9e62cddb9db3371efa1a5e848a11f74607cbe9f6bb5a9c8b141a7bacdb627ba1f81fa3cc8108c4c697f71565c738819baea5dedbd338dc5aade56838b1247ee26344c602e3bdf23eac7fd49649fd838e38e8258da39a443b6149b1024845f4153766bed1adc571950ac65928ed297fdd59c3807587d43406509e0ef3b8852bddd38fc8ea59220e6a11dcbc18cc93bdde5640ab3e7ddd32ccc33327ea85a19c57efdd8e1075e1b54847a69ae386d94350f71f2cf2c2451d6cc92d46448f89d3787f918196e675013afa8e7e556158928185901ea0a98ee499ada1749ae87e3afe45b174810175cad184fbd781b68d4efaac73610b9e27d7be25a23b2d6e1ec69aee963625d6ba6d4e106d25be4af5aa05c3b07a20bcb5ceaa4d9271b4c6bddfbf9752a040193a8d550064a998e974f509279694d3fe21fde91101d29810f4c82cbca55cc5748eee7301051a8f0aed67eae60f0d2d40e9366b3ce64659dc69f2cc65b0f5bff2ad820c9571742f52b484630532623eac135cd57dc29108333f9d036a79810c8f59488190eb1b63af671b41222fd8bd929c8be5800bff51dbf202df1bdc6d66aaba86a759b52ca0284301acd3ac7b88b5b9c94748579403619d2ba93c9c4ad42247b6a05f51b926f56db528786cf21b6ab778d8e08fc8aea3c3426e1694a812f598a76589e8e2711e5f9929ba459a5aa03e88e38dccfef898def651853c3ee284f9b41f1e524ccdb0aed98488ecfb0149670ebda1b8039059811f32bad5e5d88488d53a36217aeec0e04ea5358ffc3f4203c524397a0ee70a6d7418ed97fa32c4bd5c4f8bcb1065f2b28340b54e661197248591a328f8ad542e1a6b702d6746e29d1f78f3fa669022ab649538980a29c26178fb828eeec06490a3bd03d1a5cd79e97a5d1b5571808a8eac3defe110d4eeefad4c429ba13b110488de7b8d0959036028217e5975c89b184e195e7fac319c455e61ebc81971bd8783ed0f871366f890a8157fbfea46634e5e4d709acafe5a7ec60ac7e80315497f9b7dd806cc29719668cc252816192e71c491db86f48784e37c4a879b45f62834dcc5524a26efc81f4715ffdf800e1b77a37e1bee2c26682296aedb627826630256aa056b5d6f266f3ba86058ae45441f81287486db413fba83ec60bcca68bd783ffd69bb842d9c195ba312ad09c6c7c482da9bef7ede2dc76542a489c95c77e80877ab695781097c2508238808ff3330c23870aa69c6153a6e09837bdd801f39ed5a7d4a9666d67533bdc47b5547a09fb0232ae3b0862a45432943cc51ac32c5c660e264f4f1d6b3a74319b5e55867816288eb4c240f9ca41d700f0a65442e4a00d718343177145dfddb0f96d8e151f1c0d3ae99d2252c3dbd1af6e2ef1d456505f7ba5f716d53ce34934d7d02c456555f651504eddd1870a237134580b2aed0755a9255b73490115527739f0e2e535fb73a0c6ba2bbaf03f798b6937ad79a9bfa4dd5e8718f94c0430657b3fc73baf1403f615ff185edfe585cf39c29514d896c976aee982afb342913621f71ff95ab809ca9eda0abda80df6642eb89fef8c8b2ab435fe708a650fd0c38d906b1456e73646ca69be64d033cb589364431e21506c7cc6f2d34d3ab183109ed79c13dc5b06dbd79dfb5ef9cfdc2077f14da6262fe44cb33680a3983d46d5c7cf92013379c77a3b8fb37244e2e07da6b6512186cd881c1d4ce80d1454670e990f979545d60462ac9b273fed8d95a5686c94c886bb20df217de25b982f6ae2f0c2046a061b1a6dd7a161cf4fe5ab9a9c66909a5a2bb5f9d0f16c541cb5be04997f3da01291cf965a22f6adc4f3be1e175c322b456234f3affe393ad608cebea25cdfc983067e35d8f7038c101997c756c87d22954405c964c5ac2d3159a44ad191d710c1dd623bfb07eb27027de11fe71ad83262db0a3ad9824c5c53effe4bc153a46b5120e2a018da8d9a4bb8adb3907027152903fc10f9b1189501444a095de187e49e6a129bd0c407bf31a5654ecdaf5b1be52a72dff7c921ebf07ed9ebada48ba1ac7ced5bed395afab4bc0e48ecb69a152e231d22f17f8e29b79f2138cf94a62d7dbb562fffba833d5c6de153add8bc04e1e37ed1f6da2e082318f11aa88caeb159ac0449aa945d10628045bdc2f5945f47a873fc02a2b8e7082c3a93f23fd6c53f5493100185bfa149b51c55163fbfd428bd65baa8145b45f041d055781bcbe7f930ef768c558dd3659a3112a3be8655c2d15cfce50f14ac4369fab91df89993e3b9b83e398554832b030760ee8b4f0385fba261fceaea6e061e8b1cb4af290507743dbeea93ad5e7317f96b142c417f12ace44882c1f1026e0bcba274bad684e363ab510c74dccf7c56c96c91a47a5d47b7bdf62ef931ec8733f7c0b383c6416db4d916d8f22f57facd8d428152bd1c56a14e928ac1b9bdb6b6ba8c442377597eba6281ef97d31aac37d30dd1aac7cf1ae4bfa1ca263ebe30dd525c96de2e36fdfa38dda2c09510356d33ad73eec954a73b742de8684304b8afb7528f8a6a47a7cc16a279e316814ef483a6ca71499f0c138009fa231b67161b7d00cfa2f28aeb9eade3aaf7975e0a724e8b15b3c7ce1051f85918b99ac9c537313ae5912d1d7df49b25114b4491ba21dee073e0572a5a47416f785e7b21c34e248fd2aec6212debeaad00e392574c38271c75a196cf6cac05e8e02403173e2a9d00c93d67932c9d3965592c390134d890e81dbf931d2e683e979924612af2cf68106c3d583583e7258a4209e407f6e6d5e4e82e76024da5a55fb458812fd5d598a11dcd000d7ca3e975709afd8023f4c7525d65355626e1d93eb89a5c925dd75fe37b165007c4f66989db2e3a21c22521353259d6a4b1b4d26ce0f2664f7e61947fcb6bdf04880b5d5f5336f89bd8be90ba1a10b89f3b4aafc50f516e7be1d8d183101115526f8ba0816f893bbda43764f1900919e05f88fe55a6a7e6f64fc10f3e8a430b778843defd05b961bf42c9891e653fd530e0806bc67ac2e94eb1d5d228e9c5dbbb48d99e8832ece8b4a6b37081738252f961a8f0e237d44d4efc503ba3dc20c19ee776dc1664502a075c809c1973fe50b0d308cfd9f9bbfc27b8f4c6784528d5bbebe37963e4ccce56611ebed8392baf7318d84a68cae6e6c440368aec05db98b2e3f44b85e6bf4841f3bcfdf88e6066443c44347b21fd83d0d92a30ee0fdb9043b4051228d1972acd851831eed5e32f8f90e650cffdf34b0f23bd7c4365c22b4813e26ad4ad46793bf73314c79171b01468cfccbbbdc93b9527a8658b5055e27852f905338a233fc841f3252cbbbf054e294f9d02e5d2ad9a466c50cab5d7733ae3d230bcfe55344f39573f6986ae0c7ef52b3d59941cf94447164de77440c520256e24eaf68226b13f1141cc4e1ca2149a213b31db19f999cc201696281e5cd20dc093de36f2e77cce30a983f0e17838c24c508c180d1b9594ed12632f03a46433353411e32b80338512c209c0cc4a7c55367194659eac8ab6565d47ed6edd0a7fe10cf8831969e152680c0d4dd01ec1b453889f6cbb9771ed37b0e01711364ada1613e940eeeab14c9306a0340ed793f031fb5fdbc2bd9cab2dcc6a9968f28c79b40a5938fa5f238a8ca14db55ad90dc8d74e9bdc3edb8a93265b0f73e58310a8bbb2e4bdd66e9c97105a3a94b968e221efd33715c00ce27e272e5333779bb2a01255f2468a64399f10a8672c6a272078cbde8266d04f081d7274e6f5b2b52f725f060bcb9f249f723d05bc84d8f49a3f8c888db691a457144ab4cf80bd33ab2058d0353164ce200ea01d8c1f77af6f75f01ec98e8f794b78dc00ea1c4edd8b488214eea50f8c05023283bb12277612157ee8bdd53eb76afbe5a36dfb2f320876288a455b8c76205876aa861eb1b2d26fa7612b91f2311380bc30701b9d081e7394a3d59ed9c8fc063ceba7a116d0a5797acbc883b16881f216bf80aa5ea080d987d4a05bca301d5ad4453485dd442e82dfc307c0a08a0c3a02eb86a01bc422b659aa78db1f8369dd27ac1aa107f96013a91c3f6a06032e5f3a401cdbd214cf77e9355303d45af3c03ae1b640f4b1b33f8edc0ec9bc59741a72128ecefd8f3bd0bba5434d754d8d5249e867d38304cd9b8a4e7b0afed5654fdd69ad66d00f2d5418fd1e805c2c341fde95211d717907004441d90611a1b730a2f8e2dc9b7c90833cf3299fcf24f8d5e30937918d56082840a37b5dbc1c744638ca5ea670af9b61798e735bac9082b4ea41471fc2a0ddc569f0c8aacf4ee0d9b075a7afd6cfd089ffdb8c8b0ca200b0c181695e0590968e0488145d13c815e8a50e15b04225eac488786f3afa79513793a4d9cd6f52520142546b0a5a34d551e767b5b7d93b39dbf904fa522d965a98e92cbf868cf3e825d60956f7101a7877c1c6e1a58e26a95845c66addd40c104e70f9ecf542124eadddf5814206a9a14d44ecf1a8557434a230fa2f1f4f0306c01f2ab1a709f331b76f8e9c0ea4851f60d50cc436af994e9c09aa96bd8189b54c221cee4724f5c95ebec787644f824b5d11c5bf494d0c046ace143de538195ce425436b797749b10254c2383cc4ce471b87aeaecc37faaae3da7c9b18f5021ab9e8f98b624b02f1f8d297a5dea3af1fb66cfd76b46e99d18d05f39297a09badeefc5e7c26fa55e76b3d71f7bf1a2eab58277bf28f6a9055f08c23f8e5fd2bd5e43aff11afe8fcf80f4ba94df9310b9bf51e15b7c2f9cfb7bcbe965067b61b85729edb583bdf05c6f63f722887a4f4869102c67b00f16e57fa3f96c21f840b1d9d470424bc28d311b14d2299916dfc631b9ef9630ac0b306061115fb6cea116f5de04aba98941d8fea356e89a1e6985f9a0f30fd136279499ba6e4163e1cd5ee06f420ccb61d3a1810c563ecca1207cd3e2f1c0f72ce6006d1fc43cef0b091b08a5ad87bba649a9840414772a5820c47f900e0e5cebcfd48c854afd345c7d912053f5e0d51fa994b12a6bea7a279a65fd462ed6dd97f959334c1dc5a2a88367b94529464a42d400927b58e4f8afe5ef945e6ae646c628a4427abebb64f3d10cddbcce08bcc1e8c65ceafde959a05ea2dc3b428cc8310563478e2d1c33424cc15891630bc78e103b31763af566542f97ea2599df658e2d103b626ce15891630bc4881853182b624cc198916327c78e80de10d58b74bd39ae7703f4c28c248ae14b46ae89e85fb599f6fcf2e59198f8d68ab1b96fbb21343b598a82f488dff30fdf9e53dcc5b59aa7ffe5b6ad48fcf88630f19d6422115a4ded81527c51395711902609b759b96d9b71479f49a473b2d715eb78d60b8ea166a763a7737289299e5be4951b7137d437a4c565bd7bc4a51ceb8ce3369c43ab760edbd987f920638d79b08643aff3f796135385eb362912ee6df04a3c03bd6e73bbef6d737169baf429cce6c33f083c53e4ba4d83831be0b9ad75bf6d81c691fa42b641c0c1cdaafb5b792abb95aeb80d8169f156fb1c29b8b1f3cf99c5a0d470930ec0ed2140a669cd2e356ffbed76eece81e9b5593abeb1a2727574ab1f7b45646cd335d221a2e3c74c753c71f44c69b8e9cebaefb434c6c5308cc95017221147ccf1166e651d251beb5c0b81622ccb586664d2f504f7dacce39a0dd1ed9f527230463c00c42d8a6ee43642967e5f103aaa779a536f22719beb90b16a62e53f0fd816c2d06ba8ffff072fcbbfb2f3578d9b8270c8f88ecf1a9db1e3d0ec82d842c98f713b67991c1012f1a484d8fe22d98b808c93a618377cff07b921f6afa25e871361fe49e82c5cc1568afc502ee3a428e76ee7ce7397db0571507d18e46662c99a992d10db476e98d1a595b1890d7eebd039d1147479b58609816d74d36185f7c69d25e516662f327931505d5de136763066a0941bbac9487d343d78c6691b67da45b799d7511b5b8cdd3bc9f8a81d18dac1f851db655f1402a4f6ca3755f004c509f4a8777c8358eadc0b94e637f11eb85b4f2a53e576e0f75579771b742741d4c27ceedb6ec0fd46eb2a372b85abc7ea1565cab736e2a368c3e0980fbbafab21d57b43951b5eb764ddc42ef1c02af4556e8ec0d0024df346a1daf58f901c976183c9df2a4bd85439e6a319bf3ee4e3b29102e7dbd0a27fe446635b420ae42fe7abdc78c6b30b88f769c7aa2ab772aca560b6c5aeb9859b864252b577b7188eda75344c75be8d1c12af1b200ebb65955b72af06cc3b8b58083dbccacd4a5727217c11e55e4ff9ea6229c49a9483a93b73161c96ba49a814e3e0cf6c7513d70df589b9b991aa6def5b44ef454d5274b433eaba3cf0881cafa00dbe491323286960f5000b4e73aec8572a7c7880825186ad39257989b4571dcb8450f23c8e808fbf43adfbb93806680d1575cc8c11a342d75cd0ed7a84909c2c16b852b9bd0c8184af7b8afa285a12051fb001da35e31b9829faa4e7ebbe0017d4932d4d7dae9226d07da60148adb19e13b5ab3916beec6bec33453dfe3ed50099a2aa2ef2be900c005e2b4eb3ef1ce38e6bdf284bbdc318384ecfe7634e2f1f45bfb1c5363d61f7e2e95d1fb4361320200073633ec38990e637178db275cdeccc5247658d2577ac4a990174e380993d7d8598484b74ec70084c94798125407f8c38f642e755737d712dd135eaeff30c12824de61275c8b58c18a855d9bb95df2a76cb23beccc18deec112e2a162b24552ff23d47d7743797e77009f47d0f9cc33a3569dedca84a5a2d40bf57e43dcd9e37d5b0d15cd8e54237c759252dd3255324e44686eb87b592c5912105f45d0a1fb4aa07b9a05c1a8127147dfcfc202ed89aad27bc4553f5d0a67a1b7516cbb621b2a1e7b40e808916daae8c68ae9f242aa1e2d7c7323e8d4e64a247a17df1e045220dcd030bd87806b2f326533d36281162b2a909d82a3eb3ddb9fcbf44886ab2f4e1e22e584e4bf5c2787ef7c84f180156bbb284c53811d075df6da2b3091347b6803843bac8846f0463b618ae67732457ef221c1bc8a78282ac3ab326fa0d4f54808116ee715f5bb1a9e310887074c3912f3b552c2058ac19013d2cffeff298565aac0ad4250cd9ec650c556b39744ac4d46633ce853a1075aa8a0ae0fe86e213cc652f88d1b0b5e64636564772f8369591f2efccaf283feb7386a3be542b944eacc557436a507530794471a0a213763b2334a842c2625c6c5a3639869b1c0a49bf83b7750b1202b36a114b8ffecc16ae632a832e4571777b479c88e6b6cbdd1a5792701e37d3f50a91ab10da7256ea695b27ee74766889a556e3b4754ed8df883091361b5788c76d6da2a51e3bb2d71e447c8d03a8e8a1cd73ee70e2f75652f314af0ae98c5abcb05fb57ade3b17259e65bca107805045814262c510da82538b336fdf01d1668891012de9566584ecc00c8183fab4e84583d795ceb7edfa01215c39c7e45cd9937e475725d172e22b31ecc76f2ae655cb6bdfd2426da81d1a947b39652dd6c883564813511a8fe2f4fd51afa9130053657b57eda9eb2b612994862a1385ee5fc287940f49c1d1f0d5b5d1acbf4a1ed7bb9ba33a48c239e8b9afa6f9e2c4623949cf9ddac169fc384b109604b2b64d25dc8a90319cb1369c7d72a87c61e7cc97c13901c4028025cb623e3d72580b26ca78de40982d25bf210ed1949e0f65af17992a757ac1c85f7a346b843d954715199c4546ce6050aa5d98820124fb5b847a88cef69617b9db1de2cd27cf106e2a6a5fc462b6eef2ebb7b3538f373e0a0fcf9ff4080bb6f028233a61e0970609355113070d008c09f20faa3a7f497460ab72030cf39c1113e2012e8ea48e68947b92e551f8592dfa05b47c4becb84f0ab8a43efac1d98b4e985a85594876a5cee620a3e919767fe05b51624a39752654941c1c29889d5931791c0b0c7080d1d8bd38bfa5578cd31285898707c1dc46889e139607c946fa8bb8f68cb98938923b08318de5dc506d34efc95f708a92ca387ea3552f024d6ef5bf466721dfbfc64feef61104902b74d9636f0efc29e885ce0874bee67d1467915c73475fca789c4824b1a5f615da81935e557e192e9971cd0e6fbf2c4985f636e9a3aa93eb4306b544666c93e41475d376fcaddbc9127e5f250ffd85e18d7dfaa82e3973ca42b0f1cc8d924db3ab2886dae2e56751af5a8256d08b702a4e288d9079eeba1308e3289ca48e1174c6027e119d7a4fc6d691bbe1c238f59d00cae209cd94a293302afb164aaddf5ee17429054ac29143fb0e2c545380e2ecde1c126293cc176da38ae50a9da4e84d9e72ecc09f82c6b9745afe949da072667e4f77e026ae83c88e9891b77554d5c28476b2e1ab34e523ae1a1d221a66607db951cb73b97f614ef8519c81d1bcaed3fd60a8f26d9f01b2c81aed25906219ca2e9fe0337083aa94e2947df097361a2d54a1a9c0f85e2ea9fb174d8223eb281e4dfaa0a1d76d63d3066ceb827f092547cb564a1a542913c97762056659ee98b457f23bc66fe1686da927c9858444bec9279e7e0e1fdad92f31739eeb90dc0a278a5399625aa4f2693680566d1699ae9db2bf8e4028c527cd61bac84de1f7cf86a5dfd807c2ee7b0ab8507b6454a1ce1dd75ea64a6d1296e5cc50da53258442afa8fd60c576890856d040d24b8bc791f8681b86303508111c8c3b68ee1e654df638bc12b6530d0f40619ced8b7928c531e5a7fdc388188a55f629d9c35d565c6fe8b22ad0178c1e5f913bfaa8f148bef31bc131e6fa07a797a91267cb8b2da287620179f576cc7ce8a35aa55d8558c347f0371b06c8cab2c02cac8f6163858fcd8b609e8873225d0182ef0c8e3c90059dfc8b1510086af699f8ff1d1b5a2873447f20bbb2b7c976a331ca685a9fee63f479cc1434224fead5a24797e467061e2ac786d8387233e2198dd4526c38c2adc080a20d607bf4b0b8a61a51f8e716b4f41d00f7b7f75b6214d77cff46867cd58e7933deb89d5ddc7eccc2a8eb26b3e0cc8cff5398d06000e079a5dcc3f43376d260aa2784d3e487c11e36715a1251e89df3b62295ac394e081f98fa509ce666cdafef4ea66766d4597b108c2930ee2b923a9b8b30c3efd9e3c0b0783ef0a09464a1a09e9da0dcdf828088bcf941f565dec2e92116b8464a347ef385f2554262f403deb0185540e1eb356453408dc21f3b6234151578daca2ddab3d061e9558397d44fb032132c383a3cc1d5d45b92c86fbdaa79222a8950f672d90f1660b1621823526cb15879b0b2b7307f89dd117885d66a6c6a48ef3dd1057ee2044f4230bc89dd14598f718821bc687cf01405f3aa368b01fb76393e29bc23927f1b8f695f29ccb2269ee59f976ff202f31afe6498415394643d70569c7b5d135fd87013d0b17823a56cc46c47117e9b8fffc6a3d072069a68e1091167b30a7554f8e3408089b40f6694578a4b25df236ade565134d6e14bc69d1d1e7ee50ccc656114318f698c9622e3f965fa3652e89d52795e6827731b1a196c0363d489617eec9bd244290b1696c1d07f3898a75fe6746473b22e530bc9e00bfd03a35916995658a67dccbeef4d8e87eff43fda983e6debb60fd7660b811d1ed9cd13a73635c0ce8a5b5c1e87bf08e9372910302a384b3cbf6274c48ac04ffe617c5c541822c311a49642872992e87608868570e7263b590d334142e7dffa94c24bf75b804ef851631294fe65b9eb3b2691442db4229cec2bf546278f044ae470eb400ed18fe2352abc0601f190fc5b47c8f63f8ecf9a646ae27beda4d6b157a655d8456bbe7f9c051dd05ff8e2bd2bae493aaedbe208887749ebedb66ab6d04c8d9d3f6e249489c9d25338b6936789dd189298893c4daa62befd511aef7564b6e24744458495402f58bb833853e82e2b9ea7425f8ae58353de91832c735b2cf2c58e4532d46383d9d3aad6ca0040b5bf3e00c4a7f8c1090d9d5bc6cbbbd1f7ffe3849f3c2510954cc2195a1c944eec7e8893b50f6857bf2b6503b27acefe8b3c1412abb3cec5fbebcc5376a2579e7e057030f94ce65470fd23b7052db29014e840b11cec2296893282cd70988becfaefc6466464e07275b6a984735a2724f50a4b1f07f8c6d40000fdad4e359882f604c1e04972eff183443941b5a055ee843dc4904c738b0566fcd724e2c22c3f0462dd23c27affce65bb3cf2b65e5f4e4d6ac1f65dd6bd2c00120e81d4c1d8f60c634d85bec8f356e16a38c0998aa0f7f1b036614e33f3af120848e6e14dcaf2d6a5dad306fbb0ac5c7507b274bd8a276da9a3da290848f541daf330c491a546ecd9ac062300b071c54606cd0be359b44bf116ef7727bc716b5b38c84efa0dc182f0ef88ef674c529cbf70e2e5be22b57f3175875e3a8551848ee0b26662e66363084c2163ef032bd9261fa96a2be14f5be41ae8f03d914eea5ffad597e33ccbce81b0f81d53bc98b871053ecb8d8eb293f3b27fad743da04d4e27cac35836c356ef3aa9bbf641e4e6f4dd3e6890b0962615b516c165d7d53db60dcd626dab91ab6689ab0d9b1355664d6def50c5df31b590dde7a1fa137827933b3b539f0ad3b3f0f79b2cfd66bf22fedf7000add9e1f50c3d00d5b68b8dde43e24d2fe26ed6590450347775e3c28b0e6ce2dcb2a7c6710bc17f7a56a141339a3ac8336ae4996cb8b5225b933f550de36c67343f27e0af3b4af15838ab010017e404c5a5e785154a64c46aa01afe618810d49f7b7d09574b20618c05b1021d5021e4d32eed685a541a2af199c2f0abc772a7151a03ad59ad16cb7d096cc10b3013c03035a83d2875720988d750e5160a5643bbc1c21c5e585632fc74e0033c7f3738044d3104854e18765447bb35ad8f05544f909e0605345159074bde0a14e76827d22e64f971b9e1dd16fd950fd4d2a2e9745db768d2424222111112272cb88082f0862087bd4b1181483b8cd6a5a5efd60549a5a178b70d0b26d9a961f46755d16960eb0c4dcd29595e7c04ac7a52a7b880aa87c6ae36e8a57a8ac5eb56fcccee26291cc8a8bc11988c19cfeb2085de31d75deb1064098f63b02fb68c538f4ba48e32fa8a7f8c11482aa0e77353015bfdd6ff75a203c431735017ebc1faffcf54802fc7664d7fcf1761d3d551bdf09f0eb651cce3f5ec689c14ccb5aef7e3a62f0b7a37ba0bf5d10b9e2b0e5050067429996b3d851f6387f28f02194dd9c7b6437a33add7b00b0578b97cbe5224b69b96d635996db46922ebe21b043add67d7825b0811e2200011e767880031a20cbc9663194023bfb649ecc9381b2cf375b5959a962adb4dc28b79c2a2553696229ecb5f996ed12bbfa631bc5ae7e950d825dfd29bbc5aefe970dc3277ac5ae566157f31a39ec4efe4ca5f1dd07595ad247c68d53a89119c9de5b84d639e723b5a0db63b1989bdef6cce7768c096c0dfc0576105fee91d7580be2525e58643331643a4d6b77bc9e8c36431505f142333f663142662a0dfa44b9674174c875d1218d0ee9139526d56f50ee184aad568bedf047a88645455927bbba17969cb293556a2ab6ac83977979879a7b2b2b2a44c020ac685ddf079ff4525e58603e7ce283952b5a41159546f360c35a2d2a0daddb782bb803964501bb154cc06e5d8180dd2a3a80010a8023c06e0173b7fdeae57e751de337145c2d7763f32dd49ef0a769f96d763824140243a2ece67a425fe87371a6a55029540aadfa88888b951796172a0a5c04f135b68d17d921efb26d7817dbf52f7be653f68baf6dfd2a1bc6db36f9b21df3b10df313d83720a476fd18825a606be083d8a10c0676d813aa3c36e47981c34c6a01f1498b0f62639991e7fd4ed3425c1b5614dc306016b2a21ab60c19d9ee59f03d59153260ade6baffab558f812161e817333146549ab2b7802f3998cf072e5ab480511acdb20514b6eee20293f22d001b377c6e07c00600d420638f6f0a200037d8b0010035c8d83f9a214164e0c6e607179934646b2fa3cea8bc8c4dc61f9549c7dde2e5e2bf06ac62934fc3c3ae4dd41ae3975f62ae4ec8bcbcbcf494ef00d860e373fb06d88b7fd8fe51167b995fb25f037f5df665f0f7d9df9cfde7e3f75e4803b21d6c268659bec8902123c66cc37cf804a66fb952cb58a188612b8b5006ad3e3f6ec3784aa1d3e737519fdf6401d6fdfcfce68e199f0fd8972b7eb4ba5687e6d6631b2788a2504717eac21d35dc85bbd166865a8118b1cd0004c868701a336868e12d204b76f3d340811dd393d3b4dc0d34a83368a0a109799b5ac3f5e5d7685069f4bb1ea5a19911e25ad93d3f3cec871f7e7fed5f1bf6aaa9ac32f861c35e3036305ee010170662979615ad60ad861a40fe45877afeda5649d9ad2bb29bff65b78a5876eb07c6add86a660d359868014b5a2d2076c126b3fdea1440516b905ffe893a5392904c6093a8ac69b2ecf77a7e00a834b5de0afe7ed8aea6e50780fad9fd99b4dcd9069ad4c68c74dc58b65b5664377f6cb7829cb7804e04e0c4c44609cd1d1199c96236b80d550a65e30462c3c6c61d86fb7e17e9e2660f211fbfece6ffcd581c6f0e935802c5b277a5ebd93c31bf2c1f4747b18b93d764bbf98cdeefd863759977e41e0b64333262c08089e932371c0d355ebcb8703941c9b5a0a1b2c232a3a5564363abd93a3fe6f83429333262c8f41818182f5e5cb8b4c8a19cf030d81302350d98756a581416852191da130e69da90c62b2aee088ddad39aa6a6a675b56ddbba5c2e57c80cb586a6060d346c2ca3692ec661d66630a8866586ed8623f9d72c3915695a50d3f289e84454b306a194adb255b6ca56d92afb8d888c466a0814029d279aaaa3119113455395e453947b569f572c168bc51a09aaa1b24bba222123ca3efbd9e71f7dda75dd1315b31a1a1a352c336a6c34b5d597aa2a69656fb80d129cf91a5389e7ffa769419a964d4c5e65b3456c3068b73ff375c5bbce39e731d210698ad1290b78253fcd3ea5ec13109fe49fd918a7bca72a3af0c70f994c2693c94e442adf89482b2132e3e15149a4952da52abbb42be9d8a11ad7af562ffb9a212420c90af5493b4de3019926cbae56ffbaf95f35766948d3f2d3b04db3e483957cb0c71857823dc6f193ee56db3319eef5932e08aa0f4f510dcb0cdbaed9a12e085cb46a9a1dc6f8aad26893a6a626954aa552aa19680dcd38b369667ec6386332d382724283a3eb2ff79b7bf9278fa25440df58f333342a3456562c884e27d372c957b35556645afeda5601b34bd29176baecfeb0afea47cfff238b2c4ad364d975157a4b256785483a2d3253674aad4d155005ecf951e6eea9f994dd43f3b54da2b14f3e359ca4936999e6479f5e3faf3ef6dfd1878997ddfc33b64947a6e5f7d1cfa7d925df2e01adf0936e89f055f4cbd41ae6971f53674afe9adcac79db26e9b29b7f66ef4c999a2f8146a3cf5fb35953d378ec0a049a9166f5adb08b44feccc632e3c63d44e1a25ae3849f7033bbe159f20121f697a49b117b2375a634495f8d9047c8a7933aefb29b7fb7d2c5c8069862e5c166dc431cc551ccf956f907ac786a0209bdeceb0ff4e8ebe39b9c02a3ef06281ff1d756d45f1bd73e42604392e43f685fd74dd480045dbf29720a685ff721933be8410fa288620843d023146798b53892a5393ad15425ad6cc9047242a5542b56ab7dc13e25e2d1bd425c6d8bf53b44f3784621a2952a853a816ceeca3f1ecd03319578ac0d79647025a931598b57fe8269299bb1c2909fbc99c8aa9b44dd4794ea23dfc8a7eaa98ef1c90d46521dfbb0cbebd4a16680ea944cd754e7d3443c28d82c92e6d9aa6b0904b5572dd74b64c766242ec949cd96aea4b0165de6c93ed907140c121a22d23e5d16d2e9743a5d1e2202068b5e21aeb6e5f3f97c3e966e2572d3112cd2ed32ef17243444f4fbfd7ebf1f8fc7e3f1789ac8e7f3f97cbeec937d1928ff72d02a180c0683aadfeff7fba5783c1e8f873af97c3e9f0fc40457825f18140a11017f3f1e8fc7e3f1345190a828ebf22ef33e356aa833218fb6b60d2b72d1be5df952092421d2e7ff0dfba16c98cfd07a025e122c0fbb0ead43ebd03a944bff9a3ead0e5dc96e754c76bb6b79b5135b0d5623647582869465c77e65f357d9239fb25ddb16f9da9e3dca969dc04c5e25242ede83c3b75c37c4cc88d9ae87840e1b168415c17e6cd86c08dd8847b661bdf3f3cf8c98ed5024d25e7a9e0f049eff4020afabffa6783d7f8c973c13ba56d7ea3e87cde2b07fb5f2695cab6b75be56e76b75be9ebf0695a6d6dbddef460de5ce0098316da8495b9d7667a3d5b5ba1b4e4c6cb081dba04aa16c3881d860437b636319b3c562bdad0fd0ef39d0f2c070001b42dbf3016c0c3b14b4c0892cf8189a420422fa1b7fa7d019a469f93c9f80dd3348e34e227d06b3fb047c3bc180ddfcb93c8934ee2cd2b460bea1bb130cf8869e278a368153509ea6a12808f00d45cf118aa2a84f0d6e02c3f48c7e908fc6e56cb69cc661191ed14479f2cd66b3a1e8383245726c8d3f5105e8afa4826fd42b541cf5691c47335467f0a8dce9e963ea91c7c4a70f0dac8f847288519124f251e7113a76b19f26eef4c1e7121867bec95799966fe1bf270dfa8a67e5445a8a7af575c117248ea5496af171ca103a0e752ca94753e8f9df949ebff58ebeacf988e8f45dc189d72efddee3bf4996b0d7fe90992c36e2922449be4abf3f64ffa3d905499ab5bea2bd4e89fcc869349a9a438467946eee104576ffc34aa39a432dd51c264b52a2995c90b4b45f5fce64b1d12c8de64c167ba29cc962236e6934cb596934792f8de62a2d67b2d12cad541acd8372c33ac8d644c122f3070dadb75c037750ee5737495e933c4a22fb9349764281b42127970b55ee16cf3c9eca3e1f5607d93db2d72555ba82a5e55ef555bb2a77ca0c49cbd2da843993c54aa3e98ea839561eb2b4dca61e93cd46b3f47a95603076ff839840dc75746922d873ccf79e954673642ccd4aa3990392924909253577c89722bc347211354769346da572eb5e12e13f93c544784cf6a6ecc798ec5191d94ccd418bd17c3e120de4242ab964253401d99a8a7c8252426b714566b218dfdf026b51a5502d279096965299b52e455f480e7514093938418a874e4d74e6118ecf369a40b1ec7d511c45918b98d439d4a41fa285a31f623dcd73631954dc235d64267b943cc958e71d255371effe1d355371c3faaba3672aee90eeea689a8abbeda89a8abbd551522a6e5647d754dcab8eb268291577aaa3fae8e4ae79eae82915374837f5524755a9b8d98eae5271af1d65a5e22675b4958a5bed689b8a3beda82b1537dacf8ebe52718f3a0a4bc56d76f45311e5a9b8c78ec650592a6eddd1592a82928ccc921c692e96196d2c333e4953866ed63a67722433d90514bb10c7552c47a32fc51f99a3d21c69ade6c07d67ca8c3e6c02e3469f3fecc1a34daa3da98f52e4189fa06a8df1c53ff58f3fe26a0f1647517c91c1826d5a8fd98c82dab116cb91598eca721c455d7ea9aa39f446bbb84fa6cab2e10ef66539fba80d433547598e4c722c47a7788aeb0cce52ad316e41e836ea588b5a7cc9eea86b9ecbbd3d40b1ecc566bed54833df5062b2e6016be6db49c73b645ad6a1877c13c51e7610ffc5443a88e7a9cee8208ae20e6adac33e7bf6ac3262c6b7f5919b943aecd043e98a6ef7a1546be05fd7fc65946f25299eda5cc5d0fa8c1da0a90e1ab7f2989f996fb47cab95e4c80caa79240514cbb21645169bed259713472dfb3771c7bc4f2c8b92651a6273d4da2c7fdac768a2a3f31ca1666a6a9cf926c62ea3ce2614c4fd8d31cd54a2d1286b64a08f5fe2957422a9b9067a948ae27a42ad4fa0522a2d8a2b90164531a5e6b44a5573ac4e2028558ab10b08ab340a51a79297af4b1ae5eb92d57a8c5d5261c720a6d625ead104c2ae2557881e4556d358b64452d79097a84735871ea7d03306862c40bbc863709849646753b506aaaa342f18ec2449350d89ceaceac1f8b5ad4afd6c26228bbdda1d92d2aa3d4c369bcd669ca5fa94d5f835574a354bcd46556af629d5aefd48ce5aaf62a55c28cd8343a5607bd44f28fd43a91a28758af9d6d63b50bad743853f3d654235eac69dba445fad78abf58ab5c640078fc76ab130d8d9ed5a6dc947eb689dae7599f82bd67a582effc845452e14b647bedd271fdb24ffda25ffdb7dd7a67d6b9bfc8aef10998a95a28d680989bbc51f7bd4fd50e8132f906d206c522ed72a64b7af58eb612f16f521dbfaadceac1e0d59970b3a15f9567e8f201fd915c2798a3cd462ed15154444d98a0cccc0907d3d5767628fae5a80f9168b384fa6b15a0f53b1c655ca255369685d2c6aa55a2c910c243232eab3367df142052f445ce9cbcbef781959b1cc3d6b3d8e679ea6e55bee696a4fde69da2ef3f4b29b7bd9977d60290a723ceb7ac2224d2b2a29f9de15c16049084fb54e62a9160b65a5a6e06db1d6c69d87ecf61583bd4cc54ab97a42214dcb85442b2e0657ab7fd7b5d96cbd1d2cfbe8485a6e0edbe39079135ad7d716af507b2cf80bfe82bf149566d457564446d02348abacb4e0161696115e143fbdc272c3b79616d8c3cc3d6bed318a9c1015d8a5fcb2843d4b0b0ee36eb7302cc39206fb988a9572fd2075b08be601a45b6e399cfbe1d499302c7b64b7242df7f80bea1145766b807d39df541ab58b411dec8c3a392e16e988449566d4617cdce580250141c8442ef45191f031c911ea68a186bc9017eee8ad541bef5c50d20fd86437bb68b958ed5027d495a4ae8d7b8b2d5e71ebb9fd8b187cb9bdf4a0c38b5a437711fef2e245b8837dc79fb9675fee3148bc22d3604db0eff8a3e77fc91d72a7f579f579cb88c12f481869c8ba1e863a433e7f71f47fbddf1ceb76d74a5aee546bbfe015cfe544d7c38069916ddc637cb71ef60a51b1c655ca95cb7dd8e6b618c429f9c32c06dba892040404cab717d543f57e40ea0c898a9e89b24bc2d90549a952e30eef704a46c6c5a37e5252b3cd66ffb2b37f4d11940fe5a369d91c19f91820101010b8ea3dec849db01376c24e1810d50302c290e479a2a95aea65598e889c289aaa249818912fbb1904e494b94ed3769a9679c8150f70c5d3513b503d5293689aaaa415c8b7fb5fd7755d7714252b324de35455492b3bc2c8901153b3c9c430e9273125304040d89fb01376c24ed8093b513d2010d5232149201010900710105069576ed564977e25a0122f9552e515a9073ec90fb349409ac6629de7799e67ebdf6ce2c96cd30ed8025b600b6c6dfc0404ac91902410080858eed4a49f94c400c190e4c9080353620202026d561212a152c9c64a645c299780401814149a4bba2b1ee0f8a27a3d9becd350df27a1937df249d5345555f55f36ff480fb357247bc593dd0cb4e2b1e2916fef4a072b1d6017a013f05ccff55ccff55c4f1210b0b42301cd50247d8439cf5cb24b57e093fc260f4f3fe12720a0490c4502d2b812d04ca9a2a87175d22fbbba649378e093fc268fc6c5fc6808c7a88000a35a01cb1e18185eb2a20404964f735fbf9addb1bf24a02010f8221bc088d144d9b4549a114de4688aba24b7d8cdd11ccd31ec23298aa2288aa2dee18eb0d76bb5d05047eb3c4d20a7136a94cbe552a988df544aa552f926ea910c7d27adf589c5865a49ac4aa55267c42742976fd25a9b4c5c17444d49fa572b75466489ba2447721445566bcd8aba54772c5b42cf54a561b592608620454d2c40b12c17bdcce4c89cde15e55b8e249f2092e410890579069c42a449f209f84692580801ff00e34a1d9927bfd6a14b9d92889ebfd491452491c669269ce1083d8b5a480f15699c0e1a591d4406356992c17cd3a5d6247906120b732472722c459df58f5c89f0ebf9052896bd239e7cdb61171f19c74ea1c3cf21d77c24caa48edc4ed6c959f37e9aa7356f24227b644feffa4bf68044d0d8cc3a9f80ec91bd2c946f239e3124918091af72749a216bd555630ad5cdcd962c4b96e6e8445595b4b2a6d4dc58c6047242a554ab76d56a9144cee293fc614bfe505573e456576bdcc2d2d54bcee2d4ccb7b66371cc24153c433e422e4c3194636cd7cbd8edcfecf169b6f93cec1b9f53a025b09bff1d77bafe3107be957cb29b83300e023f8c6b0010c639c087710ff0c1b81d703ca8a4e0606bc5903143c3f275019845d1bf7ef1875d6aaf515e9b7430020881bd6e2526242c2d394391739e028793d7e1e1673f11a6cc8dbfd1dec6ecae6f59791bb3b73905eaf94b3ef9560ae525f0ad5402767d1a570e95ba8071e6094a408d3379a5a27c33794a59e09b9944cd757f35c3df154bc35f569743259fe963d2a139d5a96bc7c8e988b258256ba76117d8f75f3b010fdb0a78da46c097ec033cc92ec09b6c033ccb26c0afec01fcc880afed05a4a83c020630c48005b4ba56a7ddb53b3c1d04d86d6f00bbdd816293f1584ccd40321c66d2c9c94ac5778a1263c2a64af114460021b05dbe365ab2414cb68a84656db16431111873e3144a4446f2ff335adf7b8b0c994e00c203e149c234641ae29ca3ecd6db36ab01bb8787c00edb877fc07ebfdc2339046bb1162b16c46127071f5e09b4be16a8d585ec17cffadf2ede96b2c957d9fa5776eb59f6ed4d36ee49368c2fd9b9a79d6c57db82bd64ae964c46027b959890b0b868ed8ad6262a4daadf2076f2215bf228ea0cedd1906579acce903c8a928e7be6c6e6e1a5dbd96512deea72ca8eecf67a59891435f3f0a8a8a83321fb7b3a9ea605a97cbba379a0026b5778bb83d669bcddede0b56d78d8b6f1b40d802fd9353cc9a6e14d768d67d9347e65cfb0ed9aff1d80afed1bdebc313b00660460c3826ed830201bbb6d7576bbdbedce6e69d8ad8eb6d7eeb8a1d4dc70283ac65b4ed27287dc5cddc499c0485e252e5a2e0c864020cee4e15fe14fd33290a6b99ce9d07a025e122c0fbb0ead43ebd03a846b27a44bb9532e50fd2524c5cbb7f7810f74495176ee6bbbc5ff7671c096807e96cd7a93ddf224bbf525fbf6b44d3e6ce3feb561fc0e3b02e50621f50e040dd8ef4b60873210d8214f4fd80b7d3e38cca4567763f3f0e6a6f971cf7cbb65bc6bc790c0c63223eff33c9af63a5e874205e54e3abb1d9e8e5e7677fc0065bb7c6dbbf8dffa6dfb4565c7fcca269f65b7de64df9e64b77cc986f1b4cd7ad8cefd6be3bedca67eb27b70f8bd7b72c0e1cd368716c0bed2ed783d1e1c1439ec8fd9af23bbbcecee5ef6eb5c6c1890cb86f9ce162693c9049a80ba1d5ecb6e75b4bd1db8201fb8c8e55e945cb8b8dab1ec312f5e5cb8b8dab1ccb56811b2651e65bf8879dbee2f2e5c5ced28d3635ebcb87071b5be31644dbeab3325dfd51ab1d31ec53dbdb179f872af2b35d7fd5fad58d605030bd9f9ddfb40b033c912131296b1d378ab8382f52ed419f25da835221a9386ecfe28fbe56bbbe53f666fd6a7907fb25b2bdb7c960de34d76ee4936ee4b768ba7b95ad7d0abcb7ee4db2bcba2e777c1b0a2172fef73c1df5a7fdfe7c2dfe7e2efb77ce4e6adc56e77b8ddeeb4bceceac86e6e69b176cf8d57fd0d1d09f410811c70d8ad4fa5197bab832264533e863a637e0cb546ec278fcaecddc3030fbd11c596fa37c56b876839d0eab4bb76a7dd61b70fd8ad0376eba369f91bb05b08ec36864a33f613deea644e760f0fbf770f0e9fb29ff73a5ed763f2c071e0dfd33414ea6504c54847b49bb02018900904240993c96432994e3b76ad4e6787a7a3378312f3a281d1843a421e2f44e63503362344c64acf0d9d5d76330e344c78aba3d1137383867a437669c0c0b4583458215b3fca6ef1b5edf2bf5bdeb65dfcdead4fd92fafb25ffccabe3dcbc63d6dc378d826ffb5597f636399728ffd64f7dcf8f1c69b306e3d24fe89f7ed4080ddfcb97f6bae16c3e0adee95fde83f5e6cd80d1748f45c5afc13ef03c12ec75dbce523c7dd704b0bb73aecc2aae92de1ef5a3299b3b9e5cc3133a983ead47dfd2619c62e442644bf6c35bdfe2f29bd7e57eb71584fc44986f4fa572d9545bd10a9fd39ad8fbdfe93ca9ad4c7c9d91f7b21704278ceec69af82fdeb6757aca9f442ac1c2767e4679fe33eecf7e310719259f90b81f239edcb5e7fdab35ab1ec6a653295488fc3e23839264f7b9c9c939f3d4e8ec8bb9f43f223b1fd383932fedfbe10b627e224c39a56a6528984521f2765f2238f9343f2229f73f2eee3e4b0bef539a96f61ff42d43e47f6bc642a9148eafa382a2e044ecec9937c8ec98b3c4ece2af5accf51bd9ebf10209f13f23252894422a9ea9aa59061dfa592d615c5be8d955a7d8e49663da11e07f5383932ecd754a5cf21fdea717242fef538285c889c13fe93be09ff19dbc7c9297deb7172549ffa9cd5b35e88f573b21432a817a284e3e490f09ffc34fe133e4e8ecb7f463fc27fc4cf11e13fe6cff8cff9383932fef3969ff92ac67f5ef443fe9cffbce49b9bffbce69ffc9fffbce78fb88bffbca317b9abfd17ff79459c9cd687f09f77fc540817c7c951bfe56c6e091f2787f42d8e93637a16ff41717256fce7e426ff1971b3f429fe63f2f424833a719c5d880ccefa3932ece380709c1cd29bf84f99a37e89ff905c147b38910419960f11e6842440c20f2464569e43e23f23c7fc47e45886e3cce811502c7b4322971b3363971b56afbae6e9d8f5dfe8ae5d6e5012bbdc8094d805889bf686ecfa86746217206e525df393cb4d9862979b51d7372512ab6bbe5af54d5ac22e372793ab6bee0a41b0cb4d79c22e376bd78f6f545df34f61979bacc22e3767c72e3726167601e2a6d53597e91676b9197b8b5d6e541776b94175cddd10eca21fff0bbbe087696eb256c1be210f741cdaa9818fd0140a7598e82106e1e80f95c86e8e026757f7987e3637219dfc70470f08080f7998f859be399680d7dfb057018d0bb399721eeb16581cc4fb11f2b061fb88895427bdcdd94b4ab8bafa3662d81b7bfa366ddf71933bf920bdc477503bcb7bc86e66f59533a0ec24ae437633ac939c002a4740767b376a1ff11fb29b77d03e7a1b54a853eae5dbac38aa3ebecdc826a493be02245ec3aaa7fc86ec3ea99f5c86a98f4edde42f692f45206e5a9de42c641f790effbae6af0884cd4ab340e837599bd01011b0a743e8e79bcee2b83a7f649a5f66298c4006207461471547883d9c48427f6f4274c8df100b4ce8d9020d313addf99fdd73f42e7364feaa3479f92752081a90cf8e247f73784e784007e3a722af0a3ab420be9945fe6e80064690417f3100ebf9c59ecf50093a1e855a0ba8ca62710c514861c50944239c4c2a78fd0c21832e80c1054350c20f8c85148eb042165608821145ececf4a0e7033200c9600959c4e4a0871048b0848f0a2a28d259ac4122104289ae9ec085ba1674ddf5afcc54c74122a0427f4fe2126418d9411142c8c1042596b083d10e100b784cb153869d3144800c425674a0035e3022099211987085154afc7e782ed031055108c690043304a10945aa25f0d0420c7aaca08a20c8e0c73f903a2e8219ace8afc86618acc45f3d0e478e048bfb2a872714610a43183a83ee87c515473021031d50218927c4408e3e828a181101054664b5e3a022783092dac7f4a5a424824290051645f8f8682e8e7b05e74813e10136979fc01a0b528ff9e3c764cc8a3d3c50451745511445f1c4b22755c73d8668a28374dcc30343fd65cd0f88a1bf6effa1448e2254c1e3033a701449e895000a2a080ac11570a0e087124ef498820c51e011061c4448f1430218b620039d1364f0e309ec58400adaf1d658c7411f00fa427fdd1e86bf02820e9228a282074800a44326eb2b3afe1b722f52c8220965500214b258c20713f6e4cae9b03f3828fb60f34d83a193bf7a57db7ae08418e8f039438f0910c8906f6a1c890f0a1e2888b44084840cc957223fe1ce865f80b182d0117e30c41267e0000e1d261cf1829d202420018bfcd8ecf986a530010c7aa884d843949171a1cec70baee8a58e7dbc2088081f28c9d3cd8ebbc77c8d33f929f720c20452f46842d75a466bbdd27110112de8a58e8388e0915923410750b6b16ca8bd10aa3328eccb66dc9b3d7f0d6ebebd6bcf423dff49966fa10e762c8557878ea7e8a13a13453aaa334c2851a6e44bc661991de4188e23cea38e91b00b0afbdaf8e9867f45ec7afe979e35a98383c25088a5015d85d623b0f871b926a1679a4f927b14ba7a884dd32c4d93b4b1e462325bb2e56a94b92ff43e8b4bb37c5bf9245647e38ed5cc44267f6db4d6da1cc7d1dcef775307f3b3b9c52f4955d452983d33f7723e4287c83b42ece55ecef9847198e1c8c3180d51fe86281aa268c8dff4b34dee2219a2e2a3e93a9aa31fa1288aa61fa3d3d19350344d47afaa6fc843b9053429fdf32cb14f1a3fb7d0d193deb4472bb621e512caa24f52b94dee293779588458f2971c7b4e8883cda40daec083b138922449923870540408d1c957c99eb7a8d2d0785b40d3e0d09a7cac317fd5cf36b993a1aa3e4992afb505740f5f7dfc581d555535a9807d6df4887235fdf1dfac7e8d0fe4bb3ef92c693e49e22ab7d11de52f8ebea730b38b9f3ca525cfabbfcd98351f7d524892c85ed6c1e3effae2280c45b27c722d5fb19bd222c21aba92482412f9241202c25dc8eb2fb99248ebab205fe26fb8d3c9d7a85105a412adc44993f92553899fb84deea4f5d57f4138b7c97d25cf2f22ac911c8bfde4ef28a4a19f0c8b18c5104729971f58c59dfe66340ca998a28f742c051535a04289bed2b1145508861c074e4e0fbfdc951aef37ec891f22b1337ac947dffcf24b1b675ac9dfb4fcf349cf6e9c69a34be2363993dc46ebaecd6d428245f96b93357f47dcc62c227451721fc851e46ff89a874584b5cc4fa43cd2e008922585c41fc5512c8f10b568c48f2449922449d2254937dc028c17b9162e2e5e6a369918b85b0b0b2d4565a59b9cb825282430305be7c71c61bc7871e1d22287bbb5b0aca8a4d05c18181871ec23cc89893b32a6c89dcaa884eb9b69729429532200002000000318000020100a8591284a92200d6a4d1f1480125d8e62504a3892c863a12087911845510c43c610430800c620406686684e006ad2e9c549d8ec5b05c6af15089ef6b67337a444b84864de073085ed3fbedf39d03378375dae78aabad0de1a7a2989d3e194bd6dd8aa2a664f992277e345d22f544e1b28bca86b61e4bdacad812de0d450269700b70821ea6597d649812d0dd7e0e34244619f9c3d2e301313dcfdd5bdd70333e5bbe026624d0ee33b099c1b2a9cbcce7604526663642d01684dd6dae18d921c6330ab7d4875c831429e6e8f7d95a0b469dbb0d1dd0835da7b8d0723fe06abe7df623b49ac0edcd2a1d9edd1a27ec548e2d6e49ea65ec4243a3f803745b712852832595a699240b1475f839bbceaed7da89fd3801aa3c8d1d8aefeafddf1c342d2c65cb26928ee642c2e6793baf10c29ba63d3d4a20193de1a64e7285f1847390b21adf9cb411808f74ae875bf08c6cf196a35156a31fe0d5c60eea32ecb90f595243bde1b06a7cf8fa27d1da9eaabf71d6ec39cd688d0c7bfeeccb30560027bcefa1e5c959d8c00bd91eb5e0fb1f05a626bb612f7a8f78369b8d98471d28879a441d9e6e56885342919027383b10039110188012b21df478f4ce13972200f89e4a08955cadf39486fd06025eafcbc5cb74c0d61a3f0f3cbbf0d73a3d1c6c5617626e57d0b3657ca7ad7a30348aae6d914d8a2481083559b783ca86325c13cd1ace24c060d28a5d0e9298ef8556f29b75d83726d7c5b1aa4f08be120a94e608760d890a28f809009a7704aa0889d625de3ff46aa79d6e03673ba8208d58c2718b515daa0645dc8c704592da5224bca3256830aebed0cc42c52f0a30e4b3207bdd6215383b6b62273f0a14a142296aef4fbdba4b397c6d0969100ab57cdb0adc713f62b8d0a7d6d26fde08adad742227707703b8ecde9497838da5c81d1beaeff793ec2deaf5f89655e9cc7b8b47fcf0eeedf455433979ca32016b663a1b1cad54f89aca236f88cbfca133482db67e7f622dc88e164d5d5e85c0b4c097860feba48acff1f2c0c9f002b5768b7511b0009bfed949baefe29ac52142a1a599e1d62ca9af655a114b97e702e253d391b9d11fab32a1e637e41bc722f1f5974d3ab508104808d6edbea76e4190ff574c5e8d781cb189aabf7d05388f42b32a5ba2305cc386393ede138d990fafd840f177a6fa56e562061c56439b87417ed14dc8f2ef1258f6c3c94a776576117471b66ba01a310a94081b3e311a297e20b5f55c776843817ccd541af21c52e8151ca991ff37c4cc9060e8fb964854a39806a3bfa214908ac9d22ebe1d31270ddd5876b550b926ffd9e5198d3f434b6fbce749aab0f77d21e710394fbfd25506b1a8cd29d3ee6a390c7513832c72ed0c1de00ed46c4b6bb0e206ac70eb82166d511326e85d7fb0c76a574bb6d6540038d35ec8b1b0bba30dfe1437c74078cf4e8e5ee54f48cefe2d5cc846fd0c8a0574f6c63c1cbff05b5c539b162048c82df61f3a746dd5c342d92efd2c0a9152bc910255f12b1023616bcc90887eca6d9c8aea42871a28bb1b7e1e142ef098926ce344bc9f9dac71b6d3266684d0d024eb06a379bd2a0a63d0e855f68ff1a424af861654dfb1c034c798abd1311fb1d81b37b205d56665803b86ec03897abc1a0b76182ec782523e96e6f8b4dd8160bb106513207261d8da13bed19a99047068f7ad10092818fa538337b7e3cd49dd312488550b85327a21646d76f5475ee0a40581d6c5e5874886d40773702c35cd22b0b67a43b0114d630c031e07f693dc015299c03f5c34b03ebb7ad57743a1082f441dbef66d81fca0048fa1a8770bb9ab33f17e87c34d58d13156d6fd2bb134e55b0f7edbe87e277527301240f38516af88fb51f480310b5f3bfec56544658137e3880bde01c2127ac3f7756ab67608b2c3643d6aede8110f69bd006a46368b384beca9925427eb68da35a4a0d7629144b5c10a03c4799c1977468bfdb96692cc6734796fdbf5b0cbc4ff78c70739a2b4bee8c3ca096fab90d4d27daff4af86a38d0780f237b47a7507b24b69908a7643f8970529b79d640c29995f519c2c4041daddd0fc923630212234a1990bb21739a916cd0b560d6e31fdede7075b2e297625b91af7b5c7de68824be4dbdb95f6dacad78c5a57f7d11f3e66ad6635e57453d4a046e00c58800ec739eb3be057890f3a6c9d95df8b4c8aec654627dd9119d1b0804aa07217138a0abd80d1efb783054c56fe33c1ce1ca251127f017978307606427a67652465917ca555bbd87873f2cdecd93a8ac722c3fe2beba9f6be6121141645cb3553fb34b82192f5eb2640f8407490ff99ebd0b67ce06532bf3057a4fd4f74dfd6c0f954c180396525f2a230a0862e04455047e5f416c1978f810ccf8b24e0dce4da13eba53543f968c3ee25869056ea60b689fa125ef733685ca313236167541118b6928cc3e5c57776031800132ad33f37dce9cdb2f57200a5a6ff4054f00047276742c01d13609b47e4b86c2dab59020c56b711f0b9b9c474105476200584590a2afd37ecafaf31260cfbaefcaa500c3b6269fd08c12284fa989ac91b4e2000b1b3d87836c345c6b322df58553643d9abe106442de1f8d16e46879e300edb7f412b031d05f900f4c8462df729a3042ff81c97a0c0e7b338a76d182cb37337ba32e29327c1830a388ee20fe972a19c5743181450c2ec764389d966b57f4ec216f04206fffeac711a3efe0576c9c4f3ddfee07597263c253959638db9d72ac9f113348764ccd301923bbfafaf93956ee4c10fa5a22a79f839e0d708c3ac182e56d23ca6e728b850763676e82ee7fe8baf6a42a99f77f98b809231cfe691f23f052b90c50e767d9d554dcae4d2ea394a4205c48eab1f13d5c31f1adc435be0b4506c56a5a5ad40d9d265c4d6611d656c07ff88927dae418f4439cf74eab10328b1bfd2d625ab1ffeecb80a705957b1e55064726821a592a7019a763962b1205d62aa11b868cbe553531e6130aaa4a88f8e32caec884d574e0103bceb584c0a70c83fd67f54f5885c98f096582077578187145ddb78b070d0762bec9e4b2eda7de0151c88753744de448fbdef6ff9f481dea1d65a71f459abdedd222c30e8bf1867ae51083a85b9a6436e3111325941e628673d19d647ee3ba9ceb333212a2ae7b378d098f835d948c2e0f5280d6a69f688570d588af31cf544c705055506409e1f6e025c15b0435cda0bee666474290218228560100a8af418ff3822d39e16121bf30d0fa7ee400bba6159acd2148fbf6bb7a281e191242306f931a6611c17b66021fabd96c213acd5ec21517a743034d0f11e9cc5970fe043ba39063f456f13f851599da595e03e5a6ad7ec7af8f72c9d0a57848d62341207f15458f222c8faada68460588705209a614290f2e04ce224195c52ac41819210fe66246caa90f3b363c7e902722da68d11cc6b7c9fc9ba41c33795cf673691d474dd48f55f5939512e4b5bc1824775c225c66dc9bad415b3e40ea2390eb1b2c297102bf824c4c850187f9b0742071a15d1e0d05d09661b20dd2ddf06465208d2361bbdba769b86d22270eda2a887ae806e00ec801d3e63e78d4ac2e1124e884649dda5b268f952a0488ab822caff6e66d0fec25d02a46601af30c30570812038966a2844bef38565fce134696c3ed628d49da239af7f216921e48437bd346c5993334f9508be70256f8e7791a129ab179fae8ef385987d616f783cd9f1eeeb2f0372ca444e97b5cc55453e9f5c534e2c0c468a9ec4098eb48d0c148837801cda2540432507e702c6d519774a13ae51ed03749c3d24823afaa4102631728f610881cfe11d113ff385065e9e654e3691da51e4eb18a4da0d4b0b86282cc8ac84130bbf3f1414a762538e18eb413298e687304ac31461041f620c9d9bfc410459fe52823008f6218d7b22f29f0c094951ceef2de2c441f2ed5f894c8ec4b784e9932c97d0cf4d7ba9140d81db12baaf21b19c2bf3fe7ba4a30bd3baa0963d30da19d9e28a8a03b965c4f6666446494cb82569b5163895cd5aeed8c138b1a4514c6882572415b47c66ee11b4711c453165af731056b75da2470e254ee2be54482a9b33872047c8174e23a88ccac399d8f8d0e05938d64fb9bffbc1cfd52084b5496577ffa0eb0b20e5c408ef85b912d091142693195f498122fb771ab9b94a19c9bd8292c8148fc62a338a222c835fddc26ade47df811fd9e8f20154c6b8e1cd69d7ebe93ac23724fcac81d392e14fb51080e9dd254810485f1a3a6959269895444b4301e8c898923e04c4f43313312e656bdd12a41d395ad7bd4578f3ce1017f8bf791e3a94bc029dcaa6528b1124cc28c047520776cb09306e4fc31dd474eb9fec8d59d25250d10145c09e8934b17c0265148ab8465b892818f2feab6aec1137df3e48ee7e6cd681b85b3b34bcc41fdf99a62198b6826a77f7e846a74bc4f4e59314a5011bc90c024eae69b86c7116e1d88b320b36ebf27487c6328cefc26aa4a467dadafde27c7a20206e23eb9ba1ca0b37a66319c4684d1bb10e38556784d91f37ec1da201117b6fd9744c717eaf8c246ff5593d34d9e1f52553de56a5d44dac489ac5d53f64e8aadedfcacc24a221dc821c2ee1f404152b6e8dcc8cbad0e6ea07ab0b98bde3ecadb039b5dede598c1635fae593cf672cc8da0772d2178186fe049c13728424f22cd0401214bc3c4797818f15017584a94bb6b69a27b9763361e3bb96642263966f0d8c96106df3ac05e225175712b1ff6b030dc84b91e5f0e20b6c870f99efeba8bd9a47b2d37bb0e252d33739de10852d39175d4b20cc7c4166c4a57de4c67d06966921c50f3101c5c7e58ab2177e59d4738ad3eeb4a04c68741e9679b5bd599e37c14706e00d903b61663b4fa3d35b41679674837976959cd8e7188662c4e41f2c3b236712b1a90155cbc5ba072fe4192630a4844070b55c09328a024a6806208cebd5dfe0488d57eb2f706b442b103c41ec92388230f476e0dbde31efa0826dde51fd5f2a72411dd4f11330a9b7ecea314503735aa9103390fee650920291046911b23f5e125debefdb7e1e3aed4fbe31114620d901ee1efb21296c9377c4674a860b662a4d7f1e0fd1f61a427b90474c9eafc2e8600312342c35a8cf80d50f6d6a9a4bb10e456a63590087384913a2c547cc41c03538803a2b6acfc1f801725fefac01e0313960292aae86929d142ea62e4756ae2643e8be93aacec8e9c298a36fac00c523ba6ca9332994aba8973664e229d96461a74ee64c79020846927e932786eacaf8df9b284adb8bab9d84d4b4baf5769af962899cbf70f3593f4c702a5c0620e12d86014e5eb342b6ee1e030358ba6862613a244c06227e9b63607164a28a1fa9c0615a4799a57f3748f2c4545b1502eef8d510aba62859a81c7c36176db8be960baf90c08ccfa2d8735ed406be4ca67a2d44548deb5621d2e547ef5a2ffe07d8034e55e44441c8a6c3aac39c524eced620085602c215a36a4f6a170ba3588c4745886c05804087f3a501801f6a532fd905685a40bc5ec7abd6f432271d15d32320f83b9efb08b565016375a929747ba1d94c578b1d0513ca384332cb20f6b16a9abf50a42e75a11edcb0ac7bdedc239c8874915b57ce1f48a9fe7580188afcffe3f1cab8a8f0aaeadec5da3409064aface66110a80576278a3d1a842fd9175e6c19c69801258b1510b16e810c573db622bcdf4ea9e9702083105789fd98209e31e682783d842a82b3d6931f36f38be09e5cca62d91cf93f68db1e75a6e53b3e29277745a6c8f58e946f5d728a003c3b1f2bb77210156956d2eda6168c6a2809e272d73802c42832ac41ba9bbf780bba7538ffd07f3d0ae2ea8491337dcd7195b086c3332a1122106b888194d69bcfe541523bd3fc76ef5bf420dd9452c2876c16c8e09dfe31ef2bd59fa7eeed8cc3904f4414a98b4bef00458b9a53e5ce3cf8b39c821f363b608292ae68af43d9c3a6880c9b3e5a4149d795eacdb2ef007c580a7d659f3748c3ca3351afd8c39a35c6344016dccd14013e2c334d70b1a63638ea6f079274572914effe7b9557bc452c4bc9fd35ef0fdc1531d2a5c2498885f4ba376c452cd1dfcbc876858c85460d9f21a49a39e202656a452d0665105d48283ba6900b05f7321a4d64b4a9b03e8894f1256aa2938ee994c121d434e64a2c44d69f16ba105202937590cbfef9d3db61043ff2b2416c1741a55036e84ab4ef7e0de8957130320dec729399dfe98c3cfcf6da981fca87dee389f2a16cd7076c659c52983c2ca48473760a1c2355441a4410afffcfaf18304a0306bb4ba9f795dc93f4345059dd0bd4d22ccda44cd470e5544c8f58c9f9be71fbc1045f7b4d18fab10c227f5f2ed93ffe4289d43bcae0442259aa801978a33d4ba55c52a772ceae8efd9bbb5e09ec273cb05de988d825a482b2a4fe19db64fe6d4949aefaf22116c025306541433e4d87581b8f631ab7b4f509b09952c44b499f84041485482f886dffcf891905cd94c7815a9a63815fb084b852e10599250849a4aa7e0c3b4e660ba2b1b48c7d672a3d91d4988b39b939cf49ed6842812a7c7cb2f381ce4d4e1079074f7b13d4ae9965d5d88d7461732fe30ace855efb56c2a27b62992d8fcf3054eb46b14b50200ace3fae5eff8d4c36e77c59b316be3997c0385d9671fd7aef0b485c94a6b3d11c81a43185a92dab6785b29a7d9b9a8d0c03ddbc7adcd38a21b03a8e79bd0050f69fc34fa6e1f24b0c9108ff50964f5c17030c1d98115f5ef6277464a60e8655dc4c781c8ba46572a435f6732d3b9acbce195cba6a2337ec89c6e41cc74d8249d018c92dc7f4d9754254311dc8ebfe542d2388b8f2d680538ac35850f0cb8497469e13c4bc1b83d5e4fd3301c8f6b63f3eabbf3c614d749317d1eca791bf3ef07011c0f567696652a9660e79f90098ae4a1e0979e1569dd7a4474ef8ee1bd6fcac0c79a92e6cd3a26812f580922f740a6ae8e152d72427ac8a07a457ff44d4ea4a10b8893a34ec00d67b7227421b725a0fd4ee19701831b15b0508edb9286a17564c1c55a9cf95a056e9de8811b7907d3f0d916a2a0a3283c8fc26b605bcd6e677c95825284b2b7d3a3b36504d0248780947e1d9bd7bea5f6c44cd4f76c0536636220525534fd775ec06b2cbc4ea4b6035e6930bb0ace9d0cd55de82ec67a135e073d604c98938bcd0c7821c8dd623b7525810ace7b0ac79c186fac2201254bcb46d88c5c97098b777bca2f73d63241d1f5e0615063932c92e6268485830854ee0c83346b806d534b0d393128218a9e31d73a61ed81a73d8884397c02bfd51ac104018a9ddd8d95fbeb3c2fe463f54b4dcc302b1e658687fbb2073000bae173caab8a54b9e3c2f88374a7380385ea832f9858467a412c3a5f7f75abc7cbe0f67a4692c038bf8669082623df39eb476a393c35d9061300f08be494376aa9294b782cc05f2eabb87765d92b37334233090b21577a93774f02631f226046bcee7104321b510937fb336834238c72474a14b9d62bff48f20643f26bde24513adbcbc1156d14b28780cc78cd68d0d4f04fb53814dd02aadf2e974e5543927cf0ca126209125700e8e0ad02ad454ec002e6e760e78915d0b2b5c13fab8a19a8314610b8bba9e8aa1cb38460ffa325cfe1576012b00236c3557ee4fef0787b9c56cd52d3af6de8c75bd38fc5b29deee65157426a1ea2df810760def9a6de99154d11c0ca10b3c6a1789f6b45cb41f1c2293c81587513e1af5932c0c0a7ad5f1d40122715287f119b1111ea97f19ce34bf51730627f4136cf4adec2e2e0d61bc9e7581004fa592bfd2da979547ed88453e8b12d0f586392d34a6a694e2fd6062d0c8c386a32689bcc61a9bae3f453f4a3b5d5987f3aff94bc2207bba94b613326708930e828b9c6364021e8ac381f00d1f1aeb74c82fb4e2b4328fafed9b9f2af1d76a8760fc1ca9119b549650cebae0f04cc23fe8c165040559fc9eae23108594fc9c97fc14a606a904ec81be65ce90e2d98830f0aa475e73003d1822fd32f7f363d2e540ad86c22ae78d74514be42634e1de3b248a4bffce3083d8091c6eda452df99b41b1589f856819d77165707023708ddc0445a3349243afbba8578ac2dee2d3038ac461ef3e16cb9488374c0fbe0b6946ade312eaf0892ca61fc84d06c2b088d07a892a98be9399b6a4a6904a0b843e009f4371fca4810268fd47a8e7d26350a988b7a49607005072b47b60e254501978bd5bf203e4eff661b54a61d3b17f2eeb0418260ac3ab89c6b2a72f4d7bc5f0b917c5473b87379a7dbd83d97f882b2848e3a5f04add67be0c01d2f43cbdc5b64b016baaa0f683eddc34e3bb4db30bc9faec9c86cc83d67022a1772b82206fda394dd7754508fd7d276c05669eef473b93f741a318004908325228a61bb4519e39829e540c921a826476f39323056d27c6731ed0a8e305c1bf6ccc226374036c5c7bfd9bee1ac2a0314d6bb9bfa9b288f2bc34232ba850290adec55918075c39c1e2e0d59816bf4ed53b1a9897c2fa5108826fee35281fd37776ab458aee8b94c19787b49d3ad818a45e4a795431b013e384aaf3962c71327f2838b89706418552a536c8f1e77be8f49eb41e65a154c9d2e5a479a836048024784cecccd8d68b9e028b34a5ba0a5d6e265b2e71aa86b2e3b6b60de59ae0227b6fb3a9e30156fa5a119a6becad34715ab92f1bf308fc9ab5ecb965a2c8bd8050bc266500ed0a3147865ccb393d90cc29b169705d203e28a9c5856a016ae636b0d8c1a14c723870c6243d9650c70fb9802dbcf39ea653490328b3e278ecde268e48c5e9122cf74f03178fb700b48a001a1c5466d8fa817a81ed437f43d38fa64a3e3993490ef95d721f132391652a03faa2e1c660555657ca449209ce32505d5dcade773b717ffbd34ad3c942b479451f33459ecc9b501f2a699a2e98800517d6ba13a72f0e5d852abb9a20e677304fd60ee2cb1f23f517d5d30e2d574a535d6caf58f1f13bfaf8203f4ee78f030134bccf2d3d25910f95e2425321f7268e8b524e4551936f63f0b4c83f2ca12cd457d4b3664a06187aacde3179a01cbd58b6760dad9136e8fb014a40335580af3997182cdf2167110adafec55e5a37c6eb990460c490b4fdcbb3889e28ecd1629123d360b90e5ac9a272aa1635acc59249b809aa2acb66e7ce836a2a9e4437efc43b13c26bd683d849e5c758502e3c782346d38c4535842a9af2d71a72f8cc1aabec0348c7263d32628c4d887d83534d9fb198ee54c5010955882897904cb8ca6a53f2bd860c8ed9c63afb02d2d984478cd458d62eb7896a35839eae3a3d16c5f494abcac9c37052d20a32e45ec730fa8295398fd26ab4ddb361002060c7b2d1175761fb584d2d946ca901494556951335a2bfc63a134ec7237b425560184d3f555014ea2c53d269b15ffdeb0f013f758cf6e003d1a686e42bc5a09d9974c660a01a5ce74190a706dcd0c27e464eefc7fdce0fb41a46406a8427435ba0572f6d4b6937ed5187137f8ded8fcd10032f57b70c7e25f2d7d8e831676c629a3e55145901a547216905080c6b0151c68862b8df02a67860f13eb99811a975269bf300a8d8051dbdb5507e03d0ba5d908070cb184a4b0660fbd4e12080e76cd522f2b3fed64ac738bb628b36b6787459fbc6caf07dcd9d27b2c30522ebc3503619bb284d48bca2d00bdf45f289b17e2c95302dac51b870ceb5f27b4d0fee6e81880377c6b54d3488df39bcc1c3d92292cbf6ca37450ccde1b4fc8482b5987ab5defb4c30e01f6b87b255b5406167c2ef13c47bbc853728d87dfc2e72364838327ccfe7bd60a3c3006dc0ca99ccf7348df8d5e118ebee7e01a5f37770f8c7ab39fccbed618de6b96d92d173f8c134f95c35eb03472332750dfce49864c8f0f213904bcb31e9e0b1d9e3f3f750412b310ed38167a4fcf9844299520d4a50a0a3a1a0a0ea4bbc4f1e19308799b01f2e4f2615a268eee8913eebf54884813ab2ec325e57dd92c85815342c8c8f5dcd3e8534fdfca3bf64257ee0d5180be13b6e4f55e11fa2777c1488f9c18e0ea80ca0cfaeaa8c8e76b08d8de67a04189ed0439d9c778255be130ca7803f829e69841eeb7fb69d6f8438d19a71a0df2071926f633dc337ca385ed08872dc447234d1f104b9f46f5dbe8e4c4d70f705a0a5e9eba3bbec274905cd222a604605531c28c239a2386077114f544c86495e9b7bcadcc3ea490096261e939436e33e6ce8a0ad313450e3288dbe6dd0e8aa2dabda048787b1223a03daaa0101e93392c1ea61e846284e602d0e897fff07ab60c6f26503cfe58592a90d01262b9b3774b334c0f85352dce2bae363a8bbd9f121867525e59389ddc6463420a5e429761cf2993232a86446a50850e472168af381baaefdde840118e72493dad8de50cd464a3b141559e130d8c94458cdd7b23bfea47c2b40e91680227965b9e1550805b700fa4b51abbd79658d124e58314648d9e0e87d8d3ac4a8b6142846f3f7de1a6f529e6334b86bac1b7ac66175c5ef36c9360021b3b191e5536dfaea1db655953487b15617a7427e51a432523fa011d5569f72a945bc4515563de3b8dcb84e49224bdab556cd34ade02775bc6e548536aa20aecef690e6555bc591e19f961a17b2c4f9033257f8abfaa49eb20239dbaa0d62dbdabfb71476cfdc163f7f1baca61b190cc64a9eb8982e9a346ff5f33cad4710cf474ac0ea5c6c6cf407319627a0fabdcdc8997e60646eb19181da02c4a32ec26bf8f5d8c210f12b888bc39bb966ded81b238a47fd2870f06122107213d2df13a5f52964f07ea74a9c7fbcde3f51db60bed144099c514d86114c20e7819c4538bcb65f6373d597d1a097d659493181c58de7d6181c0a3a513d60eac4484546a4a8aceaab221cb249f6f9ed5bf00043cd201ccb8f73303b63f1d73eceaf1cd3c7e17e54b9b2cc3621523397866252a463f51519e7fcf39cbdb299f68030086b9255ca023a53c178625c28c488dc4bf0d503b64510dfdcfcfb025be6e8e5bc87f33334ae5f360a08b648bd1cfeb5c0d450e3965b4e974abfbc6d8f47e16ed87da3b0c96e0da4451fd7af513f86e53203bd86fbdd8d16b386729e31103a9f698001e52df2ab5bd3574e6e3c379a4c5f0225ffbd39b4b7b2690c84d188e74f30c547a4a6629a5f998f3100aa1f183893abc33aa47e1b952df9c10f50447e76162ba50bfcba31c3d895c589058457059d6d76744840168c9a9d31e05ac2e1132315641fc5e67c0dfa9bb615748d4c014688915b726e68645d5984598330b5ca7343bb84810d679f6b5a4ffcc584e89ac52dc01a19a3bee56df7a4fbae1d4b8dc45b9191eb86c017d3a72f251a6144c26800882836aa4273410102f12c0325b30b6ff910e927137c32b0295edd700533b12b1b64c96744446f82d37d3cb1f72ce07374938a33d5957e8ad9aa684b61d357ad68e8f9406a7c5c339db60b6b16cd8eede559d8f9542e9f758743b33afb41a13bb8802149584eaa13fe2ff0a4bcd1b43f7413b67c25c567ef4dc9877da2e0735901ee59db25e42321075048d7998ceae35a97d9cb0b650e635110fd6ba982c41e1db67cd3552f748ef5e29e9915151e9346800c08b0aae2f3bedc5b3ee3abdecb16a4e2704a8925ab73806c9cd44a5952a57f9ba089256e600785eac50584398289dbd883a2f2e200419e40e236f220a8beb40b07f03ec5e4a421b1cf247bbf23e7575ab7452f9ef4c81bead2af6b6033a7eecf6ceaefe5108e75ac11bf3c6afe190ee8e33687baadd9dc276a83f1779b850f6ab855257c486fb36c81b7b511370023226e703468a4365d5d85339766d3210c2d71c0e2dbcc646678d41c6cf98a98aedd592ca32ff7377de733042cfdd048370eb2b3a6fc7d965af2d9722ff82a9a0d8d5717c5d9b9874de87f22fb1bfdc0a139132c4357e81580d2cd335dd87966397a2dee3bc49e56906a133deb6fadbc1fd4fe34e57c29e7e02e3376d75111d160a3350ec7a43ce8545554c6582202b0dd728c01c5dc474085a0ce15a858ef9863c4f83d8ec36bd2c65a42a3538e50b93621d4640fda74cd3803c1b1cc5018250c246cff61aa335dacf004c9920731270233407d8e1e2c1301bc93e9156fce53db7db5323c0904f359466681cc729e43862b6b06d16f7ef15b082d82f92cab584da54eea1bba672558a4f7c85333ec2c8d60be76279af0908465fed27760cf707ad2d32d32ea2558bdd9f350ad9ea5771639a5d703787db4dcfdbed7b21fdab3daad20bd673b56b071506a34d9eb799e665638069f40af47b747d3c0bce8470780988e49476ea2ee4526ca84a147c45b1a3e4c407d311da5d94f599e4f334d622e05a80fc17ccc87a10c01a2a4bdab8a92a9f101cb32fe8dd1d4baa51b26cc27c7ef19df5ffd20e7375eb5a5bb8a98719feee9f3435568472190a957c010a00c8fd7d8f47c2c6e6b4d27e8318a744169e52d797878491fde65ef9fc6d8ec7eace147efa1859e34536edbbbb14a33363819f1e2548c20746f808307ce9bcdf6235e3cfaf10b8360bed907395e71a20639af34d6d69dc3a4a9fe6b427eeae2a19d21e40acc7880b4f2bb57c99277dc873199b75b52aee5078606fd561caa2795d729bbf1798a5c3e08031bd29a08f5d8740deef195517f4ec6fcf6f667626c35248124fa6554c510ad4d77a0d5a609ab41de40208a0faf08b9bb4cc624a0e337174d2dfa675b653543b052a06fd0f9ba74ae9181176774901e2af2e618e8c9826bef694a2b5ef24b5bb8f01c0e8aa6ee59eaeffb49a583463eed20a05aabef59410fd2c6628bd5f3e04e22600d11a4cd7c23a68e33f55678974d00458c18721d0a5f4a3337311a0c15856b9e6acc761501ab08f49c8b0695372c4a00dddfa77110603b1f8952be07e8288da95e04f04d849fb40e74bac9774c1b13040f1ae39881647e83bd461daa21833b55003473d87b01655aa3fd580ed28198e6049bb1a1d802833d6d82e8c46327b866bc1149669741886fbbc3cbe79a909e99c2629c3e2bbcd7b7374a71b6aa1686f3b86e74f901e1536b29f3edeee95544cf994edc7327c371cd4128f07e43c28e3d085ed8a3848abf88c988e7cd0ad945f8b61704fd423100a995cc50fa7aaaa98faea63691ac5ec16bfd5732c005f66700d5a962a26fae79dc5e26aa7f2e02b55e86daba4727c984644b94aec02a19a5d0c2ecbcd4511a7c07c36c45d60f3b7490dc66dfe78f018959e6fa67839c82e348f55bf1079ea1a6c658e8289b0c2e7988cb2f96d5ef9e62fceaaaf88eab08d1df28024e9bfa60d7288a7216b40edee222f49d637919a5dc5f56b907d94b4059ade17f6c2451e0619bafe1f9e521f843b374466b533b69c71ceb587a6a2d307085fcad9ab7c28e7b8af8723ec69722fbaa758f55e33449c42c2b7370c5e6fa1c7e20b20b008141bf4e5b7977107c4a56e690256a9e2c9a4ab746e7cdb24a78383f589448c3ff9e230dc55cf3ea1559a5220a2b85e8e34b0fc51c025a9c3b31f314d119a0b9ad9dc07d707335579e451425a02265ae3048664ea6f649505e2b990ce9ff5fafc0ad637542fd83d8524ad34450aec21b26b2d1665f11567bf338f27447e0405de3efcd64ba1482e813ad88f8cc6aae468dec1056c75ebb5f46a885434150d9b7a4f89693af9973cc59379611296763aefead29ca8b33a2e54338ef5bccd5bfd0660ed17e07e71ff457dc5774c402647f3f4894f3628f41264c8bd0bd6676d8375607cb3a61997c2958862ad9d9a939a46b88336672accc62510fe48b8169e5e2b3fd56396ac1c2679efa87a8f6cd82e23a7a722c8443ff4e00d010d9a6f8c469dae80e4ff9420a62b68af27f0c8441f5de8a11091dfebf501a9e1334b926d7094a7668df0c4e685ab6d1eedf7b8532ef8017e05d370a39f71be27e56c6462134a31b33ba4465f35be7931eb570e56ccdeddd2d4a0e7853f23d69c8ec915253a353043af8109bcfd45f411ad5e6d368854340d7969729b87ce525beece6da9ee053ffb17a410cd3f2e3a2bf755b2f19b055b2bc0aae4cd85683059adda6942793408c362864e1554ded2bd2b6b10920b19b7ec620d0df4d9c5cc46f2ae95bc2f7ed6026a1bd17c1458582a458459ff36456bcbeadce36bb674370af7a959cd5cdd4f348c9b88056dd358581b9f399b90dd4870a7c530529acb14240207ad768c596950cea56faee6363592bd21cf1dc6bdab04fac639235ca2433b54c231fd774154db27ea38894a403fa9c58f9d3155f4ad38c16f3339fbf413d2b3e1d74510001aff6a6c61d21ecf72c7b1f0c02469d86e41bb6b61c76ba2657fa430554b545cb2e57d5cb0eb9dc7c638ce9949e37138830c1ff3d08083018993cfecb61edf8e4f23005a2f263ebfb557084a48c2dcd7819bd0145c890bb08d1ae7ace431c88dda4643c5acf79790f0552525a93d567dd2a0203a26a7f6207c4d07c77987bed9d637101fe2d46584c9e4038267909c5fe192c708dcb26f67b1b5070c42d9f9dd632e719e97478f2fb365bc8903cb06403d1273f7c33d076e3b8aaa93bd410a1e2b9e376519555cee3f2e5dd26733edf395f83993756488a9c07b3a421aabc604ef2f44344764d404c4bf808130f804a71abbdab711e6aca256a9a2109cceee5d6e163ea90c7780259b354442e1a0cf2a3fa165adccf66a6bc0c42585f7c2d5284c1198970f9b5c4792dbe9d7d1e248b5500c4ae4ed7572b9147c562979aec6fdcf51af354315f2136bcee07cc8b5243e6309744552341892012b1fd9b96300410640ecee87a0e5cdf7a50f4148309e97bae01a149d406ae13c4259891a76ee217e39f084248c163cd8c39112921944cefccc198b50b6c26e3d270657cab8763115a2f92076e67426be99884c36e09388f400e9a09214939c8ce089e25f0918b130f024f49676e1c8c1937d3336e1af022de786d8b575657574b2e3c4d4901d5ca3fc8c38db758a087dea8441a56bd7a4f4c48788321e254c2618170acbd77a91c1452f3aae4bc000082db262aad55e21831bdc02474f93d7920dc493584a4c83ef23ecb835f1c5d623ccc41851a80688e64402d1f62e2b224d49d8410dc5bc01a72fd96110a6d2ae36b397108a813c6df1040821cf3fc74e44f7f70d9188f49370b83eaa32de614bcb8ad5ba24a21e58f04750bbe430a86aaee9bd0d142d3d49e712cc2d0288ec80e2f156b46639701de130237220c53398af0ad91688e13513f9abdb4a23a339b7f127680221ff5482518770c6adc54a448770cebc21ae282e62c0d920c7cc183aa82a40b15b533b09b7d9cc807fb71b0969795d5ca953fd9699f0208236f45812fb9b1c8cb7cc461315ff403f592634ec5b5dc153b71b18ba915e2f6dc8e49780ba1953c2d443e130b13ac3cc5261526b48b5a03cd781535462713005ca111dcc5c93a84b327f2fe81c3d08de0ae92242f3fdca0845d23bf0a4423a9f6f583023d1a823cfb14658c138a0a958d86ee27669d0d90a3e4928a4a2849d234a398ef0997d24e82bb3e5f80df80a2f4874018bb8ea71c04112094f349ffb858b286a9b3f001601d73110446d63617b6333e14bc5c9a287584062a9bba4e3989f21872dd6d90a58c2426d248cb116193b96fa60a106698c54c2a176d727280604a9b802f5c8391998060e6d0905d464d7e4fe2cf98e358e500d5fd18047921fcd66e43e05dc1c53bd45734a1b4060961f36df140109e9414fb48aa27c4933944461a147290324e906ff66cfcaec9db3b43d954a4d91f2628c07a2dff58e2bd1cc2f9c1cecc302a9cf862a2e9f1d985b57a52805a4ce610a98b45ca0392b69c681ec3cdb8c442a922fa91662d19e7708d88f16b4adcc838f1375b6e928285f24bf4d1cc5a62f6080a529e0f3c730b3d2b5804ef0f911d63392933bbece3830270f0460629a7757e841ed550c1ae3ee93cd11a69e5ac2db3bad48b9d9db3cb498f8b9a5b572310743876b75a8e81e94a5b5fe00767174f823120c870da03ae0c5670b7867726cd1ac918b521a5f5c96a93aa48982a295d264514638017c2c6c66abb46aa001b335efa156ba5aae5d283f4e391e54f6dad323f9993dfcc0940c32ffa8b28392d405f4ce1c84d6e24e6eb1d0def09e607ab2e826bacfc5cf6ac5ecee4f2ad53c22e78468914c148611f388d821e4b4110d20d764b2b76e95a52f8394c2484a589a65a8f1472686913e962c9860f2491e000ed85a59f36d699c29e86b625feb5118260f7cbf252fa48afd5afb4d4461d53b4689530f0a756d6c831a097237d44c69a2efdbff38a3e7dcb52d68b59097f3ae9d089a4bd7859e45a26a7eccbb8b2c4acd665a764c54cbb7969b5596e06778aa102f110996010f94a63c50685cbc8b3cad9ce0a3abccea521a38322d74bdfea7d1b0f9470f22c45633e56b5f5121bba410b0f6581bf4b3fc038d4cedd4ca6a026191b72bcc85cec1652645b384ddb3691e63f6672f4ee317d077daeffc6f5a319c79542f0ccca9e981d3a0ee17e225f452b400740b72712f9dc33c7d5e17b10f312a506a86985f2b2758612596924cc8a1cf8db18dc45efb7d71521cb69964fb427ba9d1d430592485c7f8ee8addad1598ed52ed75a860047677567dd4d1749f7de6bbb3061299df88e3cc293188525594b3e6f758f01524c698f3daf6b2912ab90cb534139310dd9104a2f2a7619c22893a425c4345af8d2ca221c93e41c1667b7d0f5f78dfec688e269079e53785aab5a162e8b93f6ec0f5d6164b8fafb567a66cc20e116a314a5d9fb4ac8c476807b0bdb406f44f336801397f08bf439ff201b2d8df73c67fcace739e05644008adeeab74e6ef2d91e295f1ba139bead3fbf09390079ec4f0019dae3e77bc57da7e070783cd5e01eb75d8e463a950dee0513eadb344bd3ab9c23d1d79251f7de77b276caf8cf915f75f3da054970773e1e8ad74ce05f65313a63c331da0a110ac16dd4a4fe5e816efc80bd96e456e93f28530507ecd2cc6c3184ac9eb34ec7e7b1456cd4f3aec7038862be1fc2ebd0acfc53684f0a3ec218d9a74c0f816c1caeff473b9cbcaa60de0872e25b1c2315f885fb035fc29326111320afce80f7fe161b19740992a26f4a101345459eb95ae25651c1ac56e4af7473fba99581a92de5029f2ed16d37d3f8f2e8d189a6eb0657f3d14083fb6facb29f193f62b6c1b4cba82d12ab60858ec71e63071f36ae2db8cdba7f48ec18245d328d1efe07476609cde3359c50824bef4e8f79dc606f7651b56ad88b5ecc198b8173a7412663b0f030a84b1717e79052b29cefe2b395647eaddd4b500c70f4a697cae760e92179c154b5750de4438c91f66719b9a26c234a41dfe7476c2bdc64f7d2e3a4f3b1750573ed4ac833d5ba5c01f71f41b9ffc2c762ddd3d1466d5aa73cca47ef5a52539ed2daaf9c5744cdfa008400407a5abf741a55a65eca1bc922c210deb288d9755357b9046f26af74625b1531bb6ee82a97c8cd66e1b8e9c24f2a7a8c96afaac3f91dcc2e0bb6dd957795a394a501fe46155c374b4429dca8f45bcbd8eab56ab55ad9116d7aad05c86c0fdb0afae92580a4e126b92eef87bfe5e182911b9f2d0b1f7e28bb2d47a25f3bb7a7be7bad612e2cb874b3c592b10a5f1b9ba8898093ca74176b05e8eda097fac5cf1f4cdd2497dd5864b0f25877cb6cef4663a425ca247f0fcedd2011782adaa8d77ae04b9629d7488ab55ef62ef0b14accf20b419abd5b856772edcc1e0d286d7d6262bae138ca5970758d2443535b30986212e184207e8e97ad3cbb07a8240a82e55870ccd741d0d7911257839f03e391b3e2ca601bd7500eb34b18175c20716ac1faa27f3a20dbd69f5a0e688900b6490bcdd6ed4dc3d6210430e8fe33eaa251b45c196520b25a8c876377995d70353e23b1ed3f288d9fddcffb860e5e209159f3435b73003279f0b672927942e405176fd66dda446d8dd0d33f93d610216fe48a238cbc8c3ddf75ab14120ffec10ba6def88d052059c252fa82abbdd22ee34349b5c071eb5716c95a0f2c3af8c59f0cfc1fc52d9b2b7bf2aa010d0c6e34ae43070cd5f38d80d9e970177965986aba87bee83413046879ef4797b104ed37bb97be9feda79c8e4971135b4bc12099864d1298766aa840994ad643cb85b20506578f49a121b71158ad1d54dafb2d84f78a01260182a86b3f8bac09d9057f24db82c1a529785627200f79aa9f170dbc372df5afbd3d5016ca441aa94c63c7eaf6cc0cb7069802328cd68f2868e624b364cd1ca9c33c33662f390cd9f9de39060bb872bc55000126beaeefae78224098eefbf514d016e8f3eeba2d0546a7c804b8408d90dc27eecbb5a7f831b8aee97760cc8641a95a97f7760269a59021b15cd916df92bce9ce06ba1e2741298a8d34b805e799928fd3041b191c3f671eb6786e441380d3d8a447468c48c3a45db67dbd076731e744621360024e47b1ad725bd44fbbb6f7d9921806404fbb7c50f27e02ee72ddcdb4fc08a3abb0043a0589a328d6512888dc4e4735c8c8db038fff979384650f24f4a302209681560a1a5ca2fd29497a93eca1777d95f100eb353b5a6f6943d92ec8e4f26f4a2d650256483612a905a0c7eebcc01319a0dd5404470144db4a6591da4d9923c86d822eec491e0e36c89a5483ab9dab0af5cdb1aedb639944f601c73ec67b4439980d5d1b99658200ee9ca4dc5929d5cf06b7f5d63a5ce0b713342390ec8f4549db25810cae7ad4fe2ebc22c65db40dbfd9d6faf1d97056a6673edec588dae02ebbfa9f7a4276b7d9c1bc64697bcf9530d811227d3dfc76efdce54caffdb527fd03f6ff7155628ef375dd8dc45f5facbfc6d52a19705768634fa1004938c4db24cb101c5c0341a37cc1a12a47dae16bc2a014740ef3a8d651d42d2f55a3e2359b7aa27bf5caeaa06ff050d30cd690830b3b32db459f4affe05ff97f9cd02df0605f42dc688ce776d9a7ee752fc368f3c52c19a22d9fb61f2315438aca243b79ab085b19a383eb1af7ef18ed7b4b09201d25f0cb2a6d8b0d8a3a138d91d337646e2932747e3c2ba1fe132e9ae087739f83db99b0fe0f70f033815deb0b38e219878fd14bf07230f5bd69b8c3058854155286e9daf52343ae3132be2882e908f13aa4839bc1a149d605508484e605b3397e2a3fa944c2b3e150913c1a9336113f1f12d21aad596257ebf5a7b0ac4e17aa2f514b7f1fddc96b7ff9e710aba2db2e2f60d10f7a000f90e5526c9b6196b241e5cd1521b65344222305d34bffec8e955382eff8a7f6a14c5b7b17a4fe3d28d62e0ff9c9d7ffbdccfa0cdc381ddcce4a2ce3516696416c95481fcb52d488a265ebac175538af7e3d8bd68313284f8604d6858a1b2b905cb3e8dc93c6becb8d8b6246bf8db3b4bb52ad8688f6b83731258d4dd8bffcf6991336ae403d53bd51193870b1bbd83e9bc97a3c35324b93e4265793d2c82c09ddae0ecf4ded78e495239a24b0ee787bfe3ee78cbc7673cec68cb9cee4b79f7be6e723605d52629e4e883dfeff01c00768b92f74693bd03d1f2b01b29c1defba3b78b966639904e67009c388f19713603ba5cd0fdba880d8892d43263de43da6680cb975bcd81207166595329012865ac3432b5cdc5c5cdcb9b9da51d8d295a1c13d2f666627b8066fbe631b89d6b11107607b96809ae05e3071712032a341079a2a79aa66247a316caad1ca179b0bb4bae4200e933cc7a6fe0e1589096e618cda723d6f57c9052ba437af02e0aa374eb182429db0c752deebb5baa4db04f76f695c16e1dd1d1a81e383254cdcc0304807ff0915167fd78322c7a82f6bf332802d44543f5d3e5c952fd9c6bd9a1728aad07e87e60eafb74d445fdf1f5f24c187c39893fc0bf6451a9a06bae384cc16fe4bbc80ec3538ace260eb362812f890d6ab66625cf37ee3fe142c2f98ffcd53821b017fc2d8003a06c9de2c0823b0b1da9fa5033f3680a82ed7444857afc91fae79df8da82e9737dc2fe4ca1d1c53ac19fa7da14ae49c2f4961151ff0c742927493a637517b867bc12adccb8df8278c3b241cb7315282c1765010967aecaa841ee473821b9c712c4f6e5bfc32fef9e91b0346a4ad3751da43d0c88df01af462e06f42d92b92deb4196c51dda339325d17510671c1bd52d118e971a4a09ca16d54ff2c1ff181b1032e57eceb4603534c0f91d88d3950e3d4e50157152609ff7ddf7874bce60761751443fc6e1080e53d3c0f892f40fbdb96eeefd897825eea98240a25c42100bfe026a611817cc5e41a87efb00a05a2a8b741aeb6507fa7d10e8c535680aecb8788a648168059593ea83e5e0e16ae7484a0b4fcd89933e23fb8480d67156ada66081409ee80ebfc558a0eea2b3bf85b817ee2eb42dbc02e55023cb598a52ea98e8adb25db383a1dd167426db5ecd36cac74bdd6eeb1ad9280ec6f40e8e9fbc3582a59173131317ee052210377c674778fdd4f3ef17922ebfc24dcdda41234e713e24832b2e28e32726242215c835839bd0c0d64523982dbfaaddc2e0bd821da774eb3d7572da4aefd1e2b8793cdcc1f6af8b230f77fd5a5c1d93148b7481f4824a35742022b98b211a5a9652f3bbaa1d53f3641a86e7082ce45a6536621169892cccb8ae96b79713a7bc55e8a01d0b0ba33781753883a74e05c8c2957a6312552f1fe219a3fe21bb89bf77b6e54b036186384292eba274a2f38c5fc1a567d25fdb1fd6834b741c19dc1add0818d958e420703f155628cb0aac4c724e37bcb409375221bc445ecc581f2a8880853a927e48a3e900e95de27ae7dddda11159e463e139c7857aa1931206ce5b21ca48b524a9e27cb79a650131b1265f2580fc1a5e1f834f7414a204d70346aa129ba3459746c5e813daebbdd6d876af9a5d88179cbe2be3e9f709a51c43e0124a1bd8693bae8dbfb004e2d447075e8f49891b74318f6ef4b341f31ada4e2ddd5811081f80215cce9fb5a80241852170d4a3893ea1af4baa9cb85a184117468cf9a3db29e1cee3800776ff2b9a62a87bc382022875327130aacbb0596d4103b31222297ada055092fc6e0b756d3ba444c5de23c2782a9511f1e2bd6dd27b9ecba63e2127bf796b8857504c950da49a7db1fda1e46336017021c59476378887947460a3abc877e7bf92e0ce83094aea9e2a3c835dc6b80dd9f2075695835b12ceb07cd03aad89d4a1a582e56fb21bfb5a1a238f12683b5aec577581f2be042cc97365403d9da50a11f94831d7aa154c772b2301ba17cacca8ef78d2806b31b391a8d4f982b366a855a85f72b9f9030a3b46e1304998e85ee6219d787855c2060280c4ec6ee61df0237388f74ff03ae16fddc93b954ea44ab247e0c0ba607869243caf17834c74b9c0d1d5d248c5d1683663cb9210a326570e58e389ca396042ad4f71cf480c0c7e92608fc1c71530b3b7bb279b572d6be8bd441c33dc0acfbbbe58affd764f8f4b2e2df2fafd8bf248601d77e37996222f01e60b2b60f8e8674dcc039476f47bda4aff7982a52fd60129533e592dc7732ae5fc6bb3e76999eb10e03ebca7e136fe005fdca7356cc0f0cd7ea92d1accf5966cdfc4d040e8183e3e57bee1174390a89c7f1bde0bb604d1d5992050cfadb18caf95d795272d3b0e60aa1c7a6bbd3d51503b8fbbcc84cb28efb88bc7653d05baafd0e2408d57c797ee7e8ddb115c9f114b45f407a26e68a2d65c8ad56cefeb65cdde1a8db61aa9d05454c9981a7998854fbaa7df9095a4426aac0aaa8e7825de264f31fc6b443d85518b41c1342a930a3039c4c4064e055b7a40829768bd6b116069f5149b9bed919b415c538c928cb4a7bcfd11b662f1552b8d5d829b9f74903e8a8f888b98ecf30b59c1d78a483d60c0dd43513260c637a008cf81992d63b7dfbdf0eb9fd8de10af8fe32336c60047c3f39337670225e3f99391b582054e98adb0c4fc8ea5d8df744ec90c8ddd9848a1fe12100508ae40269cd1bec98380485e4e6a04d5b623d2dd3a3e7351341940f68b8ef5b25cc791072f8c00ef969a98576b7fe2b48b854f2f5f48e060516916f339d3a5f8320d4f8e813b546dd2ec02791fb637d157f3a9ef3e2007399d6d656d615f063902469565ee848dc5e593ed8b61e3eaec15f8c14d79ac3c5d63371c57e85776d0edf88c181aef75b8a5c0c598d71195fd4f2181fc9a593946eb805da334b6981e380af4edc89810c44031ab203dc001ad03830972b78e1263dd62aa8a7bc93d6690a49baccb1c5d907eeaeea4a926b5743bc638a0dc6936a46e95f976b0a1cc85c3392dfe42c1875ffe555169e09d12e791328c81cb3f12dd40d8c96c1e0cadd74537c8944fdb576badbba24559182ce31838fd088e65e0bba37505b4ebf61667542da4652a8cd9d21c001974cbfbfeef98debe830fe3e0ac1e4148d75c9d63d6f40cc0164304a11ca38661503d4f6eae119a33f780434fde0f09e0345b09e2c63d7f5ecc69f9634c9043e7904810e5b20542ad38c01aa240f07342ad28555ef241ef6aef15d176fa9225e9a8ca35ddbd237112958a5a16d78cc6e7d27696fc89f0ec0008c010f182ddb6aa97ca76477b9180f4bafaf995bc8c588284281506bdd849a880809d9524a2965420ed00d750f37f3e028017b33ff8df4c8decc7b235d626fe6bb911b77d89bf93c568016b1374ce3571867b03774a44df6667e880ac1a959c9890e4d4e7278128fc4a971b6086206c648e2892352bce009a322d46842ca08ca08425254154e34c9a214dcc52169df7befbd320415268c6a28620a12e0c0434aa31182156444c182080bb620efb3931b66b0c33d9de050b44bb19e6a57a77ec171e2b4475a789ac65de39cc84006582cd9989218638c357c45bc841495c90ee9417b0c9493d9d316edf3926dad9ddb55755a5567f628ebafbea00fa77aa10a223536777da91d3f1ffffee0967da2c66e8ccbde3ac5394de3348d8b71cde76b9aa6691ac5fa13c2b9c8a3b38ac0f3a8487deaf52ed7759ea7793b2c17abb18b8fa3de3b71eeef3dc29b55874e3aa758efc1543bb630445d20c2362dd77bf49ed669da3dee9dbef3ea689ac6d51334ad565ad24e6b9ac6e50d6b9aa669ae1a837d1a91cde27c35bd3ad7d613b879effcad31eede7c6b4c3b6c6bf75e3f8af7d5767bf8bb757bda877bdcb9a5b29e634c8d69738e65ccd6a6eb630913479be59c738e386a4ccbd97af6f6945e4de9f599797a31f0a62e79bc4ab96975de388ee3842a776dea82f8d3bcadcb1c97bbcdd33e0cde940dbf5c756aae3a34579d19a6e6c4c994520d7f38766c83edeee36034b1c62cddb6ba8557c4b295e96e559d6b1f8466070d6daa5ff9540bd1bd7dea923ba5dff4b671dcb671dcc6715a29643d8e7bbd3ad772dbc6715cf78e3b377ee2740fa3c4eeb7c63abc6ddb36374ab106f2a0a7db174689f5b3c6bc59edc5da46674ec126cefc0c7bf3384af14b4a29c518538a5f524a4f31c6c714d34d5ff131c618638c31c618638c31c618638c31c618638c31c67847d5b8bba9cedc37fa7975eee6cee93c693d81e332f7cb715cd61b1e4bd8b394810a21d2df4803566a4d4974cb2119dd47ed91b63d24432c715218041108620f4d6c19b6a629b369dfab4bf000967e87c5e30ffd6cda76042ae914ef56febd9c4fdf3d3a85d374ca4332442b17c97cdab4d6b43dc24210cb6b8f76cea2e872393ddd88ab887844b4b50ba5951789366110a7530f44fbdb9e823cf4bdfd0efb626136ed5bc6f0749ae57c1ae7d32e2d757a9202b5c183f52b88942211c37236a54a1ed0696f9a878e65bd5ad57aa4b0a92e3310b385f40f10adb2b1be3869e2545ad20d146edc13e4e2249c6477268964095393d393943be4d5b0c89b302eb89092d24c2c130e2e6aac9e9e06e12020f041f7578b1c39b468123ea573931acb8193a1d458bd3aeba0548aeacc546335a57313d48d1b23f0e07f23c70d1d3746d800f0ed34546e60fc991b239c5e26e7dec768d9d24f903d50cafef513a469bf2101d441dff71500361dd47567e9208e7b0bc64a0ec5c9f92c7ff98328d541db761b777de927881e28c5a4977e28d64118bfe5eaa07b0fb33ac8dadb9cea20cf7b0daa8334edf42edf98549da0544a6f4c9a0897a4be35517582c2506f4d28c948c26f4aaa4e9028ea4dc91149df8ed0db1179885963aa6723aa6f430ce18391aab319295263e3f6b469b135d55893cd6983326e52362ec60d6a356e61c088fc473131462e57266ab5ae88a295ef3323e72a35568328d577a342b72923d30fc09719aaca2eb5a4f1e53685bedc8cd81d2d67d188de8ca4b4912aa0d69c660ca7cca578e3a50b8cb983a3b7293b38f4ac37a997b907bbea596fc497b98c5dcb8cc6aeb3ded478c915ed7a98b00c11887e4d104b0e96a096961170825aad9c209812ec7f0ce7350c9f52a9725a3ac21c2c4b3f2dd7f9ca46869a38368e630387cd08ac5bd8a9ea35b62944aa4e4c07c5623a13b1a1836cd8a8f12ca5862e9f8daa8e8d0eb2b1d1d9488b2b5507a6836030bd5d8132a5eab4e8a09616bd4d61a252755874100b8bdea83031a3eab47450aba537336a2c56cfd2412c96ce4f35a6d241aa03810f523995a20e0a0f04ea1f201a1514eadca4a7846588406a0e3e2443048d3019994e3b38ed1aa6ad89ca942a1c51dd29428f6119620a28f5b00cb1c79c38255866a6d448c31f88f9076211a82275db9d0c556fea6bce4c3567a69a33532e32c3d8e5d6b44d2161975a95cd8856454bd29c5abbc44f569eba5d62228c241c43c4bbbc4bae947b748f2e91abe41ab9435c1fee11b7c84db25099698ab545987c68c226d92396d8219458234832539d52a7542a9648b552afd8a36a8635b244b5a94ad374da216997d54815eac49499983841b152d2a4a42368130e89bda9b409201a2ec9de5425550728cc56ec4dc819415275806832d4942a963271441c19e2a83314d3ae870a0347088bace78c6ebe1fac529fcda829dce08868525472e0cfb07e0443e6787dbe32824c3825c77d4c0bc7fec5b2f4e31d46672b5507886684175d5af1472fe40d2d5bfad9eed22555fd47bb8bce4c55a76cd13f6a8c662655a764d155a76ce92655a76469a7aa53ae3494aa53aa747eaa3aa5a8b31655a70c75965275ca94ce5c549d12d49fe6f2a6e16b6bad95b3514fe841a5b141731a1afa239821b60d016c6c5e52223438e625356263613534d9a6c64c816624e75b322fa279216f5e63e3bfce0100091eb0b9a6a961034000ee1ed0f1344802fce8858495f0219434e1d74328a9f32194dc798c1e42499e0fa1a400be9b791879f38d0bcdeb4ab2a2f008861c6148b2e7f3df4ef8d10b790a14a2f0cfbb6e0940800f40860004780a4730e48c5e2a2d016e68c8efefb80adf6d62b89b97132a851086bc397a2165ac96917939b9a0f6d40c4d86c586e6e5ae7b80e6364034387331319f9691da03b0d778e9528966e64a367a3c8c2e5f68687ed7cdd4658bcc59744b97ac98aff4cb1254ba146d1eea942e9560301b9b1899199acf5ce63157aaa173e8d0a1c79bc76ee335405d7edac379399b38fdacb5f158db8fba066673d86bae3ffea75a96e338c7f1117e8300e38f9c3df2ec5021853106095460869e9186ecbdfeb3afa9e357f1dbebeaf9f530eeb5f5eeb5724d28f094300a608c8167fcb133f2e88c39269430ae20017b33030df309679480bd99c1d290c333d61857b03735f6230609d89b7a3d56609cc1ded47fa461cce1d91156890dc1541621bc09a361967023003746c83102ce08de7fd808b1df8c30c2d10b69c28dee343f7a2149b838df9a356ec3047c981fc1902690c07de625f561cf7facf918d86de6a709f72f25d4bb02307e92902f0380ed31d7fe02a251e508713e977e0dcbe7a885e81e4fefd24274bbb47879f97d39a5c0c01cbf9c5cbc5ed75e4ea898986f2f67183232cf2fe7949999732f27151a2d4437cdbb6b21ba7f6f3ef67d7ea65ee2c8617df5b20496b75ef2b8bce5250a35d72f6793cd612f67131baff1f246c7ec6dd418a9cd2854e91075861ab3d7233522438dd9b78cd4870ad02213182911098cf40802d4688c81128d140a75a24d68d34899d4987d1374498d2919c78f333263cc0833beb8c6900c312443accfb971fb1246b871e34258e40b3858fefd08e647d501a2c1615bf77ef4b2e3f605c88103e7472f2de860bdfb110ca9e3f628e008affd0886c471fb1574accee5501d478b3c386ceaf8472fe4085ab654d21ff0f74730e40dbd54feb4dce5f61f787bd1be15de6e1775cafcd56d77d6adf756f99de52e3aa6e759c6d6f87135bac6d6283aa1a0e1f18edf908d2a4ee1cdaa13342735a34a954d85ca94296184b1a1a0b8e0428a14fde3478dcd4381e2e4d4a449130a28f0f09450420e8e3d1f8bc56030d8ff98981897cbd56ab54451fcbe2fe77c1f6475903d50d53f407307d9cd062f9c38f4214b688658efa4ba3d4d5afd088654a92e84456ac1a2ad97d38c14644521cb8f60c2a06989b72f555a3f7a816ac92d2f271516c77a39b968e958bc965efaf96ecb398505debe902b2d5bfa499d46454555d94730a44a2ffd84b751514578dae59492c3339b9cc2f1c7d4e21bbd30464b858ef634d89d98bdb1b712962102d15db3e6b4e127ed4ac116ead629965699564233c4b00cb1477d10dda22e33e0dae2abce00cd16b5904b75fb1f53cab6130ad4e59cf269dde97236b1cee99267dbaccb9c6d57d80149bd997b5b618725f5666ebbc20e4cf5c6b5ed0a3b34d59b5749d5fc026fba3e7f01e0ca717d1ee672b94c088b8a95c3464723f37a116b3edfc25133c28f5ec89acfdf05473f06a6b2a872b0be1a12f46762461b356c2ea4e6379f8fc1c1d19f8919c1bb6cdcd8cc67120090a365427274c46a5c39c70d97578e1b2f171b969c50c76bc074a46235638e7b18574b6bf589a365472f240e2d3b822147d03221b19a91e6c6615c2dadd585b0e8cb84b8b0a86ef4d2c68a79b7a197becb84d4bc865ee26cf412be109ac3f492bd4c484dd5d71ff5ac26d45fbb68f452a8b316ab09327a29a5c5189d5fa08e81f95c7abac058f452d697a59734ddd24b7ae96ab16608ac39082f27e6614b5c5d88b7432666465c5dc86b82572dc9c0dadb85b89c15ea257b212e517471b5ce025ec8771891ae529f8fcd749ff7d785805f7d9e4788ea319709f90ed3ba4cc8eb2f9f5fc1eec8847c97d14bf93221e063f492769990173ecc8b5edaeee22ec4e5adcfe7d5591792baea32212e7a4988ca84b4e8a5fbef42c4b3e82519582ebb231b42499990965ea2dac5ea74cc4a2f715a54650d13b7502fc9c0da9a7efef492d052d59f90d61057ef929767ceb5ccdbe62f6ff7f638ae96d1606fec53c0f7472f2407443383bdb13f7a21b570057b6343ed1510b9bce91c7b63cf636fec77d81b7b2dfb616fec63b037f612b061154a442b668117d3daf4421a0d5058858848a6eb5840e10ec2b2bb0bdd4de9b70202b4ed202e9b76a00a0810dd415cdbbb106db1505d8a0ff2a2f23ebff28000a9b083b07677216d534f972198a2ab1410209a1d8465d30be1ceeb74093e08cc064a5dc86e140cf2b23b20bb83b836bd50dd5d577a0f02b329a59f657719589bb5ad3495060ab178435b53b5821a28fcaa09347f59cb727e90975d7a5a495614ca6efe110cc93d08cc2e59bbd340349c9615d14445459159cf6a02bd101679f4426e5a06d3b4ec58cb6260c8ab65ae6a02fdd10b69b5ac55032f17291e7d2ab15c9636323262d1473c248bc562e12f6bd7462bd0132f556923154be712c29914b59128c3e5cfdf2f64b1b48df5213c9f0782600a831fc2d3ea52a9548b4d7d080f99ba1006a274f42259ae7414235bdac86848265974abf5f0f3337667ad74944386baf5213c64eb42782b15d1a86d63695a69058220f8f91083572ba952a9540a4810bbad562aa25164ea4aa26eb55aadcf3f0cc3300cc30fe121c38ba2288aa228be8ca9542a954aa5fa101e52b563646474c443ae7e142357373a7291ab17855632b9ba51688588c845166d578888c855914d83885c7d080fb9a2993d40f206da7610bb955a7a9e35ae46d5288ee138ed8df6d4088e221422dde594f2c519bb3e064bc3eca144bb7e937942ed79803298eccd6cb237d3c98a4fd62545d3e5a16c6c8acd39401955eccddc567b35e58557bbbc51ca8363df5f8067ff50ebfda620aa5a257ef770fc92c4ef3955c77b7dbee2b4e088a3c6eeefe9588d65d11b3d2d843d101515e5b4f1ef057836d63f575b7d95eeb679dbed6336dda92b9581376d88e1bd175acc856018be6ced50277ca84b7cfb908e1e0e437d41fdb56a6ca3600ffc5b6915c22cd05490562f3c8f8a734eacbfad6af8f44e8f563ae79cb3e680677504163df0b826ce18367bc0dbec19b3e129a554a3e1a759cf03c70d1cb591def3be6dfc0e03f2e8c0ef20c802fdee516d9cdbda6eacfb1b539f3796b0edd997ae186bf5b43b2c5a5c1d695d1b4190474ba585ec9eacf11347fb8a09318bae1ad32e8eda53de387707f2e8f4145361ea7bf8dd0bbfbb27f869babdf0135b35c68438e93d16ad87f68e7b15bbdb2c44852c0b332a2a2aea560e098fd249bba042b527942e96a07441442db2d4523b034b7fd012ff07ff62ad0b5063da1d0b00f258c1c7eca177fca17747f6e13ddf5b8df7b0aa7355bfe3dcdcaff7170bf7de59f7bce3b1a4bbbcff099f595cf05ede67d5ef983af87cfadd822e78c7baccbfc7ba1c4ac5b41e62d659b8b3ce855f3dfc62aa4e6eb96df90135f74febb2e3f2a5d5a6ae12517b79f77d0e0e6d558d07afa66878abddf35222db6336a9addbdb9a81c317d2360784bb90b65b9f200b1168dd731848cf8903df310c63aace8b4eb085dbf08ef5302df6f66ac6be7fb1507f5987528330f67d7dd8b970cf43753b7eaf23f898aaf39d722e6cf7605567eea12d7e47ad6fd531f5d6f8527df5b9ed5bdf51596f8d2f16b6b3be9da55faabf58583df5d5534f5d753b7a3155c73b65b1c7f8ab51bc6aec31b4454dcf32667b437f521a1c7f2cbed563b995f8da29e842d69a8ec98038a1d8c068830618aaa2fededf6df5b759353b79428636b7e9e985ec6aed4f59d031032ba2b60cef8a65185aa2cab6fa0297edaa95364fc8a61cd8f4da52b6e905661858cf30b328b2afbec0cca2c8b65a49362f247c42420511b969221d33500111395f7549c3041d20da27af868d2fd1bcf37306575b3b718038d2825274c4aab127940fe060aff684f281277becdd29561d8beff10fd6e2d577f64c4bab0d9e03c4bba71332986c7a2f748088f77452c5d3acb11a5cde347c6dbd8068a3b8d933adeaab31ba67530e53ec5d7d0066349408c1b3a723942b56ae0533108dba62b43242c7276804a1d65ab1b602e3de7bb5cddbf3091a52344ddbf213349cbe3d9fa0c164e22df29cf33b5ba63dfa18b77d756df09ee7b445a27da7ec71e30384fbea9206a1bc3fcbdadeb8ed476f2c3d0478607b40be07f12ec4b2f3bd2983389f1041ca16f77c4204a7cde57354d352794b95427483a7af1c5407fd77ccc67123265603e6d29f69c5c0b8c496d6eadb22d5419fa23ae84775ec8eeaa09f600fed02d9a8a87da98e5da98eb9738eed27681cedf9840856eaecb1dbf873f56b85a95f0698dced487705796cfa65ff83bfdde26dbbb66921bb395dde07a17b1389f61589b6366a1744a27d27d541b9276f561d4f071ccb9f0eecd11d5f66f6d0bd3d3bdefa3c561ca2bd0151a0ba9d8c8068fb4c28a2f94a95e5ce3a48f6418940a04b4454c416caa77b822cb8ecac4b0db8beffd47f75fc9a00019a558888b6d06a83b757e8dbe07395bd01a9b0ebc4c91648be90b6c12ff60644b3b7e71148854d274e7dcd806b0bb9f6a6e7c4d1f6f4dfd8e39e1b270e7d1e65268e1d873e7ab5f02647812891d52ec657b35bcd41b8b2dbf8a510dd40d60a512983b73b2bfb4fce98b2ebd17eb1508fb6d76483d1e6d9f8c91952f6378764bb76f70bfa70edee68d7a3b1a4652d2dfef0586a1bdfdb3e3cbe58a8477bf6d8a020405b1d8386367e0aec518982d88d0f96f8ae6e4561764a7d83ee8876bef4b811c8be4e51526354965469c20c262b4cea9526a226d6a8ea000112c4b5f183e48d0f4448c8b5b1d3b6f8c8ee18191915d154f1c8ef452a40bdc8ef464533aa898868c808e4f7212790df8b78888888c8234c7eb7ef11f15d10c00fe5d93d22cf2eef13951f7c9e3d41167836d6402aec0ba547a47b360d19629796ca24efd37dba4ff7e942a926d830768fc8dd3ad99d212998e8c1d3164f4a507440dadb29f6c8ee4cb2044a455022011852e4000864699f8e7ca0063b5c7114c511516469a54c1b546182043b5c0101134990a585caa2ebb11c7bcf035d183777e9ddaa86b58b359cef76459bed2b57cbd954761ed1acf7650559a8363c218291fd7db74dd330bed7da5a6dd825dd1387ea12d7a38dafc7fa66dd59ea2ec802fec1f76580597feff9a85753d9f7f94eac4cd9570bd91d846e2a126d3a8a4496ca79705ac8c95aa86d4523c46db354b6bda552ad19d68abd7289ae51d539ba448ef61099efdaa53844963c3f32d8ad3d689714b880cd4688e56cda7468bf50bfa3002bb400017a02c545b8caf2811a1b4457cd539d31883ff794a8ee1925aa894df73cdd993bea0e8f69c197f669c47d1b8f32b97d8ea5106e1cc2436e41eec67ff9a049f6157c69fac7f6b95f10c952fbcb47b7350d14ee39839db3cba16d4f8928d1cee3b437230644fa49896e124c4e4e4850705284914dbf22a4ec5c0405cf3b9239ca44aca75288f9dbeeb525bd9a18027cadd562f16c186f9aa66d541442d4156b9aa67d7a5aad746adfb4d77baf76a90c43987e39f03a2e6f1abe7bce29da97b5d65c6badb5d65a6bad5e4a081da275bb410e1162e972c51cf6fac17dee6e54f2b4d1101e92bba78f32d965ae1bb7736356da6ec7728697bd6c36911bd8be81fc006c9d7b6dfd6bea1ff9562b609239b8de27a208c4228e2a1199ef4df02893f95529bf9cd7eecdfbb9d1b02fd626fd11aafbea20762b6d603b6bebdc6bab8d8a3a61523aa3a2c8ed4adb2708830ae4a66d199e47c539566daf1def9d2306817c74bbea206f6a57d36ca55aaea76478396f9ba6617cafb5b58a4388f4147fdefca14bf10b3cf148e628132a85485f8a799bb3d671d4e69c964e9c795b2d160fadd5d22c0a21d2e789335ff2ec59e6ec291e11e9a9b7690844a3f6cb47b78168547de2522b8438e79cf3a342503ae7bc3536399aeb66b58bf1b5958e80d211887497dfae3d93eb522f1f5da674ce39ebefe7ede9d49a18027cbf3b71f4a5ad164bb4541442a442ac2ed79ca73a5b4a85486fadb5560891cef272a9970f6edbd36d6bca08464f689911a0fa70b166c50f2be896a46cede10536586badb5d7da56cbe5c319fbdbd3c90f50ecb265b7e89e3675c24f73e2d457f10651dbf45ebd78a354a3df2cc8026d01bdf7de7befbdf7de7befbd9f4e7ad08bc416038c1f8cf1039c264f9a604c91c01a8846617aeab4e7c4c1f41376c521a0343b278e0bd469dbd268d29ab41e88ec328b382787a7ead49d93f82b549dbbf15bf87165e37f55a76e9c8431deee64cfe3c3701ad6da5bf7d4aa8605a574cf536d579b3a9938ab8def0dedaa6973e2d0cf89a35dd3a5f6794defb07356c9353c893988ad4dbfede9a4872176d5f469e7e4008010fe831d50018218d648ad43a8439838f53d415608215823b3c71a9951b6b64626ce0de26a97d4083d2104554412eb35ba63801d789893ea18608710ccd8dc656a57fac49ebb5c6d03ec4064c6e80906d8a1c8d43193be1178f301a27d2df3d6599234451cc4e9840726db090f4cbbdc76e561c9269decd0b4a9d3b6f742277baa4489f6dd2e50a77d35108de20104dbf4461b884651277a828824ce53a5d913448d36559a38f33d414820b1ada64eb327e87eb68062317ba8d3c419291613679e3a993ddbecb11a286ffa6ad1f0a6dd7979ccb59f339fdde77754c0eed87cc27c9e32d99dd7c3d4fff97dd67cb23e1f83dd597dd6f83c45627744d7f5e769b034c9ee8c600e5de69de32ea3d6b3116e1ce72ce3a8677a3cecaab1868dd778ece0783dc3c171967b638d9ec1fe9ae7d146cf70b07c846f238c9ed5b0f9cdb5f1a56baeffe33146cf60625ec7193db379d16ce8659cbd3e9bf6a69c4e8a24ed1c336fe12065661ef3709ccd68a411c819f899c3bc83d1482992e62f3d7b79f8188dc442ceb6c3dc35cebad75b678db357eb314fc53cf59646b2e4cce5e1b97116a39170c899ea31a977bfe32ca5910640cebccf741a29869c7587f94b23b5c8d9ebf7ab7176034723bd90b37c98e3d048999cbd6645b32172048d44c959eb31bfa191347296facc5934d28a9cb13c7ccb38bbd1482f72f63a8e69241839733dbc8d7fe3cc8646eac8d9ea3135345248cebecfdc4623f19033ed3017c7d908d34836c8d9ebf6d74837e4ace521f79f8eb31a8d54c999f89817cd8648ad91522067e167f48c6b9df5d555178f44c9998f1a69879c7187d133982351f2f8f7a719672f3d43a2e4eb33dd39edf432e38cf5183d7bbddc7597b79ce5489484d1b3d96c163e75f0dfbdefb03946214dfb65748d317b33ef32feb037f32d230df6669e659c678d39f6667e35c6606fe6552325b237d3decc87a3686fe653238f0da9d6f8f23ea3f060c5c3a24aa5d22feff38abd7d4b4d6b7f3eaf356573a7fb7b771fa4ecef19c50728b6a771a3f72dd43925d4d8eadda89da7ea889e278e3c2594dc3bb0c7778e5b61c7a6f8e3c6594fc5ecb53e591f37d2bd7a275ea54b106f13517c99935363d613bf6f5c1d1cedce9c0bc823bc87bf10e4740b436085f1340bd67778cbddf77d5fa74b3ab465b09efb3e9047fd6afcc15fdd6379add54a6be958812008ae5abaaca5108ff555b01ee4913a057be8d873e27475779f5910e1607b14f4a9fb7b141f8e00bffac1afd557678daf9547c183c75f753b5fc3e0f77d1fa8b97f87d5a5ddddb5d203df3bbbbbcf283e34b127e8c3eeef29950186760defcf96536d0b43558cb4c7466bb915d1e330bb1caa62823eba5dfe381e4bcee3b2c6b71967ed425ac659cb7a4e1c5ac5f4b8aca7b3caaac2fcc09bb7d6da496d91a32bb6926096824d0131b567911358cbed19a508124017bc463608625340f48a3ced32cf9e222bd8ad1d782d6f7e04126714225776941c86d8f3ad3da3a491b4e93f027894524aa9bd4994d28ba35c1962adb5588b8246151b850895286838d9288e8852841e766b8c5284a31b9e4165e2798cb52d4a0e44b452567ac032f13c2a4e4b7503a2aaecbbf175b6f9cf73c5c45c6cb55cb3a7fb3771b22ebf4d6fa6f41574016ffcac85eec6f7fe36f9b010ddd86a41e4492a8a0ba2c85188a88003f78c42c4891dee19e5892cec6bbd3dbd77ce596ab7afe38b9bafeda5cd5a6bad5e688bc4b969cdc188c9beb7ef993546efb13db69ab518d38b8164d75a1a78f6db4bfccd8e2fbcc36e9addae1ddaf78b321b62e3577a42692f7477e909e5cd3d6adbdddd7a5eced7eed1fc09fae077eff4cfde513dfb69776b1bf55e96ddedb7f1e5e5abfdd569766f2e5c20bbf18e6a338dda9c8e6203939d0f608ff5ad1a0bbc20d1556374baa04055d9a52b9ca73587585fbf3da15cb9b2eb6fd1aecfb13b3b762c597ebbba70885688b39c4d2d404086d9634bd7de31566181e71a22664f285788583e487d019302c79e50d00041093668b520853da1100109660c161759a49abe269048193963c602c297839728304cbcf68462c5886b4f285688761114d74b6a6a37783944fa09054a8a5793903031352515e1ca11a7b44e4beda4336f594f20d5c35f109a8da701380a1a6082b1e7bd1f7b9cbddddb3e3be297f7b314b25f06d8f839bbac7bfbb4af7bce1326174bf6a6cb9b4b271188b68a4988e5e4269412bbb4303908b06212e2a6cbf27e5a7085793590ba95b88be15356accb217005d5b62f1838923bdd150cbb2708c3a6833bb30001de138441cba1086d4f1086011855b15d1c28c97b8230e898811530582e7ad043b72708834dd2095ae05533566003177c7b8230c8e450021f8a00f70461a824302ac3491530b478b144d194704f10060ac60978808410515124f7398a7b8230a86ee005126914c996ec39d5c663a90161e16e7cfb570fbc8168d4eb471125b9e37d375002ba73071234b4b72b60925e8f9f9c7aaa1b3f900395beefb0e08f52f775dc53e0edf81d8f403c708f1fa5d4e79621c856ea74f8b9bf771d993aa7893a123c05629f80f0dc3d3bb270836c25eede0461d02051477e1a8c01d4441d19bed3441dd97d47b5230b00d8a5d277198686d84adcf7efdd84214cbdd344330c12dcd5a9b45be9480592fb9c3d2dd400061367ee166af03475cc3d75792be843fbe4b8d910297a60c905edf3e00b4d905df7c3fb8eea82f6180220041492d3425ac801134240c90113424421b90fed09fafc28719f5b860e6c25224c722d5b3b384a609c39e0bfcb764c7bc3cdf9247e92df4b3125ea25fb0f609741585b86e90409176ced3ff3da654b2fd81b2d9b414fa84ff23352a52b27141b8a6cfcc382ddf341565b863bc50aa25d817e78b707470f78077f14cd86c8214bf84324770bb29002ae626f8a74079f64f4a9836ae29ea068124da24b8ac86643e492fdea07716aa019b54b14f64f10d69681c6091a2c215b7ae17bea44b24aa48b7c92a0955d56a24f1b5592fb243b3dedcdcfd4e53cd63f3e437b3e486bcbb00023667b7e4259a2ca2e6b91a95ff31a80b24b0d3cd9b38892ddb97148253b6d94bbdb11888fbae71530c9ee1e55c02481c2a83d5f9d660fceaef76c1a44f66bdaf185f58fee130a9416fb3535038648ee130ad4d39e50a0a2ec054c92fb553a2a81e43ec10414cd282bdf51bbb1688b8a8ae274c6da17481b7825873ee0be1b74b70bcfd6e0cb3000534d46285e308d50ba88b25b148c255465c9f0c248d5b935b6fa00ab862b79cc2196d5a9955475ca5a9376ad47549d4bb4eb84d245ad5aecfada347be8d4f09db6d255ca7e529c2a3db0ad9e13a7e6a1adce74e1394064f168441dc42be6d0edd053218501a070428f007876744c28210024002047470e1c1c23dcb889d9a86103abd1e39e3fcd8c4ccc0be6c5e5d2c2d262ad546298023fafe3f2a6e1d9736fd37ccea2ebb192a7be690862499b422b37897ee1cded393c54cbecc1df5176affa36cab6a78e4719bef83ccaf25bae8db2d665dabd73a38c3bebdd48c588214b34818108960401d61c0683b52e63b18e04235997756f6939eb0b316c100426b6b04110a8a8542af02a2d53fdbbece5363636ac9696969649832a9c30e102307eb89204044150cbc0b35cd672180c0663b158a9540a0a0e821004151a9c317b227596cb6a6e131313e379de0a74a081152ca09a4200c5bbcc652f87bd5e2f192d43c10527b062044a20510426a2cc652e181818954a45821849c66862c81058f4a06209c390c5e572812018f3411769c0c0c80e681881abeffb58585852a9d48b490539788111327438925aad569ee7a98ac801d1932184714409ef3397d58461288a62b7c4174c3500c2882590103f73d98b8c8cccf77d2a954ab3224410948a74f1450a5a5a5a5ab44cf599cb5a6868686c6c6c66b42c0354c4608c34ca90c2082be0c7cb662ef31f0683a552a9d20745a480ca18455b1091fa7899cc696a6a6a3ccf4bc00a943002041d8a8c6c68696969d132efe36534ffcbcbcba86506e8c10534f8c10b8458c1103d98bc20428923582091848c8b8bcb101557a6a48106104200e1079a56ab35740492232298c28c1bd0d8c10c8bc52af2451551461c21c204894c4c4c0c11309aaca8210645caa042f37abd8cc220e2034e40a0822d803003030333adc0408a0fcc30c2851750d38c16407521c618548871654e09e2481a5338e1840c124c1b6481022c862005085850c6cc824a122c9408c1901f9ed07cdf27001d8a8060a244099a0063c6c6c6a604292700c20b223ed801090c06b37184087610e58a143310a2a6a666e6880d6a98dd30648b18bcc06089113d60c1931138894e20d1a1e8873148c02464430e4b64204593a6243040b1a2c91549000183223109c02205452280628a238cbc6262627c9ef0a189252880c195252a98d7eb459600073fc8c1061c9c6007d0050303934aa590e420041652e0e008aa86148bcbe5f23c6fc8882f88e0830f3a6832240c6fc5c2c2228ae2100f51e050e405471cf1400a315cad562a956aa8861ba0a0046154f1441739507d611882202804273e300326c0f0420c24a0cdf71d4905f24ba55262cc86e081531753b4c08b54cc65301b1b9b1983218a8c8882ca163af8c27b5d560383c16614208e5471c5164564e00311e6b2979a9a23a940d6a854aa1c393829cd6e7084072454aecb5a5e5e8ea402f90282a00d1ba8e0498b279cbeb802b25ce6d2d2d2d2f2c189922a4f517051e597cdbc15131373d9eab2968b8b8be7799a1846784004910e466378ff653267bd5eafcbc2cb58add69154205ba228322006612039c348922c4af0cb681e03030373d9779966b158d7b21e553e9006133e30a20815d0bc684633f397cbe5bacce6b2516b1da365435528f9c20a1a4360228230665e349b39cd655e97c91c868585e532d8651fc7f1a565434b6ac0e20ca4165051411534343497cd645e3493f9cc69602e8bb96bb55a5d567319cd7f182d1b4262848c2830f0c15318456666662e9bc5bc68167399cfb82e7b9d250cc3cb5e2e9ba1a1a17169d99091227c48a2441947aa484246e64824903297bd1e731996cb60befabeefb296cb6466666658b46c0ae10a0e8e20317285524c4ccc65339817cd60fe7accea32d7431b1b9bcb5c2e8b9191915969d9e4424a12299ef0c0862168f17a1d8904f275d9ccf5a2994b063ebc8ce51f0c06bbac75d92b26e6482a9031a1964d272918c10f4804e108164530303097cd585e3463b9eb30df65abdbd4d4d45cc6ba0ce6f57a7d5a8663c6931c82808512a320b85caecb66ab17cd5667b94bcbbcdb5c161ef6f2f27299becc050373241548181b2d830d4112450ba6400193182c2c2c97cdc217cdc2afcea265e261977daf696969b96cbc8cc5e53a920aa40ba66521940e98a670c144124eac56abcb66df8b66dfc3afb44cf59acb6cfee2e2e272d92f5bb1b01c490592a546cb30f0c50e55c0500193276084e1914820c3cb6cfe3d84bda5d56a5d467359b85a1d490572358128356012620857fc0083ef3b1209e477d90cf6a219ec36ffb42cf596cb6aeea265b3238d647d866449d6917048d66533977d9f1d6d64f819120f191e490532f43c6f47b9a20414348e88e2838d8dcd65b39a17cd6a0ebb8dcb652f6fb55aad56abd56a691a5cb46ce8c80f6ad002084a0c7184133018ecb2d9cb8b662faf394ccbc496b36650a954446328a9020c2935585aa2a6e64824903597cd5a5e346bf9cb6bb4cce5ba02f3864e207c1184122ee8e1073c8cf1f2f2d2f2172d0347168bc562b1582c2d01190c2982698a13a4b1031cb4b4b4b46859ea31c08cdc008a3086c08318443c9a1f2b2bb680f221092bbea0429c5961de50954a1543110f6270831122462a50c9ec98371404c1247408a2065dc4e0890f18015d1ec323def39e4aa552433c1401a585132b621471c4e545339754ebaf1cd5c517cd5417ef799e376960a50915479604912444eb45b3d65dced232ef2f97b10ed3d2d2d2d2d2d2f2213c648b8ecd1b0a5ef5a21978d545513c128c14b5ec45cb0490c492275584c0081e30b9b8b85c3663bd68c67aeb2e5ad6bde532978b8b8b8b8b8b8b8bbe983af8a259eae0552ad59160a44acb5ab4cc023510821952882841c1a4d56a5d36135f3413cf7a4bcbb8b72e6351a9542a954aa5d22ecd7bea4533efa98320086a594bcb869c80a2e28c3470d084104ec062b12e9b69e2595a96afba6c058220088220a845ec794fa552478291292d5369d924e3c80b78e0a188299e442145f1482490e26533fca299966d072f0b53a9544ae779438bba732f9addf38e04233d2d03b56caae00a1ccac8210a23b83082947ddaf33cef9e77ef4378484fcb66f745da6533241752fb0c2907a91d890452bbbd6c86f442769f21cd90dd916064f75911f7fca2ee33ee59743d96c3530a6d426a68038678f45696a52caea3ab0b61ba52d1958aa54a7dfe054624c597c89e3d68b2cbbb6d0fa2d89e673f4a6d0f96883f4c3132a5dc0368bb1ca2dcd69ac47a8f8a4c9be68d1ad9f51a9d5231b5727fe9bd4ddad48a664b5b65d353245ea6dd94f25518fcf2cb297a7a9b8890f1113eda2e930635650ab6466e5375a23590f513b4c2c37bf998c7f799f3de7be7bdd3dbacc5f770ad35671f3e57db7a45d027cf79b11e270e067db40d44a37cf03667e6baaef338aecba00f0f8fbde13aecb95cde0bf8e338dc591ff5d8c3f75e5c2fd61ce853e7f43ab0de8ede463f51fc40056cd73ef005baafcf5c616eacedbd97ce9cba3e17ece179413edebedec6dade876e1f3abdefcb600f6e03d1a80c622db4278e8883f774a5cc1edaf249833daf27c50c1e538dd56fdbb631d89b5a752a50a5983a3f33562f65db26d8f595c863aa40d5f19c7ea68ef764c6ea3d2629a35645cba841316feabd2753476362c63ca7aaa32da942a362f678afd7a6983d4ca4983d29d0a2d098664f7ebd06c5ecb9aff798ec13b3c789d9c36a62f6b498983d28983d4bcc9e25b347553d263a80a33741e128876479a9b1a0a6c072134a5a43a5399fb1eb3722ec7a0d8d5dcf7545d8f55f1a3b6ad7839468b7c60c4c9c7ad61864e2d4af460d4c9c89a31a81264ebd38029938f5e1182434712aced7726f543ae2215bde712dcff7db18440ce2da572bb5e8198092cea80ddb847a0fe3b78c348c1b6b6d77373001d51dd4c4a961ece0a833d85a42cea2682dcf166becf39e3c2a7809155654b1e9922dbc7bef8e0a1045ed5e4aeb6ffdab9d3edeb6f7f17cbc5d2fa40ed95a87989b02f2b757ca595badb5d6d29953f6334f9c89336dddb5de5daa54b58715b5387166cbc7d5e3387156f9db67dd2b01f9db5fc85ba75597d20bfa88b5fa840a005fc89a67d33c9a4feadb40342a9c7b037bb054ae6a15119dd55e477357bbd405a2513e342de40ee1ba89e1596b95b0a9241e0861a3f06007cb83215c116c941d98b16ddb4639db7d963a41b2032e78701cd77951a22889b2832b7660054623010aa0f6dbbcf7de5b694ed9d317c9e3ecc99f27d2138cbe0fc9a4d112102042e206a251f9427977074fc5cc9ddcdee1b14e9c5c49701c72722948def9ae76377e1c67cd8cd5a7461bf3a61e1c77e64d556157189cf3cbeffb71e751dcdd78274ec6df3772a4a77ddf98c98df4f2f78d1ae971df3762d2ebbe6fbca4e77ddf6849efdbbe2ec54d6d6cedd448553b35667b23d64db437f4b6807c6ec7d4f652a3253dfaa5c64ad69b7a6ddac7f548709c64f5b6760fc8e7a61513c47504040edb8e3eeedec6cf9bf8dbaeb6b99a57e0f9b0c71fda94d6fa7d3e2ab695f6b8d3567ab10f6e7db44e5a53e550d7bde87fe6ede9b415f4d15e09d8aefd05eef9dbd0b6a00f9e383ef77e5fce17833ef6f5c276ed0ad874b637f6065b0df4a9db5aab8156bfac1d83d06cfbfaaaf6d55a1fa38f3dde5e3e53dfbb3368c11eddecb07ef9e06ed5f30eddea25e17954ccdea454ac13acb6eaabbf1aa34aeaa93893444ac56b67b53c5649a2ce2a20c432b6a70f923d6f89260e0db2f283b5bac766a5da7a2dbe1763ac699aa6e241c5430679a878a852a5a989878a870c1e60dbf71e37f3cbc7be9889773f27a535e75aadd5d3d27a8457ef85aa1ff0f7078636c5b8729d16bb49e4fe679e4e7ac3248d66dcf367de2b01dcf35fe0f40b5e5791ecc49938f608bc5fe1de7b6d5000f702a7b3bdb1371a781180a7cfd0f636f0feb57dcb18dbd1c787be3516b537d0473b055dd0f4cbc7dd38d087eebcb9cc4dcaa5f069d49ea00b9be6c6983467d0a7476ace3d54291dc2abf77a7c9e057df2bc3e5727e0b65ad6e5aa626b7b18ccf9da7b6f8f3b71e8efc499f7c21f46b39b59c803f4f942550fe2057d38d0a71cda17f4f1b8c9f9687ae22c2ecddf2622d26ab1ce7584d9b5c3e16ed4527a91eeecb9cfdf166df7e59bed4ef568ab55c694b0afded04a3fcf7eb5febb2f1ff7de7b650f61da39e1a97610cbfab47d286b52952a3cec408b14014199a3da41cca18c5d9f6a8cde8a07048846799783d51df0f3f96eaf76a3d152bdcdf7eea81591def67963f6b86e1c72b2bb57e7047b789b06a231a79a40ef95f035396d6ebcfb7ae3a4dcd23d123daa36d821b5ce9e20229e27500d4448dcbfdbeb29cba6b7dfad3b464bf455f54daae236d0ea29661e59bf7c7ce77f67ea1a27ceedc63ce69172209773972a856caaf3c4a1cfdec8636fbc9cca79d3ba318fddc8137ef86ae236b9bd691c72cd2df066ddb9e464d1ec32060132880022892c6a2841181707227a300383092747827006a5371041062c3862b6c412241dc852c462c9bdc10b1ed8a003922343b0200bbb6794165c615b708585a2b46736b9428c18804006370041116f424413226ae04d98cb355d2e1a6aacc9afb5619da57dfe0579701cc7e957f71feef81d777c8eebbe7d68d3b1fca8d0be65def4437b2b5947ec12ff607d3fe0bd9584ead8b3e6cb69dbf6db8516637ced43dbe66dbc9f201ddadfbe76ccd4de00aa524d9728014110b4b5b504dc34ae7be71dacb585a4d6f02d24e192da1d467bf875af9b86bfd4b521782f95aa5f92569256925692594b89d6626a219976da69ab6d2dd9b47a2b0251cbddb59732d8efdb412f6541cfbbe71df43cef20a84b1952deea9ef85a2f56b15ef5954a0c53a02e3fbd799d6e256925116bfe01d4ddeec6549bd81b5ea2441445115fb1a5a4a5a495a49544a494de72bbe237fa959e561ccbd692cbe1903e1ca9eeb8534caffa54d1dfaf95a495a495a495a495046a062d259795d442725b4c2d252d25f6c9de6ccfdfb927bbddd3f6e9db55f52d7cce0f594a76be7855a8bd6da4507bebae7d5a866f6fd7ced1df9275442b492b4958b52b1e154aa97854cf3ba3de4675fe86dccf82535524fee4e38dfbee616a45c9d642427550bc515d89aad1d17d7754a76a76315eb2a9ca48d44eeb91aa48a45061d829968aad725b495a4a4a0cc542d9302c95569216928d9584a9ecd242416dfb56127a02d6e510f7925e5b060db19658a8cc52f2d929db1e8ff889b341c8a5be794abfbb5556731fe8438fe6fe4e8978208fcfe3be775d67e4f3f1a552b6a37a470d6fb30b37ebb2daaed3a99732d8d4eb1efcf67dffc0eff6fbbe0dfcf44fc8d987a12eaf0ff71fcb715c66722faddfbde9e12fc5dd5a6b378ee3b8cdeea8b73b250296569794c8de0eb31281b63baadb7fa9bb53e3f7fd76233da23abe5390c7a6b7e751bb6a144f7b745d97a2cf39f51d556fa737a4bc977783d7c679eb8df6f9a4fe93b5544a979aa637e04db5eefefb7ebae77fddbdef1befa679ee4fbc55e7067a02fd6808e9a6e27df53c4ac45afbdb7529702cf3fef227c879babc7b7b370e6d6e94e1db79abd7469c573e5022f4067a446fa0442811fa092b737288384a5705e62bd2ae4d449a76dd2920704499020a070e1c38be69d64a31db60293803d66ab952a083bb4dac41d920881434d9b686b4d6f68006db7a7b4671828a0cac6812cab91de674b2b5d65aaecc1bb65d6badb5360a130858519c18c23761756736c16031288ab04b580e57311287c20caec3f8062c4ec39aa66932f832d630c6f85e285ca06df7defb2bde2850acd4fb7cad0605152a4d73502ca9b058827c390e8a246c390e8a1068b5878de3a000012c3f31c65eed19e5892ebe1460aa599109927db77d6147c08c83b825f13426330d9f71a70fae98c51b7c755fdd2ab3a64921834f8a2bc428cad85d936e0903493850d1441a504d50398a15e18402226630050848301426b5db74f29237ac049e9c1c9b63773afd6cb72bf084e5c48e73342b5a71f1afa6a26bb3b6046c99b88bf1662dc65889d9a36d6c8f60bc650dd3c6d8cbef4d2b75d73a9d536fe8e98d80a1d2b4d53925d41b7a8e89f8da338a911e76b767142348bbccd93457b0073a1747a49f74a852ba84370f4eab8afd309a79c08bd95391590bbc08839ced17a0a0072853ce188215e4ac2aaa670e11d3571d8aec0bba62387266530e546c0c853dad4ad35f8d61dda483d1bef3035e0d71c80656a345a3a5abebad578fa2f5c9cfdaf8d558be8803f1e2761868b61cc41951c6eb41b7cc6bbb38df318b5c8831e24bd3ae1adb9ef5edcdf698bdd9b6ed3e782f18715eef45b4777929d4cfba23d2aa5d221d80e742bf6d5b923761b9c6b40f07628b88df82b7e520b2b0e6b72d48bfa37ad3eec0706c7b1788e5b7ed9d38af6bef02a25a49bbfd1743aba65ba2ad15caeb41374be5adc4798390b26794209c7649b3eb73ec192508239baabe1ac3746e166325fa7ad1f589756792758968775e7edbd2a973ef9dfa55872ca5364c5149c32217a24a7c5dedbabdc18fd91b7ca3252ada402c45f1ad1c7a83278a296f569d192588a33a7a1e1545a325aa8d96e6873c47ecbb6e917ed2677ad7150d804111370822690b1120b936b400c61835e84114688c41c41d52c40c88262db27852021d215c2145071fa6289145cd2e79b6fd2cc56dcbd8b6393f2c518318c221064f9e4cb9028228946ed3ae9508110c71240442cc9e396705e2071f7ae06187223ae44004871b908e6c18329b3d73f6cc1a26d14e9a37f53c3cb326cd9a346bd2ac49b326cd24984b74892ad4252a6b92f884583475ec8cd93077b67943444f9853077da5d9f49a2e695c28116eca61a3aa6b604b255691b5c3f229fed325dddfcb76bd546deddcdc79b976ed74eeb45cd3a50ca368775e748e4b4f7be3a2bd9651b437f8a2a87996c70e609293cc9f3c303a034ad40f43e0010969cf23058924594841daefb03b2840c1e40727302306559220ed57288115337c4006f9021b7c700111c608c1ec05a47d0c2aa0c119528a20ed25607768ac10821355d46008117a40da57e08815159c41daac796acce67c2110cb6f1a77acb530b46979ad6d19654bf9423ab2c5012e6f71d1d3de0c6d0e54c024b3bdb13bebf20739c9965b72823e549b132d5e69dcddfed3699ff9d2c6570f1d7bee926e4e03bfc37eb16d2dc8a36ae316af812cd01455a1628b5564198d9652d758ac8dc56205a8b8a8642b98692335ea944333030c000000a315002030181289c482c12808f33051ec0314001099a646604a17684910c430c818830c410610026444a4044848d200f446d6476df737a1052b1164afef77a6f9565b4d014e2a0223e2acbf80d5f4f00fdc9b516cdbe55f912ceffc2e6ff6d5b1aac02f7a8c19ff59737ad3fcd837f59c1bb4e2582112daa9ae75e74d1190d9b0f6f2500a627e5801d07048019b7f01f3001d7cbbb72e498c1eb4fa97407353f8a4b4b19d788155ca8f950e73bec02223f0ca2e703d10682124ecb13abe6ce45b20072ba53ca084966c9128c06d60ad5780321d320cbd647ecca36a02d5d3d3f7c34aa381d136a1907cd5f379665b13f7d60de5e32698c22659d38d0e3dcadf8c58967b46cbaa5c2d952a6f5b17842cf4d0325d0fc0f41987d9a2defc871d511df3e26ef2d072678161d900bc1d22a404b44a648c8274f18d5320041ebef76315986b361e92f77e4f5e521d8ee30e6d9adca9e3deef1147b6b176dde2fe44f8303242d94b0306cd8352b0846c51093232c661328aae354f8b86e3b25c2eb02a70730951960f14fb60291cbd49d78eaa3d9a8a707336e555863588aeacc7ceac877506412e35591c5b1d0b6d36b090b42cf0104829abde60d499bc585a834a596f0672ddfbd16e030d932533be92609e0f50d0e2b65b2687dfcd8183f7b0811a2c3efea3c13d5d1ecf3296bb71eeb01edf652061be5ac80ad498f4a1710e2dd727754d7ec48b1e3d83dfe099d076b370f6b1f91fdb0f4b689c4058a918ef0f995d82f543f999d0e361c711b597bb26e375bbb962c4e38cd33f200bcaa306ff031831415316b7ad9495f3514f084e4d468f479c013b87b457a05a2fc3a6a668964795a640c58144a77375b0a6875d8d92af19bc1ec5b2122a23193a0fd601bb01d477cd07620d98e1a99daf42d8013615e16e697272fb25c36c8a006b3b8a2f2d06f02912625d0b8b15d1c0c5f33bbdfa15182d992371039a9d967686ff212ae677d8252f1e7cd3f8863a7d8fcf4fa7781bbd5d51c2684335d997c03bb3c88dab53cc664ef1c30753f846e7b51ee7bb1d6248deacd48ac544735e7a11450f1c5d2201f9077ad686f2cb9ce0aaab05fcff8c73ec61b777b0f90e35ebaf7d73c544ae63bbc98dc505e69158184987129f47c0e7137b862d7727cf085e0eb1bc008c9cc13e52bebb569688d6d2478a199506d7bed6a8177e91f1d521fc7f4f39c1adebc2c6905eddc18f6304081af6f7aae44754c4495497a8ca9c330eab4cdc9555260ce3573c257332f74d03f16821bf466ac0812ff422a501d2ddb6ba5ddfa1b318d735285e6048409ac6fabd37fd80749675e3ae652fe84eb5ff004b1c5b80b2f67fa9f8f6de7d20195de413da3d8d44419df7ee5778cca46d19edb2ead0979ac4fde5fc471b251c4096fadd47f49219814bce3f806f697b20998af87da1d745c96d79e27552cc890f040ca7d86fe29a78de18ca5d6cdc8fa0d79eb2d0a519831c37cde3f12883f28c2eb9dd731496b31d8409ef00625342df609c2b650719640369b85fdb2b836eac75e78ffe16dbfb7f62be0e2c20379561fe9e550c75e64fae80a28bf758b090bc58ce4e8753c4e4381579807f3bafb85a7b418c456c73a75786183b685ed910d221bf96a74df49b12c9db190b9aaf8c29d1a59032be8c08237ef89fc7f48205c838b1786fbce7ea42cbfb9b92696ab117317db3709abb6ea0af47251817f3e05830ffe6013538e9ff397254d67a298fcfcf0d919290bd816074e44ce3074f8ea6134c7a4ba9cf5319a025c241a0eb4c89bc4c8dc30ed0e312fa8cfcb45e7593dd1a00636349d41b793f2552273227cb4adf1ce87c5ba2040a9cc63a92a80406988fa397d4b4115018bf55da848a18cf3837c013ca5dbc95fc29f0c5909844af5b0d49e4c538f55117dacb78297dd785a4d2e57cffbe9b6ce4e7241e5118159f5e9f65bfbb80557d1895e688a5504dcd08ba93701abc54595f82b4ceeab79815fa9baf120a421359b6b70c4df8248fb54c365476cf3d553918d07dad3e8c2d85a6fd6cc74101059809b3764654a7a2f392d2d3b4f491d2fef4c919231536688b9b9120e9cc49b46810f80185199dd1303a56eca1b9ab5b88d4dfb072dfa18925e81cb4a0293c07c67e06c1820833eabd10813da11820f9cbfeedfae8f946fd752ee6aff8d0e8757da59c1368e6528ced348de57d9cbbc057b2c7d22bd79cc8d0e116a76cac0febd63b246af91a470d3e96ddff3488b8a2cfb32b803e1815edd8bc3f12ca207a0d306424948be9c48935cd6b4ae67471acf3f3c49ca2ae5bb1976e1d33e7da42101d45bebc31cc8f10f4ad4f5815ed1b706d2f984c53567078cc8d544a6791e7b49cec3720792e1fe56faf5a2066655155a27d9f3eff76995118cc6a369548eab4c04f47ea48e505b096c221012aa13d33c5be540df53f8f5e794799dc164c395ee3d3dcffafc486285353f3165150bb85816b9a4558ca4e12e9a82c4b6d9e1736814ffb85f1563d9a5ba14bb502e37a7eb2f18a734a41f4b1093e49826b91072f04ba64ace511ea8748c2ea6e0d9a6e0dea0aa5a6ae10ef7173b8e4823118fc5ac74bb1462b509ec2858ccb825bb01b70465e76a151076bb7d1067b9c18be7bdc1ba9990ebca1d92ca06ec03214a7552731d4c61c75b7b6751089b5219dcd5b6f97b6a65fd370a45db3d8a318deb61389561381797837f061ba0c963aca4b2c09e382c6f7f390d112bc98b60022953b0b97ce4f04de6ce3a674de960dfb3ac09a9573338a157337e99d179776d7ed8b92a8be241c58bbf93bc843d5e4dc8fbde2ce011ce0a277e14d520cd224ff8656411be94a304efa9cf724f355b6a155a1055652968e15315c007e00aec7a91f8ba94dac9b25c573a6681dba39213af4e91240d3d6e029e7478181251ae1c12c5b9e6d23aa76b8203daad045c7d3bd1076742858f92cd3ce620d9da920f5565560e17f7b54904b36fec5bf1208b384c98469401332acdfbc716ce0d5254a6a8e0075cd4fecca520f1aff9c0a1955c60d4495116e8c70f67abac08c551c2722c54f75e0489c233c1490d24d03403b1c82370a48fa9f742400c713cee3e531ad1bd41c39005210727f6216daca6a40e2152aa6a6e7e63d1ffbd050b6c16612a1d1061023f0505248db6efde795387cc7a095c8060542ff80fa68399f1d61886a9b88666f3f8fcd9ecc04814bde76c737f397d7e31961409ce474a3e4289e0102d0262f9213a9d67b7fb92f50d4b21c0335c4224643608cbd2a44ba304c6667d2658298c3832c8e65cc39a316e6bc609ac6470413b1194a446b6dc95d1ee13fc7ab99028a9e06ec530d1974ee5241dc2cb02b0cfb845749e5a02d2357c083308c488733110b57c382648202194bf2f6ec2c6355a31b708970347efc4368c473674fd3b75a4f0db2d7f38e8a700078f3806f95b452f1c7f78692110f1ed2a6a46120f1d86f931a239c638b008e826b8ec0078dadb93accbb57403eb901f469538a535e181b5815e9564ba5b530a05485917320c20818f8c189435da0420f1967716b414a767671dc9328ff179a11c2b961efa2db120362e7adfc2db399ead960bdac186ca1ae636ed587a1b2b32e2b7d89a4e8acd906161874bfcc384cc8117455b48543e60c25672d297b46b8797cfdcab1defff7f23bf88cc4b90062d16402b03837330a33e488c407608c89a9141756a7afde729fe676d88ef5212b875a899e46d64d3116cec1ee9fa332c49dc2aff86a03fb871542eb1b00545d74d5e08656eb6832456bacf3a6df68b85eab4b2ab33d637eabd94e41079dcb80f1c59654e53723fd133b23aa7077b09de0151d5bfbe99da88d2c528720a9fe989fb1d0098c6c479637a93021937036e4f3d3547f9b2680d5c63e9007c2c7206d6cf21ef596c62b92f7a0f94c2bef5b0120c90e5ab597ff58d4b186f99cd9a96a684e629add043fea0fc748fca8d2bc21bad4458c8d651c405544bac2c75c9606b88d7a4b081fd60ea474c4f6f4f3bdf999f58244ae78edc987fb9991f3b6b4ab69cee534e811cf95ab4fb00240fc491aa4af03c197195224fa102ce4bc0307d2521e418e3ca4ae2c19faa096825c2cd6d2c6e4ed57c81243d6e7731fe718b3481760b6aaff58e12d110f0425880c78b97c9e288429d2166012475078305ef8109a19186d742dad22c2b786bea5e86b770bcc69a19116987e08991cac97af7dc04fe2c9f2b8d344083c17ed5e28177052dc0321e18665ae4216747911efa6a128bf8bb408530daad3854eb003b8b2e8fcaf9312723da27d0c5503849efa441213bacfb998fedd14dcc6d8e0c1194d0db65228e44ff7a48371a8f17d635474605a00781495007243ac942db25803920d89acfe15067d01c18e8112cf2f7d18cb8024a99fb60ec0164c8fc3b70bf84a1192262b57dda3bf011cc7c416bafea1dc015c6553048164bfe2200e3d5f8d62741a38ee126544e3960b914c818ee538471ad816ca382c44fb5aeaae0655c02f6b702f7416b323d54aafdc0b5a6d82fefa1f9d8e2de1093e66932b82a4b64c87843a4134e4c8e6c7e968704bdcdbd2722da68454ab0b2aeb8b43e66bbea83c55677e6a5886de6036008898e3e014fe01ea4e523cd89088c1510a094fdaba1458d7362f6cf00bab3156885b1dd467cbf4280b57b9f06e1f30a3e0830d69f827e0d1b8fa370a06228e44923fca7d4453a0a9a942d43dee1c56eb5e828691795614040cb3e67e618359953e2e8e6eb9d0d46004a7cf353a1cc48ca7c62c7a8e278205498034204969e3d45f7b49fdaf137b9060ae626b5e52e89c0bf572c3f81f848be25ea206d77cf85cabb524fb506aa65300a5130b42286e1bde7a6837e66f0de9d6c509dbf6ea2e9a2c01c65fd88cd2aa1953fded9858a352370d8bcd88d5faa30a59fdfd86de911edb40c0c157d287c9514e858d28fd80a337d324f29aaa5dac23a076ecaa70d2cf5cb7fcfda2f6fa0c96c90aee3c8b13eea53bf409b0f338fcf83b84c6ca9b61088ee24a906a071716894b041cd25059a58b95e28c987a1a002be2a57e6f8ddf423d89053e0eadc5d103436f9e0b85b26800d0af843f5e15014883be0f9ea83df1c032cfaa539b25f84ae94b2a1875cc7d62b931f7fb78785a095722ce63589e29950ecca91b122cc3ee0f770bd92c7eb6ccd03b31ee548b85d660bd8fcbdec2dab271f66aaa8b5d0f756baca32e1619d6c73a12ef5699fb43a806435e0d8f371f4184fb89184e0303e77b14dcd7de7fe3cc88bb35b5ab3f73bba838d61b0c40a949a30f7780e1674a3f35830c95ca1ef4d8040e17d8f7cf14f6c51142dcb573d15007d09615b56fff8c8c471457ae85c6ed27eeb77d10ccbb158d6c87c9a7c9fe10207253db506b5913596aa093ccb816ca8585eaec8d96effdb4bf03eec3a9ab90eb2cd2e77d104ada1538189c77178f6d42540622139b0c11a305431b4af142672ac5ed701fa85581c956467033b0aad5b7e04c94ff45fe95370427cdccd2222e3a34347d380f2fda213565f73356eba905f57fdf51f5b00d88e605fff4c5c769e92bd027aad53dab4f9ca3f234049542b1898554294fe0a24dcf82dc8fee33ef34b5a21fe7d102e301ff2daa13e9420701a300fa68c39b541ad67cb1b06661222f47d066289e987dd017f2913990f976b4ba7735ceeb8652b90d24f3517599238e1bf987f8a286298429c6a588bd53ad25e23ba8c5fceba2d28044181236a4e497a41e7b08c6f7ead8e79d4bf05f75d41fa86b60d9632f7cecc95cb7ed97e9b655359edf5dad721d30233a076f1287cb7232445c099b77fbe8fee0d68220baac1a0c009e04f4eca7eeea7996e1cc84a51ff700c723e8936d817e4907227fe840730f783813b03032d9a2c7ba07227007e21e72c6617369f0efc26b2dcf9878db2df5e972721f1d42a1c91468a471016142e57685171c62ba7f6f4a27f9dd07b6605de876eb45c8ce04c7b1340e924430d788a26b16eb53968b2b3b8997c144afc5df3481b7409b8b6dfbe275790622e8705d2ea76d7f4dc05a5532c981048f40f1416d2d9eef5209761847bc153f6d297131a2be0ff90c8e47d1bea261c8ef6513d0eb7c9dd35f01f407908837901980018e7d5fd1a40502f1c1fff97f08790569e6b5483e977d19fab98589620507227a4245ba4c00eb870721dcd173c9e7a86838aabac46042d299ecdc890705002d0f6c7d08d9235bc91d90479b6cd300af1ca114e9488ba066e74225c407540217b3524655373b716a51ad289da1179ee02d01f043b9ff780f052c02b74b28969236e60bf18190c4753f76f8938ee54db7df2f8983c1a8ea2a8b5893aafdc593fb9ffe1100b2ffe77a42555b208a552523b761c400b4f96973843811987336e38352e44a95ed9631abfe77265819d8008b82cc4cdef8c0657f1f38d12361bbb230252a1322c121a534c8422885fc4e23e0eb8468ee9d2764815507bd683cf6ca766de8fc0507c1f4d7c3ad25b790e7d58b37d78d45a4b3e0d1744599b5f434542273e8c4c0aa7b1557966cb2aff3ab529abf54410639cb1159b411c46688ccd2ce87d498f798e105d973cae5bf72f1986135443026a510bbdff1bce41d9f579b1ab00d33a880eebf639095c06814a7878182047283d1da7d6da628ac4ede253aa3b02f2ee41c141f06dc81ff7c0ee222768094a7f819d4b07903ec531578d4bc363a40c62f2ec5b01782a8b07ac82b56e2090622a17bc2f87b17f60020c20dc13c91b6f0c45fd8f0ac0d156848d6d7063cedcf923f45bf20004d048ac2ea5520fa541d8d2620b98cbe2eac4c53f4e7f79f073a38202823ac1d4723f9316d06909acf84567de18719f70fa05225ee43ad4c454df7edb4fa6bf369a5b663f5a401631d7908464483ef581addac70a5e2cff096a8a6c5f883e426005c725afdda85557007bbdb59686125f4ee3720a6958ee670c9c9ea46e21cec2f52481c7c1287a1980dd60e1502b09d93db66f46b5f2a564d0e696954c6cfe247be125dfdfa3d200791f6809fd5f64bbfacfa699b420f2178f6862cbbb5f8159e1699cb8d16754888b6b371bdfa9709d2f090073991bf4364f2787ca9f71e1a8f5bfde015c9d3b80b4f57c67130ecb8d503db9a6f5dd21e5ce74cf1482bc02e632d52285c2394bfc7ad1e08201d278f22906f86797c181d52bf8293ae89e214e530434bf3d5b753623d9efbd3e045280d3820041143049e3df1e830829d9cef8fa29a2730b513dc898735296d21cfb604f1dbd6777799b3565be0bb47ac4b750ebc94de88db4fc81c48bba712ebbdd521b488bc51cc0c68a81b234aac8b28dc47719912d4523020ab21eabeee0add41395250c6cbcd956cc2eb3280cb2a926037ec3ac416cfb37bbc0383d1544e7aad30bcf79eb215611571b71c1acfa6c9ae2aa85c140e0057627dccf67b196ca1a943fca2770501ac60f89380663f28b13ed67ed9755a00604b5d5b6a593a0b0d792ab15e05f71cbc18ecf8037f3b020a5a7fc99679dcb7ee77d1417627f3c6de7d9fdcf7e9ddf92fa5716a8a80b69e97817ca1e0e7623d0d9cfb7a2d07a29a0ab3003fb1adb3a84cea065b2895b266f2fc8ef27682d8f49c186d6a2140c6d6848ad82227e722a787bc137223ada6501c0f43715d457e29af9df81d5bcdee46d690145e152550293c979fef00018a93ab1b0a86389becc511681e7381daa13eacabe2394fdbd2ecec66ae337a652da684a565dd278a8325bc73b0ac4fc9be39d5df928464e1498178d032b2c384d98120f65a08d8195349c6aac37e3189fa2c0382d54bb304a6670c250cd08b65cf95d740cbd962f9dacf91af78c80c61d29257dc5661f386980fdec0c5c2345b8f9f653e95a414f00dfcb000b74a6a156a50bff69e7e29bc411a547ea172b7837db25c1c29c4e15d11ed9c7c7cc84becc46fa7cf819e3bc8577a39e9d883d2f241c446379005e703f9224a0f09d78e073a92dbad54b277ba82fa62e54c5b61116332e7619baef010fdbe31adae092813603293860e484e579052ea14ad37248b160a5a3594a74d6ba886a1f8c99e20fac6b750fce211751d3a4b662cd8305fd483acc837dbd50e271ec604189b4399a44fdb1d0863f23015f9de93a8cafcf3c036aca07de621efea195d72999cbe35581b6fab88eec1cbb869f9619b8bb24c2ea0bfc2602fb53df3e0b986b846e514f22196c7fabe238fbc2d6aa18478d3c41dfa99afb063d49c63d1c6fb6ec36c835078526885787a50ecc7f45d5854763a1636d9c6a0c1e2ea06c27fcf838bf44cf757492bff4296ec484a3b3ee3fb9f2a7ccec384359ed02b2cdc9c0d325f56617bfc516ed62d23b8104a7615feb81da5a43bd3f988af67281e408bbaa23b6b53c37855dbb0b67a69997b42dfa150847fa2d97fa7c392a6dedcd92a27ec3558409f6b11a18289c30e487d5aa4d5b1c09934f758a0350ba6a38cbc759e18bd093cecc0eb35f40ff74e7df7971bb63c59d70dad106e95bba980d314d5952436ddb11e831b9cd808b4d649ada437f47ad9f0a7afdbc89084cb6da97238852498efaae27efb6a0ac9746ee000d019a7d88e5a74388a522cb70d7cb91dc0116f2cf767f733d83fa51b242459e1d929f35964ec9039e4ef0f1c89953f0fe0a9ba0f7ce17a48df20bba3a0acf05a56fd3be095222fefb0802dbf56f7662b51d5fcf79b67d5c0af81065b358bddc5335bb578732dd65288828a0a01b6bc4c22728d8cfc603e80c72c0b11f74d5875de0b2da82b3c87b3464fc0cd0570e450c4dd81b4e9c47db19d68c274b36932c4dafe830b6169d2abcef19de35da980be906c5b06d0339f9570806ae7bc3eaab8136120c881c558532a4189a2b8e6bf65511a0ea0bc9cc54d147de01655946b21d5203e0ba475d692fb12f3ba664a57349a855dcf4b8a801f315c4c9ed831890e183fb7b2906c6dbd4b6e9846e809493973a9969e526314d5cd37a16b1b75902e8f16652f0d510827e62c3210159e1f3962277af54976c57dce633392b339be7b3a81c14f5f9aec48eb3e81dc9d5919b9ac3632b2d36f6646dd5179ebdfd5a82a79f5e5b5dec9d11181dcc6e806c1f06e1d288c7302e6600eae9e2843cc1c5502034d61d41d011ef6813d472cea93f1c7d22ca213bfe22c6ccec776452385a374b87541264760b1d901b8cd041b7ba633142c047a17ed151121c08d145ffa0022e15d2d08d2a94a49cba71a80692386464920b9d6fcc22de8c5a63fd5b4a3f9741cd250979e08baf56beb93e9d92601c966bac33ec8af239ec36cdf5dee00f9cef83e92c4b21d5d077f8b91c463bd623663240f38d9af6690a80921ea8ada7b4f3d98976d35688171ae5f9281fbc5c2159bc97b5b72d2ad61a0147b408cd82779f801cd586f52f10ba2b19a32982ba0027f3ba6e6e403b9d46f6075f3013cedeb6a5e159260eaf458a35321800021212b7703e75a25dd4530ae4a5173cc28cc6853fdd645d9f039d8092dd02a10dbf8ccea4da91ed19b0284a696daf1ee7d0402e73a027f150a4e50f22df50fe789d25743a03ba38c088b59fb9ed06d68c3a789165040a0fc5b7a3f5cf005dceb08005b330657910ca4d795a0b19e2ed6012364ad9290dd9b347b2118a7cb1001a697fe87ac5397b9efa15956834c87d3aa3cfa1f73c03578075ea55d232d74c1a77e5a996ce50dc0bca4fa6b174b196a2a85f064e367b4efc2366837047c731de9c73d0de657e32e0625b54c009d60c8f4876c3d23f17803c6f083f8b7aae37b3a4376d454be9cd018672bc7daf1fdd6a0c43b2bbedfcb6d3ccff56ab394d22b79a68e46e9674d46dbf1be260697c085d6cfd1f11240ba6eafd7fc9b96e494c2560d7133f6436ab378282f829f6f59cdce7e863f1fb149d72b8c8e46ba3166d9178a4ce69ab596d6109c159a40569cb27364e5bfa65ae74a85b39ac1109bb2442fd0c782a5f3b5d8fd36e006981fd8da13ac4d4270331718aa98f8574e723c04c4172917863394e2da52f833f113385f6692f08cd18eaf428ed2191d2f806cc562cf86db53b25bc2edb5f7033f8ec24ceabcadaa5ed4203786fe2d177a4387dfbbcbcb4637eadaf5c3b4d9592a16c62b419e4316c89279fb3965e530a17fc1c6e0104017ce6cb20aed9194a8c0bca781dbc6217ee28551ea3f22dd06aa473670aadf34e4b0131a13b69e33b4b959a57c23784dadfaaf7756aac0ccaa362eaed21a4bfe9788a742a2f41925045bdbce36c6d9ecd45da87e52ba77d9c31b68c674d850f758421d9a5000308ff9992b37df891926045b4bd154f06887062cb60dae2bbfb9069f34a41aee75c1008fb49c5a23b7a639713c57b6e645a6be0c27bd6fd0d2533325bcf9bbc640439c02fa1f7e42545cccdc73eb3b648404bc775965bdc00b81883485ee341270b50dcacc6c98b600a7a3ead55d5bd368ced6365140fff9600eebebec3df0b8c1efbd3281cb91c925e948f7cff0e16b18f3322fa01a19a5d00619a0be43ff413b6a20cfed95fff6b63ab95f3955643718cbff41d1de5835a1bca8e581666aeb4aff216a81a4d509931104e9693b7a07fdfcf923edfdb7ed93ca53c8fa0165c4581155f1738d492f718f59e46c73bf47b87c7917102d13e3a3d07ffedcc68ef4eb25fa3091904e697bac949f2dc6eb2a1d367a264c242607a140ffc1dcfcc476f604cbd837d5b0a8d5d684dfe6e562651ac1000abe0defd10300078ed4e48ea7ad8a4102fbd27c4a2e002f19fb8b9bd919b08df8f0413eadc00e75b7f5771b834cfe06131355fc77794aaad51f56d266c52b0d96334d19748b1969a8297a6dcc0a01af1257fd34d93c8c1a8061ac9e97b4019a91b57ec902e83b9cb2b35fdd606472fdd51ce5cf3b0107adc18dbd37262215a616ad36b28a04b71f2141454a9ba656cddccb345d35934b6bb2049263ea770783a906952b1ecfd05a7cbd0f997dd33662b176fe7ca861bbf1a7b3bca2ffbf820cc55bdd95a9a0ed700746787673bd6b4342dddcf164e5a23e960c42589d7a531ebc3a3a07b6d7b649a8d99343fa2b77255a8b56fbc2f65f5bb183962c60a68cf4ecbcf67f0a7acbeee730809c59f87c98f5d20587c469e2d3b8328f8ff134c39c523c5db7e43119bf2524928a0c4ac02c4e416cd548b35fde426701980fe27d3f3ef95baf041daa93859e1b108e1dd936bd3bd59ee9dd12b0a60ef84b8b8adb2bf37cf02ce4b33fe8cf229d312c848d318f378c29fad68eb872614c857b5ef9876fe4d38d1df65c22cc50672b3a9bdb6a704eddc4d0e21fad1d10627fc01c118cf12c3e0dcf0be63aa79ec8cf25af06bde68655e142bdd87f362fb83ae7e54eb39ada410fc80b9a5e78b9ba97ad4b9dabdd293170627eaa45f9730f491c90869956563b3e42c1dcb00ef8a19895cc63890b4e9c6ce1c5bfb2ba1a232694a7bd617933d62e2c97e55ba4649ef7b5c4afc75bd933045b964acdb0a6a8672a7cb4121d8f5ed4ca3f655b7c28897e020293759ab9bab6cef4f1205944d3a325e9f3ef0054e87b1aad6dd918a4cdaebd1d49773a0798237a1b0347f93f89b76adf30713815cfd0e92ebe149e6f8a0901046e7d249fe126294d72c2d6c59e84a0639cfc5085f7efe80abb7bcad48ac288aec809437c73909c92be1447b9608b45be64de2c76946b84edddf6afece00a4b1f71c0bbb6484826ef1376839c851518e4a5258fcf5248b93c8816154e31aef7d248b6066daaf625711e6f753489ca844292acd4c526369f58c465a95a4201214be45de83cbf47d9b63e62b7b8373b040bbd858873b779f73e0653227a09a8b7937faf70a5795a388fce05324be4f9447801cdeff9356198cfe883de8ddaae27924efba02cd88a4546322cc32fbdb1e917d794a8993660142e79190f7de13140af57757776dc72345ef91bf204900967a189bf75e632edf4cd0463b215cdc478efc5c7644458b068e000f3bb7fcadac3bce3ac808bdd63f44874b179fea69da2c4a11cd6bc13e201120f3dec32c7089bf143db7a68ecf7a396c805f2f14a42be46c7721bada5782091905b20cfd3e24147a58cc0ac85b63073c847a70310c1209c9e47a306e124c5fcf01bf193a754b44f4a3fe7f7690dcdc002515cd730283907115f711e764d4fa0da80ee1ac53477eed6fce5e57890c01745b4a6b8fad9bfcc6ffb75c2a6196317158059eb1ed6aa31adbe5bf4e4e761f5e0110f86b76e5a4ebf2a59329053590921fdc42530f293757a307fe01cccc1f8c33d45c72fbc22258ef17192a52520b48709dc0983e578945b2cbf70f691705b4c64f6d9c1e93b61a8c4061106bd0f241a2c51347f3e0dd25007c252cbce3295a4f74c7f8dabfd6f5829840d74d81a213c1292f3cb1854895a5c266988561a17619dbae6c8f705e121da5a0c05154d6df2d5dd7cf4e62599a8cf7c1fa10cb243fb2c7b797b5bb88549cab23228321b8a189da5ef0c292549939fff6daeb20561a9c37aa0047849423a3b492a87eb7a2162dc06cf721cece0588c8629c30bbf08822f092095c33141bf29c4992622c62df13196030aca5366a1fdf1905e1f1b63e23ae10f9b86aaaa746d4d6cc9d651b2048dfa47f635744b5ef280c3fee73ffe36ddacbf052d34b5647760dd959661577e3bd512678827e9f9fbb5f8892b0f169736fd548a8bad46e50bc02ed95ab0dbef8e4cc414c85f15ea29f01bbddb5a8a7d6b8b043e6e62ff006911400a5e82383eb91782c8aaa47db268db2120f4166cdee6f20b6cdc7fb6dbfbb759bcb1e1958bc6e7e54ec87fd4542e025cc1c09afec81855ceb585ad784f361b9e795f6eac9c9ded9ceb4f3e4e6c52a32a430b8b16cc366a8a305d67ccc1f4e40e5c5152ea74bf02d9d4d1a800d819c74985a73b588b78bac078ddbc22172fa32ff3126b29c13c035779aac24591903955d89150be03248143fc43c39952b98337dcb050eddf0d83c2df4125dbf3e081c1d895eba63ab77914a352ace3c64d0b609b39b96c295d16954c552cd9cb4b87fba338b3d520f607a6c6355b80806e5b394a14bd7bb9682461eb1cdaf28a53087969a41eadf362bb754a3def26099299266191d4f163d7b8faaf389163d848c1ad7019a2311cbd9ff4718f25d61aaea11722d1ca6323a58cd8567be490112a67f2f76224ec487704fb3f46d0bb21625531c73f8ce8623254677c88127110fba6a1f03bba3543ce8c4b00d553cd6b9baf2f65ddaea7d89ae14038807f69221c9cbe238c4b4a0826297727cfd46c9a0e4be5149fcf6fb3200e2e3843f7a70120931d3174825966ed11af96b6e204f9e882cc4802195d3f80c4119030bc55291b7de751e266af64cfd05a23a7e404135723ed5e369e96cf6db5a601055f01362947f62dd9e94af62f1ea462549f82cff43625e793ffa04abe84b1f0a023ae87ef3ccabda1b190dd43d1927de4811b19726f5ac4efe197b4e71d0faad338ae492b2fd838b46562a8319aab8048116bdd159477bdc7ea77219d9d8dddad7bd3ab694917a2fe0f15e72a2f0944d63ec25ff0ac30b8aafad2c3485a2fb584f924129be0381e3ae2745ce4247bbd1a8cce8841ad5995f4be12d8ad5caf31d97126a9bb37810128ebeb8b224f84f878b51d6b2ade2f39726b1b9f792dbf421523b838a4c3992421aeed09b693f85d0f0355da9542f43014cf9f750e4d7aadc9cdbca505c5404a41ee350e563cc1875d98cc19f794e21a32694855cb203108bf7e8ca05246365c45ede7e1d5292019d4d36e8f05ddfa7fee95a32451e86ce3ce85115945b40fe0a36c06c721a8588a4e8bd673441c7265ce9ccd07405f95650a4a338977896ea37db10b17e39af6da767c6313ac231946f1359f744feffc4d8aaff9d37acad7243d7be7df7fa91e5ef3ffbfb4672b53a2c7d3fc493d5f377bda63036033c2396279dd2ff61e5f0b738393ed839156e4abf137d25c5df9e1b40f1185be988b53672cbe7b8f787e82baf5ce06dd73ce6574ca4fc01fef2cf1559e58395d99ce361074fd5cd1f37b54346a4a15a795ec2bb9bcb171b866772ed3964640a01f74cbf741e861cb19d60d1a8e014e6aeba6713d997ec5f1d9677d47f496e0d6aa970e76ccae380d839ed8a04bf59426a0488f042d8a4253780dd9faa1f9eff1b5415855584031ab05a332f053f0b975ac28ae6bbffbcfc51f3a6770ab4c881e713bf7addde8bff20ff0deeb22ca4d46be397bea1aef450eb6719d64dd64f58807b72e019a7544181e958e8c4e0e91bfa24b4499a8992c836fefbd2f5595abc191ea6509ca96c322984e5ee68d545fc5c96e1394c2b3a3fc4ffab3b492cf082e211480f65272b5e18d001407e7ed895e7133ccc767c8b14068042377cc6529c56c2c2d22d048ae57746e30c56f7513c6767a159600d7cab35a58bb439cd6aad8a380d51738982a2cecbc0a283152503a02c1168369345ca1296246dedad6a5864dd378b048994559e9586f39633be2ce55c3e5bb551138a387e253d3ee660bc01bc234029417614ffb0df9679436ddb994d6b4e0b4f69f27c8dc436278d2494c346d19d1983ddcee7f730243f1bcc2916f75fd29d0806fcbd99dde88f4d6dc48c2ed8b7a4473a9c3446677653e7ff5e37ff785f70c9cef0ade332d06d37bbdfdfa886388d59df8b3f416b4426f12e7233a7a1bfae6277cae9d765c90eb26f99e484e8b16a6775458db1ad51f18ba9c059d01f7aa3a76e34c12525d1bb0b1304320e451412975fab529e8ad3e950800e2ad7ff8d4793274c215f5a92ed840d9f3b33c3d7dd6ed4024dae3a8b397441a93df8a8a2746d732dba8e6887e3a7958cdb56c6859e63124ba7e61dc18af4cb099130e48ff0ec9bc179af9c8696fd41bceedae111fd0b28e262452a0ca7f2c62d27c764bf5624dbd2aa41d62be05a578fa18a3e5852529489c597667644c1375811d0ff7c99420bbd14a87c7a832564b7473ca7e9b4690db16f134a45a6db65fc00470885f54377591996efd61f50083cc06545c8e53ba520b2d48274408cedc33b201ec0696803b2813a6281258a7770dc410e3250d8341a89f456884ac5063dea8ff11e4b2bcaa6cab43109aad6e56faa3595e522b32c3fdb0e6b8531d6eb6a02081095aef21f87f9db2a79f9b92e10e49aa97a78ee3e4c5b881d84bb3856b0970c2868cd73a0dfe73fd821333ecb380b9b0dfb80acad822c35b14d8e98cba79ef0f3b9b365eee701ea01cbbadfc40e49c716686866612f781484a5ef7f204a69cda0045cc8ca2010dcc0cb50d0e0c19750b4e425dc3292a8581011df6631b3fa8096f26d1960ad08a3de505b55cd219f4d4a6d9b303b34635c4f8d81e570e2a7ca80762664ae5384fd3c32ca8fea505010d099f269fe18654dc952899366a9c536c211ba2519ef06a579fdc17a997f889aafbf5bb0122d2a54c0d523fd1989cbd3925cd69063f4c6cd6a453013415ea5104f6662fad9061176d3b45a846c7ce1eb665b614cc2faae07eb8463dab9fe1bcfe8c682bde64d3bfef3cd318c3f1c44687d7afad26a31f98cd5fc17d8f5259cf84193bffc716c187b35d11f78b41c61340ec619bc8a07d58fda5c0efbfa4aed17f72408fb879b2cc4cbff1d1fdaca53e02b99f0e2c03c660727eb18c9deca72ebc621201054ce055235fd995cee916a1fcfdc26012c00c6d75e62a0a855efd1800627faa1980021ffef915ffc14fdaf05caa050196c0e27f95592bf1ced55b1f0418b6d95c8d0820f2a497fbc7e58d3079f781471e8195b74cac16fb8cebbfff7cefde53cebced81db6833cc61f7efaa9e1b745c3f924c091ce1f46db44ebb6564f77f495da3eb0823aae0821b6920e9801bb0e0fabf4683089976f4602bc83551a3ef2dbdeaeaafce889e5b839e95f646fceaf4274fbc9f0102ad95f586b0eafb2b579ccf207ebe15e89de46afab32eaecf387ec60af396846af9b73eaecf387edc0a7947ae1afed91bd33366d0d65921eec58669a0980a934f081f8cd6b142ee056a24de89af16fff2c2fd94e4b356ca7b62d5e5bf2c819e857cb6d2aceabf39313d637a6fa559d97f19223d9ff6de2aabc07568fc468a119c7842f8385a8f35b90834dc90b4b3071a7ec3fdc965fe1fc1375645052558fe31429f5411d444ab8301b7ff7a9bff3f88544c803ed41c4470fdefd6aa3f80aac5c2456bcd2082dbff746dfdfba73a36045ad60e2238fd6f6bd4dfa0fef0c5d7ea32f8e1f63f5d5bff04d5c30ef1239b70fba77c79704d9b80484c021cf88053b0c011ff5e90d5d900108d0f811becf68fe340c9903f43870d2870aa3f8bb182945589841162524d0a47fa7b8e062cf2ed9d008938755ac002dffa5b4de64c3d0d0e60de00ae61eb769fb4379d985728c889dc409af17b6620005cdee7dc50a424b0cd08ee0df43492b88692bb08fac0411b14ceed20258fade8449e7407a5037a108716273d0e6a00347186c4198d213d4804135bba836e4399dbed75774d8ba52d4e41defa1e1088f3a222a2aa7b901307c325d0f7bf9073dda975e3478e2a2720f7befb93fc2b167960bfbe60815f222db7cb526676f9d4ddc261648bd3cfe351883d3e3e7ae6829c44d98af529c45e1f5f7d73019c44d9cdf214629e1d9faab9e04ca2acf3b237c1a1641fbab643115c744e802007573d71106c018e90aabdb4affa6440ce7ba8aebeed552495680f235539585b53106774952c64e786edbecef730383e4a40069c6bed9dfb15b27323a3b6f3388ac81c9ca9e4d92cf3fc958191e251c5391b550d861bfec6f8da324570f0af0137f66cc92ce483f18b452cefd8e28e7c7ab0cd63c5370604beb3dd1af208ca32d3035c4532de916c6e4cf6796571322222b6831453359cd82f6fe655a6901f3ad8717177502dd0c33d9aac042193d84300c1010e2e1d2a0a0fa221d09d091a803106101ce0e0d2a1a2f0201a02dd1998643065903918222e0fa67ea24a2881c2c9996433603463b90d9efccc3475e58fe3e085464845844ec6aa5478a6f7a98fe693c51b5b03c5d07f79e302e0a5f70ff4b525f789843f210fe2c5aafff43f417dd7fdd808f5c5a1f9f630b3b3b555be337ecf7c265e31e2f6d650dc06ffca797268df934a9e6b4d7911cb35622a2388fa8c649729f81167accdb09d797cbb8280cd21a72b53d56b7a9799b37328191290d53240fcc61154c388d0413fba259ea640bdf36bda793f40a3bfc2bed59ea1b75e444f398c77173dd9d52b1d2ec13a9bbfb835a0404813964906c1301c32c375c8146e86739485203bee1c207cbfb89c96494bb9af6bded9947229cc442a4a692d658d095614cb30beba81fc11febb175e1cce29c0aa800ef4bf14483911c6e6cf6bebe6d42756904a115012b82c91c18b3745b26d380183b2060963fb8881e273d8bcad8a972d5c293dd37830b4fecce55eb7b100053a438b832bc61bad85a1a351488a5ef22e0d47d64214a4699b071a53e3c0d6e869cd08b4b3949f9ea7abe536a56b0d1a25474a6956e6ca0a7f4580047e8bfb7ccf5dd19653762f1e7593bffed11bd3c9365bed7e694ec0a9a5e7862f5dc83dcfbd7419ddf5e7f6a2526940a84cf119daa3ba64578c7156319ecbb73f0f23e795a154ef78824213f0a02710e9a46c37ff2dd84a40df616d265b974fa0ee089ecee4042efbeb612d7a9fdcef409d4aaed4d9733f17090b45193544a59bd60a8667b701a6b8ccd0d7c2b1293827dfa3ba725e8596d8d916b15767950035be6b5be8cdd0ab27638e76db52850df4d79ccd3d02c978c95257fb9b390c8e966e0a603a76bf3215200ff60531dffef92f40c3a8aaebe498d65a73d67996c354f4d7d25ed3438884179606fbaaa9746b455d80e1050316ff4dddd0434c29dd7a68492bb0aa965d7b262c93cd568f35fd87f2cc19e02d3178b334271003b305cdb6ecc4a0eb10dcaaecd0b28e2daa290d15b119e4ce35167f38723d290f28a973395130f7ce75c1719d409b8c7a43c623cf8adcc98e046d21fcffaca163724af94b68d83b195d15200a44a6aa26df3bb8434e9a5b0f293d0bf7cbe71e3a6ac50241f774210b6d1b5a44edaab42de121d8dbb454103653022182130f4049b26fbbadae5d7ceee2c36c98a08936123e5582c012089184cb4be726b1490984dc5830f6a893d7bd290fcf64adac4513f4ab23dafe2981b0f0fbbc5ca5744d06bd2e6d92723de18c199aaca0d9619d8efd2a363794cde142a2aa720596a5fdf781d2c20f251a4f06bfa009341ee30f7e83c053c3da819c4d7e76cc77b55be741ccc248e3a532b9ddf77c6a9d9ed0e7e6b701e8cb4d3aaef4b11340fd51ddfc4e478d0e1305fa479c955a3b3182bcc9f5c906c22dbb939dc378d295ffa92fafd73c40aebbaa454bb0fcefaebe1c71c9ddb65862e067ae8bde8a1b498c114050da1dbe007e14e5c591e81b17ab08ce03f09e9e87a94c3420b3c94e4dbe491386290f99bc6fcce855e45fb0ae05f5af022ae6ea552e688b91d95f6fd5c19f4eaf6e0e279517caf106d949ae759e289ffb3a71379761e2d2b50ed1008f034e4f0234be8dc0f1b9252148757e10e04736eb4c10dd1a69fdf7bf2bc75819a24ad1ccb4217058f4b2de13f29621d0fb2958b576a01d9cbae9fc36bcc3e4545d5714c8b8d17dafe868affd25b40d6b361e4d1c1f866b5053916b2a2fcd513a4e39f43302fbb3a417b386dae60fedff5ba28515c3810367347f81d9ab706bedce609472ace878cd0fd037e1c9296a3e182eecf063a64d1f17a1234ab5c9fd5634982d46ff7aaa0f30506f4ffbc7a4fc4939260251dee51f64dd3b89c8c48610e1ea70d9e27c3192474dacb7ca7cf4c6d03ba814e9855005c4614f6929692b2ee2101ac470ad5a6cfc56046d637b85acd1463dd542cb4cb511f1d2d0efd6f6a4aabe87dcfe2c88add73a7a9eb0818cd355c114e5b1c9456ca9ffb4e4fb4f3de2dbb44f19b9033c04856f56e8b77736f331a757b77fdcc7d51f7f28cf21698d193d5605b1d72125a6693ed546902422f3433c6ae49521d203e4bd9a6c89807a77812d659d6b30b895440c05df26f47f45a999190044ecff8c5660e04a02b8014ec4afa2bf6be71a03afaf4f111f81a07d2353e67362d8df54e3001efa798b263ac44a1dce3d00a60448a0b4b4a9a98b03c2b669dc41fbf95a316310381928c96f98ae2763fd36c5691343fd9b461b89befae60ed8a4f403d9b760ad5bd8ceb20326d4a899288044df883f3fb062c7653791ebc78e8b43886688755240f6abd24bb3e075b6401409cd8dcc031aa14ab31cfae32a9244a0c130bf85225e888a74c67d1ad11061ad08e92557e05d81540035ae9411e315589732b26b5f660a8bcac2b739c9456ea8216df980f5ac5541ecd610e1f3fcbbb2c9b3ce3a2adbd8fe336eee38d8258ea086b82201623061e5f4e505e6c21818a0f48e7af5f6ef3dce3a68401fa8ae1ff52a8e7e55b8376d7b6ab9ecaa6a086004e1704fd051b39ec76f397ac5bf2588f4fee9a8430243c7933f0bd6e206fb535b956c07fd41dab7173ab8074f55cea221d56db404acf75ce888564c7837c534413c16c9456b86d4b8f58f1ed8ce4c9d7b77dbd3f56a34a95c4254fca555e5487208e6d49281944b4a1228facde5e8bc311589ec4cfe237af8ef010d0b371b3b17ce16fc38de503a4a82e9014d73263978d6c4c3c861295871dd1950c76c32f750949219a86f8ac82b82dc67488c0c1b6a22b06cdeea444cf5e1e6e3635113929ee8a1d13929d3d178e4d518722baf5800f1823ff747c34ecb14de2453ebe0419b98a066e9117578aa4ac2f6a615277e12de517193a500398f91ed0d635acc9feb2708e7f8e1826eb0d753466589b9a7148c908fc75dd917cf801a74681513d5f494bfa471e890642e5998f27c0421268a079c834b9c960b0c080bfb3035771b945f3d28fd241adc87edd6de1e7d359fa14195b2b4ea8305e89c7f97a1c470887787867bbb3dfdaa297cc3e37b92fa0e484dee4f682f90702afa1e0b86f834210e2b847f0380a4a4b50c0a1335d2d34ec515941b2a4b4dd1cbc8b0949fd46f1d4fed100c4a8b64429352d270cfb440a07faf72a457b181c4929d406a753c26056095989c863a08dfcaa7c9700a68a5c04ca81c5ff05a0d7f786b0b79b897df1ccd138788e8a9db955d7761975dd935f7ab8911f795b6fa0a84e3be3f19c84728952b31e6450069a2a1dde0fa20f8a08bbd1de325ee0834b796c9a95f9403243e925780521bfbb9a2aa2871f22280bb30a50fd4e8f89936e6b7a90eff23dcbf17b35086dc83918207dc633aa5e76ba0ba7d1b4fbe332da3cc796c2cb14d9505b64748d1cc4745833b9252c779a7712193afaab87836f28ac35ab8798a951d0107551e235b8fb3fd35002c9aa99638437c95a01d1f9423e21ad98570bb7e47b9d90f4ba3e55c9069a8e40a2be1028b5d7b9cf9284bfc07f7c0a04aa5bb752e97492cf82d11012a38e9d15701102ae67ab1cd042782025d8361d93acaafedaa97e7ca3c3dd17a2ce4c3dc76858d2aacbd89619af14ebf9f8dbf1da5b890d9456730851c755dc2b92376df033daf5b5baac06433530266a6dbaef1fcb978fbfa8b39b4b3c152044444ba838cee6bc64063d64271e95873fb2afc8735a404d40e207b28b2c9cb581e629e1149260bc27dc03b417d43b3154c4031148959cca171752a87952e890c4a65c8a70649002f5e3d578b03b5854ea64e18314d4a54d7454e0c0d2c90472bf233e5b3e876e67d51734568bc32bb4c85fb37a08283b6c4baa479bc416f1cf2d4a7108d86dea64fb0e667e3db03a1e6d8facf776651b9d5efde0d3d0e1a29835ef305fa13313358bff79ec9c22a1ded10100b43e9453ea15e3e357071bcd9826a09f3025ef13b708b2869b2ac38829ac96e548aeae3c629d6f907197fe2076e23f51c5279bc16345effb885169d37b0b9efa3527fd663eacd046d8c98bce534dab101c0b66672b40fa15f0dcfec567c32103a571cece81a9c8609e67450eed5b3ffcd49718d134cc0dd8e6284660e83daa468cb3cd900402e597bcea701f58c9d249e57fe8293d24a768559635db49a57daefced0a1f3b4093434fdef7483e9a8ac66c214a12e9f4479a9e306682165c919443667ae260d9224bdc94cb61d6ec3a5098eecbf9c9a5a89a9f857e70e42dc0ff26b1254feed9e46428bb7d6293126873f95b97607aa32ef27e819abb0e0f9dd2d12bd7f77d7e2014d6bc4212c361cb1b3b1b1736827df4871ee71900c4f2a4dd2196382dff8a2549e4dd5f1a5b0bd28a0ef7705332d601908829566c23c05af10f8a960b01477df8c51d88994de43ff0067c7efbfaca50f2c8a77cca0979c66add06874679d00bca8e130c8bcdee79111ff6279ebf122b73a816793050dee505059885035003abaddf011b4c9d04a864682042712689c991893c4c92a74fa299514b5421a56ac43b9ffd84319f7712986fe7f311ad7a04511538894b785f460db84352a170cb5d633ebb61b01c05c7fbfd71cf444f98cade0b9af5a80c666b2cb46f6f4d612bc07078f4eb3ed692048bb48fa26946f18d1ce543411b1444442c9bfa7a6f58defc94dfb663efead6dbce996a71f77e099ff2c839e420a7a0a19e091640dd2871103221ad5006f6ffcda43b903fcfbfbd1df24516aa248ec1c820287217e05384e8f83ba3e189200c0e31960833a6502d89f644be49adfeef146c95d0f5e2a6a12b858cac24d43e7847b10109fb717d18858c4594912a14b89079b0f9ba03b6109f736855b6bd126b0d882211d710068126e4d97cbb7b57488330d99554cba0d7253b3b020b44b51820d1b955120bb1be96d3673c38f6842fc9e3d3b58339d37b9796eb0f0a8df67a192431e5c9113fb6fdf701fbeb942334f31b324d84d7151c3aefe10c0351738bd2de29734ceefb0ded8ee41f16de411a1264d0129e8d70765572cd8642ccdf231b2ad288be93b05ed0b0c1732bdc4d9a24ab9971f3d55499ef3c1a510f14633209a076e1b7be2b6002a0f63f68fe8bc7d9077e66980e89fdbd6db3fcd0c2a5df05b1400e57b1a8a1cba2d7417e9cb28affa31be30abeaf74300b7d1b77e8c0b46edcd171a1e0ffa4c32b80b5635e0d14c4bb2b1cda13c4be8906e4757edbcedb4c4717837a42b4ca9bd67fa4743ca4521d7c3f20767611b961a48a6c89260d6b317fb7f12802c315bf3de25d401921ffece4b629dfc3d3ae5bc6eafdf4c3185147c20510159fd33b275931c38e4c59e69ae0a705910481e73b48b08d1783db2a24a59f1c61c90caf368e7fc6700d174a750c3e8938d47de448af3009fb3d3b679a355b0a266d27d2ffa5fc76f71617274c0401750fb750a080e983df408619794d5629f783a9ce671e6160b59f0034013dabcb15d13a61cc16bf96ffc9672e48eaee85400f2795cb7eedd22d05f3c3ebb1dd9922ecb3f1d90bccf14ba382a9eed49b37e0385d5a40a45e1920a2203f88b8121458453096cba7b07134b22bacc10352e728ddf8cb594512c184267887815d422a62cf1a8c8c35f899d258792fe75f9e5ede69e45addb744c0514f087235de03657d279195564056e4053d752013a591506ef12d9c48a0539dac33103e591d6d8abea1b1d334228f92d3b5a49b60362861c6f889388bc88b5645e1be09c4460c81dd144b28271011107e549495e434cd6c1971340662f4d086bec5be7baa2fe77e520815dcad99b570e29495c447de20227346e0caeace70763ff688641cc11e90792f8cc10f7ab67085d0b7e1a0d65c24a4b6360d22a703e9d725765f461ffb38e75921443e0faaf5add41bf8396b4ae36f3bbcdcc0d1f87ccb15fac4c293bb0393dbe736441a3be4334694308e5e9753559706f9e9a7482473e8dba3d8edfdefd05fe54105d17bc8b5738e58572c543c1759a6d641d3411dfd13d7dc8156315394ba595a0a985ea1665a00ddd068c3bb02ffb73e097ef2fda4be85ab1f0fb5aafb526c17bd5240129eb789e23eeb19609977d1430fbfb1c6758601f5e246581c597bf288ff6a46d962706d017b91c3c7d481567ac72315568be6e38c49b58eb38c9645190e3b42df68d3635761441de9c31aa00eb1b1907f08ade10d046611f865eac93c734b611f357e4a72840efb8f8a040b54c500a8f964541bc41caa04364a9e84ae72fb4cff70858dae236558b6b9b5fc45d33de30467c2889fb91205ca10320bc33fa76d16f0d00f7edd20f771e10bfb07e14baef00f20bab8f281d2764bbbf29b26ebc0d374f7e1ea914402085bd705f404e8fd786862852b7ce5b8780b547eba39dcdae965c499f5aa5e804ba0cb894ffa0bc271b7faade5db46f00d99cb404dbeca48fe5bb985b85eb5a9b6e9f203929e435e7a2b15a5c9070f310478ba9550041dccb09119349fcd88f4296f89462d0138169599a7aa261854fb23495b2f07fecdd98da8175e57ee904e7c11c9456a00c1e259557f895472b056215feca2228c00130eacf379dd4ff13cb42115778015984c5ff78872bfd0d97cbb0ab3aa651cb3c903ba670ba1280c85239ad48a7f0f66aafcf4286ea0a168b2b35720a1d7f71e48825074bbaa31f83bf113a70d5430b6e462d0d1611df83d27fd68d544fed1d0678294046302d3db1b6a6d46d5f76820e1f4d798ea28dfff898d339f51fed6ad5e4d5806ae78af71b6dfca8e828939370f594fcd79967c843534f6139268f06bf49130f1b6081704880629680587d02dac1adf2f4053e63f07d49dd0566870e944f0d9bd642a20cf49bff4c0ffb2ee0f0c8367a5c132eee91aa42f56c04a832183e10ac000066f41a0c06462a088104c978f105950b105cb9e2ef21a261158d0da1a4e1433f2228e2577b66639e8b9771b8570baf61d109e8b2a70949f0c2382a043892a0a08dd2cc873924d6aad5ce73ac1f403468077259cd2136c4334d420e2165aa29f00f9bf4378cfc7e48ea7ac8313ca2cc287fc50486bdcada4011da0f131bd76b14573e3898e25fc01782efdb710c6356cfd73e54d797b0e3eb3a947552c4a17312b0af78b944b8a08699157ea19d68c6d81ac49ac93e6b1e26ef8a34bdac029002c2c9b83e0c23f89f711b51472831101ebe3a8f976c42e9319cf5c86672ce63d8eb746902849f3045fee09d1706a9aa4d9e29d1bc25ff8646df85e5a109cd7e2884c3f68c6934d5fe067d166566e2083e8d78bdcc6c29905031fe397506426f20525cd1d72f8f4d230b77261ed4b270eeb0548a5c9a0e8d9267123c3fa24fa00fa1954cd478c293f1c9582d5e7b6d6083d6fa33b695a9cdcd2737ca820ca724a3f6cc2ae182c120bf9f1e30f91f4896c9aa355622fc7d398d0c021a9364b5ddc562bd234bf33e6079e112b86a9614add5b3ec88bb4a479a5c934dda9b3c14fb8b25ae65f0b257b1d229f8991d337e1abb6fd3fa555b5e8793b6555d532f238ef0ec22fd8b2a9efcddf372cce05058c012b69721e921d523492f09e5e989387a25bd657ae4d3cd6999c2482459aa8588e6407259c5408d48e1a5f2f69035fe87e29f29559215eb4f38043643c6964eb809a088881a7a80e6272064ded66105a5c823ed09ba2d5eac31fc72b7ab28b223db64f99fcd5886daf6c5007a43a9aa173065932f82599ccd4a67c86d82db18cee4b533059c33a2928337394525fa0a23e2bb30cbbb3cd227f00165228b8deed0d0e6dd7f8a5fb9b37bbaa7ab32ab3910900343b7a97a8bced7ffb65193ef9ccf70eda841f6863c66085418a4f7c4a7c2ac2de610263675f0eb010bc2122799e42b1ee858437a0eacd34b85629f1ba82e2679bfa8c472173d64f259130bba13913f64ba4dceddd514510ce8f83704b93659b7da18d837a6f45f59d395d04fcc79e7465917826332423e82a79b71cf6ec4050474952df6cbd7347bb8e1268258374fa9947bddc240dc6e404c6f2baf1327c4445f9a56299de61b54ab9134e61f6f193f68f7d1da0297128173fab36e1c9dae19a07a67bff6aae9bede19502f5a2f5d2fbd574d2ffd7a0492dfa6034483a044cfe712f3eab38a01cda74359008ccadbaaade4553ea0d9a3c123cf9d4b2ea692dfcc6a7fa8db33426f57cdd2ab13ccedc64eeaef1fb58f3a882b7851ff34f0aea1065df785b6d7c53d338b429c52738898c24babc0ebcbf173441ed4eb3db39b8417602b5268bb19704489d015922581a78c1bf38cce634cb1d26f7621a47f9680239c01f7c273d09d06950f253b36d7ffebe1e3de12e4c38e18edf3c320b864ea2080e1fec2ae24de81fdb9d95ade976b85f66a47c6c43cc4344072808195b6ee418b15063addd97d75c3953ab1894fa5cb6a7017d7dbdd96ef58dd3df0eaa57cb5085af7586249b8f351fdf636eba9fdc4744146bde75dab1d0775c7b3ee0da8808afde7a7650b4be856397a5ea8874213106746fa2713df8a4ff52caf9d08b7d12e67fd32309eff3f8b7dc7dd94ecc4e4fe09a303779f9bbfb9d10b2d6c9538788020c6fb9077146e96876bd086457cb7f455d70ba85f0deafd9be23e64fa0180fe70d03fe1067e2033ecf7d483ac21ead3d5049b203ddbc13fdaa3b04da340df6c308b9dfd632ebd7def3604ed5f4c3e6086ceb227848223d832517e774ca850dcce9e434d417ce2cd56cc11395ad9af86c0055b07b12b896aa261fa96397465a5561d0be2cd510b1fd2813c0bcd16b54b38c1e8baadf85260fc4f6bfa74edb25ffed17de80aa85df2483367db994eb696b9b21e6244f1f404fc659248a9195bc2005377806ba7b55d06b8c552bcce9d470019c400370c7ea72937311484d39a04cfe703d3cc96e9b3b58efaabd2715a4e404ad8d3990b6cc4a6b21bf4be87057d585d73b91b05025dca44140389d586820804d25d161a9397c97c0769ba2f51b8c3fd047f6a5d824fee74ae57d69edfc3b29a6b57b0847a7d4bc8f162336900f5f0fc93cf78b618ca0c86a2882bbb6f83dae67a1383d30ffd0031d83ed82206855242d295916971e9fd4bf9465a683ccb629e9e700c7270e05c4fc33791bc7cbc77c5e321f788f8310906e2e1bdd891ef173fafaf3e9c998caf7836df66f2280e776597d97350a488eff107645599df609cb4c2771d3493424a44d6cf4341a0d3b5f4626114ed03da146adc7de1230a4e1103436d6b49ca4e77dfc9350504a4802621efea602d9a7eb45e07d3a84c66da661f7aa6dc9b5035b6a6d4b97a361c359a97a07726920fc59519b2943a2cf76c977c0c3cc6c38a85d776c2fef9b0bb7324f178ef9519438ac47ffa40c3e0746293cb58a6cc87bc269f7634ea58e0ea42a470e52a852ca13f51b3d30d11217696e9a381a249f03a869db856fe618640c673974a7ab8a972d81f00c5cb8e4aa1269529bd4076f9dfbdfd5ce4995a5aa7a5f7d4a9c917a9d468eb965d0337a3d360b2ee54583f0d51c102ed78e4a1a8b3099b3c2d0b6929801d48621344bbb7a83b9aac328a46596651c54eca13829b6b6845db1c1e74a1ef5d89a05da120357db66ed7fa340beec8718936e83a61d775e2eb56a757458d529cb26bdcc638e7a34e58cfbbe81c1d8a29d3f9a30aa6067f1d6d9d28ba345c6e38ec74fb586a6c127bc080fc6ca625ced651950cba141505554916329615a8b5a95a84f2f76adae0933baa1b32407d8ff4f8da693feba68ccf6bb39fc832681dbf3faa397e1aaad6c2fa2d54fbb3a85c1626d2794efaefa2a4f3904181c84c261a722d70146a777b718247a427aad474638255e6ebfea2ae598d5d9ee80dfea15e542900a123e162ce9a31ae2ffa5df4f1ab2f36941fe961a5ca1ee75bd6274123e40d32b85051c3beeef644377ddb555905438ef26f8e176a999f757f8ea516605f4cab2d506a4e0bb8fb8a3e798cb51ca9202905753fe0e1901aee81318fa16c258896c4974bed848bb68e4938d7dd2ff6d87cf5a60fb3756d713f134931454f64156421cc9810d4ec91d0ee1d7e9ebf9b3b3a9c0621796392026c00f51234b325083dbac576dff0815818a2e51f654ad144530fcb9c0e6d2978e2d7dc69f62eaa5842da707f27a4624994742d5ad012ed585ce0a2f9e9d241a81ed6bd5b5d01cc3fb82868770cdd8234ddf49cbb411c11689f76c7bd3e43cd2fba3075f8e67b3c5b72f0c0c2e6fb5b1f13257b18d2904d1eaab431fac4a310dd8f216baa6a2786d8c3f19927a7d42371ae72a95667c9a3b72eca2e53aa0f9a2bb8163d0d5543ec631285163b86f8474bea733dc27895535732a56e4bc957bde7b11efa4700b8d3513fa2adc1bb98026fa4ae2e7b9cb3d1746f907cd1a637eb2e1073a988571313b9c86e734981726f058b362accbefea7b32daa6549c90db7778e21cb416bf0d634c069a26458678274d02b70cb4d279d351a9b91a66b931336e3b73e1418d3c3018a7f451de6376a965ac24aa377c2616d7a7aa2fe303e9a9bad14b4ef2e20e74a156d25f987017645704f852920b672521393ec2e7e426a838fa05ef6b8d2d0dba4987e0ca618cc9123aa8ab2b5b6f9274588a0265e11ef296ac494983d0610cc5c6cad77fa5694aac38eea58df13d1d88aee8dc4df40dc0de2de20ce0de26e24fe26e26e24fe26f16e50f16db601e9b5901fe81f4c4c020c508e81f5d02510074c41b8547b7432875694492836f7af52d40af112f5c598af1f2f50f3cd95253a70da547afc753150faffa7b3744fe4e401182077729969ee54d6d51365856954f3188749a28c545df3c59e0d23e2b64648dbd5183dc84a7511cd95753e16356445f5438244c72ea8158eb2a75e6c5fc9f5366a75d50aba2a13701672012c9c72a68d15cfc92ae9c506bc89dd137261c78dc03462ffd89506e6eafd89301be8b0df5a5c3cf6e231e6143ae50617877a6bb8d863b779c7c6548dd64220d490886ffe198105fa444fc5f756a43446cf6763e59f70109d883d56891b11f9bb83fa32ec81df961315852c2b7ebaf01b6d3f03ee97012ec6497eb832b9b1d7ccf2949f7ea8d2ba0faf2400f852d1e9ab5f7ad34825ba7b32a7a95a659765e597681c08318f283e93af39008da8ba3127f2605cc5acb6bfe836953c6779c63e88ea8ff1601e1b5d84330e1cc56bd915f85025cb38c42600a8cd6929d4d0cefe70cbe5532fe581ec766898c1e3e223e319aff9ef8f7e068561f32a250b5866eaa377cd01c445fba7be92b4b1939e4e1ba8c5f13307a0133d5ca184e419da66dc47ea1caf24010d428d76e339d1a5ef2dd98b174b9890d0f20e7906b830b51328e14ba91aba3541fc080740683833fa43b1c2179923005d465d1770da9327b08afb57017709848ee8bfb591d93e57b3aea7e9e9bcc03b2e8764ecb205be5f59b9cdb2e8c1491fd9d2c6330649ea1195acd36e1666158c6ef307aaa7434f59e177a9a111d9de1d4d958c90bee1a40907d862580af3680d5ac6db548b581307757e55809322ffa5308399d2d2a306403b3712d71fcbcfb58ff3c9dcd27a7872be5f0e1a975316ab7368242c4c626aa6eade31d2d42901d3846b96836faa55b2caa2249c1148a9be8c9dde01ea52c44530cb2e5e573d61fb0390881943a6d211ac27b90989005ec245c1945bea85f600592c66273c08362eac5d02530d4986c4b69d9d78023008e5fef565292c71773d6d6abf1d779f608b5859852643f41019d565c82eb99c0042eea5c47308ba00f281932cc3a66ef0f029ab9a35bb03db2c3615e1d02829e5cb9d37bb30e627002173b27975fe010316ffc1b880818d022d9e2d7c25912707ff9c271b2ee8f1ba8f419494446ecb15176515d2b9fffe79ba287f049de6c8c5801aa887970c33a4f75942e4f0eb56f0777d281235c3a7740ab98358fe1ebabd3543b75ac3f93315ce927c4568552306ab53afe61d8fef964930781345f5fef056da13c8974ab6fdbdbbdf36b187d3d4d5ea5f734025af0a42b3b4697fd10396a7140e24860f6ada572d382da80b3506d130b516fc973770eb25901c57aaaec8fe7d45bda3c07eed0996419a5a4ba69021b0ea1ab4422a0253fb83601bcdf7ccd2ad64b4d939f2ad6acdff1159ca8d4e02a9f0e3e99c0db61dc416ba5196f520e6c08db21208b1a750924523c43639f31f76014ae106112b0e3139d7ec3c24c54471ef1e740ff73636ea5df45a7c9142b6ed04c3d31f27749759d80eb05452b46ef638676827e1a8beb11056b6a6813e8b2d88260bfe80b3c06e0ba3fd152376299cb12e74bac5188131083e63309c0ffd01f890dd3901324f7abd643eb8f08b68e167fa05039c858df364aaa36a3a18cd3eaa6f21305b09e66344caa425cc4420e5c8b7667739aa7a821c5cf1aff470f9c08696c43ba38302d640f7b23b8fd286d26a00670649137abdc43477e8ca7f7edb313f117cf641b5d35b4d47e4a1caa746fb40419e234995d06f35d8f9e04dc2297899a0f77cd4318e32c9e7d4cd2cb7a6e415d3b745bc15815d98ff4e717b6441194f3e99ee8a7690783851119d1d7f80e271d32d08b9b28b67ee880e480366c74c1291d0f376ddddb6771107a9c484781aa01a834d12f3d772f94dd8e470f05c1c016c58e04a4d3a33a312eaae293d4b78c3f3e4def4a694d3568d4de2c5d24308b0781b70ff37029a252d18a746d9a290d76be6277f851e3da79656b04282f9461d86c77524389732ca69ae42248d8e64a042d91c75da8507ec00a07dbe22f08a54015f1ba4ae5ea06b027abefdd137cd85d9104ab9eb9a2098cdc7b59ea78a316d1d146a8e075f8b5e80506dfd1328446fb6391f4e699e2a38cad487156bca6afb56ec36ea96f2278a38203d431816bc55cd62a28324b22078542800c25921a5429c2509f169f44189140c93ec12f4a8512d1434d8c0a5a3ee32064bdba8700c3b71798aa209c7eb74502a7d3fe7b240a7648683986a0d184418b6b0c64637a10281a87c529f883fbef14fc10da315b47bcb91d4ca30d97ac9c44a99c3456139c1177220c43a287941afc544fd59b4922c402bdd58782346492b135062dabffb3e1010cf0dcc1978c62f114dc049b2ac7c1444a321eb4564e4163938218f27a7a02f1f2aa79c129e17c090c3c2dcb2cd14a5889662d1adf13b427ac4427fcd84fec8c9a559e62e4fb5802ad6c42d84146854984cc4506e705a58c80e2d10275eb89a6da58a89c6cf0ddb6a54ca882d3e5fcdfaeb5256c69b58f2880442db4782c524a049266b9b97f6a71b8d99ef91d6d70f6d032e08fd4d542b58f888847ce4418ff599d1cbefe643c8fbe58171ab24dd4b0717109ee1913e3f802df8147673432f88ce61ce0aa880cbfbbf29d60d355552c9140af4dcf74c87834b28114d4e6d9a5bc4fc0027e2d037a01ba46087614dbe512eaf1c07a923ba59a1a2422b4e29aaf0a1b2684129af155e220f6b7a8dc82440400db87275233cd8ef9a05c5a0a618acb3f0f1521f86ae182967b88c00a241fb26086ed98ec00a3bad58a8ccbd5d280441813bab0896a805dfa16a89e7a5c821b4390208106827687fdb4d4eccface43952a27aa86b2b4123afc0b1cb9779f706bd44287c00751c1d218fcf001043f0d5a894c6a7814009da82a18d2f36578e8d58b7de502a1027f566e5b21394300615180923439071decbba2c3335d969a36fe8762bb3237780b71d94acef87354065360a5ed3991b748de3d25da5c0e49bc697da726c9edf975070379c9dbb4eef8d740499b9cc3f461964c3b3908111672b835c9b2ea30c04671dacdcac77205b6cc5ff85eca2d18ebb82663c46291dce925cb868f92aeab2b8981860729eae0387d966004d6684b4ccd87b04e50b324519fd3f0b47151744ac7734ac9630dbfa4995848cbd8cac15983b9caa97583c12ba70be10211d3970bd6f08c020ef0a7c8b2a0a5c5cf9a5afce31eceac1e3794d80895a96ff6820063f5325e8c063bda0897b7753e8cc6543d63dda1c40194646eb6eef660e0439233b18ba7d2677dd049d304aeecdc76539751b21b65763b387dd3b97f5d72e7bf929c14f264888a9296e17d822b2e7e23b250cd549985ced248df24852ebcf7456acb15cad278a0597dba5a9013b50291d7ffe5ad2c9a32798b307fea71c9a88a27a17b581f321b216e393b2b4807b27b8e052694ee8a33c32ab4dd6e90222368503645e25377ae1a8347f82b4f86a0863d6a314433fa43a189b0a7fc3127e1b35e04db976391e1d55385d11d11b5ddac2cd34423eaf2dcac5b35d8dd4e10adc23a15cef57999862c7378d8d04c885c32f18dffa5272f9935df4a626f3c065f6a7e1e559a9fa7155b399dc0d85280236d53c0053d8a7b9cd82adc57a8a99daef49aef697bc5d974c013930f1612cc389dee2bf888d635c9e25d763a8f6b353907cb9d54a8409e07463ebc6288b5e010d5b1bb11143fa548143e6fae505b1d84372477de42595cd16315e1bd6711cb8e2312472c44de0a5d7ea0d6ce088b1ecedd994676ce82972931fd51d3a42521c6d9ae5f55adb83cabb98e79de8f706c1d6a1f1e8f5a8020b5987f218efff88add23e282fe460681f6616bd7590cc21fac45a96391afa1c966f2c48b2c39758193751bc1136a4f9b78b4829af85ed29fa6be5c09f1b465e96b5547e26e59dbaa12020b1832954e16212f78ad43e4ed0e305b359560b29cd1749b18cd8443017c6ea2cadc021181712e5a5838a829d1af63bad343853f58839587bd1e0ddda38745f0daeca8af876f470f904156eaec58aea59767d0f9929cbe4348da8c11a9dcb354ec93f707419043e43cdee5e36bc592e0c4ed4fbc4494a93c4191b55eb132f87a2a44d60452bd431cc155c643958125b876c243f340cca88a51b09d23197092211b32763f25598682f7ac45ab00d4c6ae546184d51b1106e0b63e6c3674362857c2042cdfaecac157bc92f995562b6c25fea3082f3065354f42342711617041671b544d9b9213549e3da4e3d5863200741666bbe92f82c4206bb3c73a360b6b2c2dfa021d7db958160baad3a9772df673556eff0d42701a086a2b788c256727ee9eab4d0dd3ee2e0e7707d12c02b6c98b808fe071f560ba5b65c5ef540bb56fc2d93da0920b8b4577d14f94bcd1c8c0600332c2812c77e63b2f197559ec86fd1793dca99f75aaed95e7924999ecece46230fc94f1dc66e8bd48e7bbed13c98be5c6734539fbace0190fb9efd9fc0af4d270f96791afe008328dd7607682c0c7e8db067ecf1ac3e0f9b54cc2266f7a52963dfbd38934f58c5666a5be371c82593c8272d8d9163db636ec4f0f5c28a4d52f0b28e6d9c09567cdb4695d8134921d3d4891c72c677ed9ac620110a2cd469aeb7ae3cf91fc1b3180ec0867cf203a3ef326e41b1618540bf7133daf681f32904d7c353e7253707664999a71a435b4262ff33cf3ba9ca4a5590df5226e7ebda7cc1e97fdb5db50be765f2bac2ec1208666c91286eff70de559badb4a33318a3cc297acf98646298a6a3054a45aaf8fe365b78210ace8e8c59ba151be14dc43b808e99f5995fb4898325a25ce8a85dbfd2ead9255627f6ac08a999d3d50c44881d9426899bc5dd3c6e05e0f645da40d94fdfae97116bdcff440907e80333b17beaecce10510c668d96403358d2c84b3e12b16d4b91f6d4d22bd4be07fcc9e825fb5319050bd3b2c160a4625a9e519e4b96b1ccef22213b5168aadff504749f690c4cbf976dc5876e6ce533870dab48614def054966c46aff9119000d3e6026f084badcb93be4d6afa0d1b0d3c367f4fc2a5e3d2b8802c38c321315b8b8a4b64b373a4cd19c3564436e5009768dd904966812a42fe54bad677a2583c45000ab23f25d1c77c0254d4636a7a0304a882e0b98846606a10763ba03cc0a0ae314bbb5481cd764e615476091668b46302a606151cf56a09aaa00f8cef39c2de24df360e7727c048ac920a9674182e6b625c7362321cf276c8eb0e71d85b3810057412bbfd21fba4a8dcab5279ffa5e8ab9e257fba576068df298baa9b7d6b1c4daf14616a5d91a5d5e02118b18e983b39027a3659c0f7c973f1bf443866d341f7879309b1f0408db9fa7f65217c49623acb3e23c5881e4905dbec04a4e26899a42a367644c3ac9935b6073b1fd1f514641a36dd262efa0435f7ac0bb29566073fa76987a0bde22d8e2f13017082d739f9480020430ee481efec20c2827f590b29c68ba6a52f284b5fc16f5cde0e990493994b0f6bd76332cc0e3ecb609617152b825be56fe4dbcb150f66f6f80618ddf885a8912a930a255bfdf9a0176e3a25314a9afc4b954b62e7f659cc6d27311db37eff3263ce3acce86e97ad79b344c53dedf670e16bafaa4c9d6e0d6709b722639e8742a866e9f0eee09e52b3f2b7be1f403b5610f95e781a470e9ca5d96d61d538209e06abfe1f5f49b3751653a69d894c694ec5921ff09b37920a63d3ca7ba812f4d9a9272a61ab0c0141f2ba7ab85e45b9fc87f43daed896d182530f44222f84824825f16ddb46403863fa9afe18cd88876960371bae47b62c8b998051b4c728b96d1a4cb02de8d290b0b62a1232a61249088da4e0ed4f96d96cd7517ce923c8479061150500a0208b1bc4b947af369765a68282052fae35267181384de3b79e100830a5f309187a096050e52ac26c3e4ed48f051e6675d435042cf7b72e917e2a3f38cc3b87fa63154080007798135964799df4e46546b4f132f53e82459304202a2035c1b2c8a6b146739d25ae1e0b1a9fc9fd2e8d48d6b1fa4128c428b4aa32e8e6cc5df71d28c557a8eecf69ca790411fc0bdb6c86947d504c20f03e88f3ec7cbe5d1f2e65b2eb9ce49f1fee64cbe1c9f8edf2e897a0482bc60dd30ee844219196d02d5697005e8eabb87cde8ab34f93315bea66bd75554dbd14e0e5daed5fc8a764df5fa491f89030dfcc1604edbcdbd70c956639e465bffdd0c389650b0e0cc18f8e6917a798ac97ef1f0356d41add9960d4dfb1721b7a94980930cc2d1da4f6943c08c180f9e77f77baecbab543c6bd821de4364e1bde4bc4a5313c15796055d04cafaf3530355049c8830408356734025de156d90572908b6a13c0aecf901968c5aa1031d80c1f0111d3f0c00135e506320e076d538cb9a663cb714406111bb1c5505b640909bdd57247d0f3148fde511febe9c7c70824c6c4a786b7850d730ceb35756ec2f3fdbc2e889924c87783c777128e09b48b8b9958e892dcdac7c48b500723fd182770f03e9e80c916f3f570440f1f05b0982d0a9918308c389ddf8977b6549dee4898d4db760755574c0666cc8bdb18135f3bf51fba65f291c3b147504ef51e3596c0aeea5c50af73c0f764f93b4983fede52488482bd8cc67a92934ede6529abfd032422042e6a84280f5510280b6d73c95a7126ca6d3c86cccdd3cbfa3ed38f3df0cff71cd6b880449123cbc7a637b4220adc5a9e5699f67df48a39e0410961201df84e849579b63d29e71131a407608bb9d8ea08a3b78d96b697369152ca14fe0e3b0f950e20eb6d66b3d92ca59a928d7e7ef6538418fda45bf0f798bd0feae3e39352f5789f9f5264d423ad1a329aa53c509f74ca683483c068f6329a4dc14633104c578c1562ad59f13a45b5f1afe0eb24b3f7ec2396d9df35c23898e0c77bfdc060361b83c14362afd7db1ffef0d726533dfda712525b9448f0b3dff178964a6d015327e10e5f82ff3e7cab4c74b5b0562692201351f0df6735994882ffce172aea61ab097ecd5a76fc9ad7dcccb0e2abef65bfe6333b5f2ea3b5bf2f63edeff15626ba56383ef2c36bf6977b9f78cad6733d7711052858957dbe9f0075ade749d7229d7415c249d759ec9ae97af6a4ae5ef6f9c05cab4cf481499289c49cae42b31fd35546a6eb89e35aedaf4896ae45423be92a34cb49d7d9bfecf58101ab32d135ca22d3550433a6ebf981b9e9fa1f18f003a33f30385d1dcc5e3f30d8c93895b0cadadf8b595960c068699529b3b575e68c9afdade29afdb9aaf6f7bd9df16587b61e923ffcb7b77dafdff97ddff781ff81e00782af4b3c04a69f05b87c11468d321860c60ca31fa7b87daffa08d4fe34a831d1598af9dcabd3c6efba998a0073d0068e3e8373f31a9c8ca3d3007170705ea3c1d16c7070f499120707a72cc77c73c25e250ebec1317c62187e6111cf741a9d46a7c90ab70079405083afd180200f086a346cd2004110d467341a9d0608823cbf6a352008f21415ed3cf839d4c10963212c34c39c3ffc1c1cdff3e7abd481f27c0e94e77b509ec781f2fc0d54867af6c9156d1d8c93ae622c5dcf5086c3c7b31dceb0902bd569741a9d6607bbacecfceba5cfecb0c16bbe345eafd7ebdb7969355a8d4ef32fd887f7de9dd76876d8ecf515dbd979fdf91acda9d3d8ebeb5bbc7762e1c3603bff2dc6ab56b35fb9a25c91b88a39ab6cffbee04ebac27e2dda3af72696aeb32be259ba0a6d9d0686ea3437e8a5d3e8342216d8bd825dd885c1320c7f0161b0118b7b553ec62af11712567e39ea5cbe9889625fc2cc58599a3fe22eb80b98ee908afb872118866198ae64f824f9182b7c322431162149a2617ed7e1fb977425c9900cef5588bf902489b370564892241992ff0ac3173311ce87e99af3a6f93a7136cdd73161e1f9e1f592838624f83837e898b6ba0bee82bbe02e271693b8b9c102cc97f33ccf0f7f397116cec25d709713636171af608fb160f80bec5758bae2acedfa60b09b9b83737e78b95c2eb80bee82bbe02eb8cb09c321183b662f978eb1b9a5187ee05c853e334e26aefff62ab44b213c248e26071d94e58b2fbe9be51d4331bd5778765d29ee12ee20d1d90ff9e36ccebb57a12fc45df0173676fc7c669cf78e25f91f89bbdca0321d47c74228c805b80bc67291f78ac45df41ed3f5860f435f28ee72b3ce1c7ef6529eae4562d70b5a74cdb6af421714651f9858c6da8431334ccf9c9bdeb9fa89c6c904f65fec61e679f3b11bbb31dfd4684e7d0696ea34374c95e059fe175a82094a4a30c1db7ca14e136be304fffa18133e34ce8b74c343e6c33e7f0e3bb55078e8f53acd0d3a5318ccd920dd1c8d4e034b359a1bf499af2fcd156216edf57b766a83bf16c984c8749ded55b673b8ca4459d1775e571ba47bce8174cce5de1b0603b74d95223270c5016050e9a1871e3220c68983c63a93ab60306bae162fa379d86bb9f115d8dddd3d144351082df7de2a34e49cb36b3c83ea8cdef77ddf48aa41138621599a29e372b95ca509e68b599224499a2f2e573b47bf4ad334cd172ccd953439c0ca8ba6b1c15f57b2d84bce9dbd8adbd18041938504ec6596e4e81243f0d3195f3348bff8cbfacb80551aab7bef07a6e9856b72ce190ccd385381effbbe50cc001661cc30138661283a7a7376958e9a1101d0050579ffe28bcbb842c3bd17e732a8d6f03c46067a5dd068c05ef59253efb0d7d9f6ab33d0d082461534c67dad9c4ab08bb131d516b1d0cbf419adc63192a97bab642225d8073fa6129aa5f1f8c3749589b19e64920b7ee822332c35673bdfd4959fcc686e50a8cfe0d94592deaa6b55251389a7ec7f362b759a0b244ba5a14e73836e7873cc03e7ea596821f19a0fc9178471debf60e31b3c4e41fa8e9b71cea7fe7b85b3f00c0fad536e1201e2b48cc9249908f6af37f1c3d2b5d524ead7cb1ec97f71d963ba6224f72ac5583708a74878963ff433269a24fb64577aa645b8cb05823f7f10a954ec7283f0e38cb3626b9c2b90aca10b80a0f93373e2ff0c965173cf9bbf496128893fdf7c6ad388d622911bd488ef9bffcc0e7cdfc45edfec3cceaba0f3390f9a3d22ec3cce2bd94995e8a49e7d725e74bdf8f8bc7952146f186210ccdfa7b5fe720631dedfcdcddffd810f3e368fc41e7c378de01b582c3d51f35fb0cfb9a25be4dbe29368915ce49e4ecb1ffab7c676fe287a0a863f0f58d1a18431cc9421831a1592d84012207640830656078438c2bdb802830c196b2650811cac8c5106135bb050e38a1ca850e25e81a18c0a64b161851ba654986265460621c6a8a1f585951ca65880e2882934b0058d2a358c74ef035678b8e2080800a15ddbc348808a59050a1d50420c9623b8b02187c3196496d86289aa1c8cfc9dc90d245011c2033390f1020682b861e717b71043f0d319b36854e9818a335d40c0015fe42af704f377e5fbbeef2b4551081cb060a38c153263e040c41946e0980d9859c20244f03006025ec2944153a5296b9330106709750ed637586106bcc1e045cd192b68b10219189ea8a2871664102325874ffde0030666c0801a6238d146152bbeb842092458d8001961a47184517ef72cd00a26f69a83f55fe860f6a148894c860732568a4e1948bc029917349920fea80c0e48e18022c28419c348874185cb0b64c66001092e8c3499145e10ca63e812c778a15d2eeaf0c1f00bbff0cc3ef76f4ef860fec6688d38479fe0f769adb5fe72d6c07775b601e6430268b4404507d490d980105fa068630628d458409b1cd9799e8fbf689d821149740085182a80812c3bcecfff3bbe7c41881b5fa878f012c604c47c018226151b86502387225ab84cd4606a71031a0cb880060d6334907ac1e18b28a20882011800c31c2f936931fc3e30d4e58f41a20c835cefee3972f5953fce56db3ffbebcfffa26b0c8a3306c5e93d47c47d5f5cb7be729ae723f210464250d1c319350c21040e79082ff3802c80a0818a23c0f0c01d3736bf8d37be3e9bfc72cef8de7baf7fe40bdd246cc5319cabde0eee2f7ffb74da45f7a73feafbd8fef4a7bf2fe7de7b8bd09b4b725f70df7d752a661e9bcc18e38b31c6d833f9c29f6217be2e865832304518010d68069011010d6081d1800fa471dfc5211498b040152cb4c0058ba6214acf071c1e2005d615109a86a9242e50c40d4e606106165b0fbabedc02ce8d0c7ee0077a209489391ce386470c3f50a603ce4e3a8e5a5840ebe40cb24643c5075888814ad50c5a902973acc059e2bc09e2156b40793631c2b4a8c1f5d2a2b76396a1534ae086bc023622a1e31a93e5ca147d80855a3ab840ad14707c1b886909dcc85940b1b56c6b0d7e20088263ecc8e28735cff6305bf4f6d763431e4597d66234a05d39e719b656228bd10236d5458d99187ab872c4161946ba85363c88210504ca9002849196c96499884c7768adb5d65a67bd434c9a3c135fdbc388d92245315a642cd1df9bcaa081d40bc8328fbcf0a275c6102dbca065caca14689298428a0a3a8841c61a20088220d883165da198174431ebac5d19cc202846850d822098434c05088220f86959978f89930de345837ac797f307a6b10abf58ce39af09b596410dd1add042034f0cd1c4144c44016a612503665060872972a840ce21be4c66c319593403c4a5ec14c51ce64cce39c42ddbb1c6cebf231b6d61a5016d8218a2cc163f333723022e336baf3b5e6932a84532cedcc8f28b2199582e65a7289e59e42f7fdfa77315e2165fd18fd08c9c6522320d92452ecaa9e7ef6322bc3f32a1d99e0589e2fbbeef0bc1d9f79191c4f7cd8204290a8365af415e510869f11b22ef98fd5f7989378421193288201953c8effcc0f73967200d57de317b331f76d173ce39e759fe72feb24ef3994130e79c73cee09de121cf00147a47fe1de05e5f488decd2601833a8f28359e7bf9f653c331a3639b22ce33bb8459acf3f7d86e1f77ddff77d2118668b32f6f77ddf99e3daebf9a00be4e80ab1803978263bf5a7add06cd161b0a44f4061850e583730c165062b6af0400d49a470258c0187d901fc51758133bce0c0150f00a32be345ca9a2628c0451a10449303a8c51c06cd0a1b04415083200886270862d1c606df0804c3e0409a0648200343f4b48c40158c306e9882430d4f943102f5a7331be597038220088117d6a28bb78c0d822008821a04417009f29e329d0124f09733990a6918be84665c645038a3cd17618821230c2fb098b9a10a0b16e8e200233048061f83af451004779c2f7425008220087efa6f6c0f8345d616b787c1026baf429e259b815cefd67b25efeaae7b8ee86c1eb9bddef6eca978537dc3bd42d9d73709db4c467023f0a53ccb28eafb28f063bad7d3ef5b83ee3a3b7fce7f631fc9eff7fb5231fbf8b9f386ad77f704007f4f91477b8eac5f4e5709b6a7229e89f87ae87add1d054f692cba40864f920e8e632ae2efdeaf0cbd4834e21df80ed39b2b674ed12647d2487824fcd13475f97a85e28f0f338f88e28f47cc310c61621e43334c7bc8f155c2643298691ab9a3f8ab6b7c12866130180c765d7c8dfb651a3145d175c4fcf143d48829fe488ee387a34886a1d34ad388b89bdc36e1b949f3348f98e2a3a669043ccb101dc18da85488f58800fbd72b51123b5fa6912fa65d39e3a8a34396b72ccbb2c4c931cb779da6914c9a44c6274d9df37c8d4fbe7664b29df3849946ce7b9ee7798e39a611d78fa38e59be699ae6e84a9bdc36655a84dc634ac64c233856c262e588ee984ac29bf2c99b721c9f670cd1db66f39823c0fe650058ead948cecdc9c9c9c9712cbecad7318d8c5f963ba6f931732c5de58763dae49a691172977f33923a65881a193f1c8271cc1c31cc31d33275a791ae147cfc3dfa8de4bf608f4e7fc4373a649d60f65f3bd89f289ea74cf6bfcec05b74f3197c11fcb104cf743dcf57bacaf6f766fafb67fb4bc3347f60ba76b0bf29abbdfae78af663d71fb83867fd69adbfeffb401004c122f946e014cf9cd9b94aaeb266cdd5e2656e04ce22f9af3f7effef03e4f7e487fe778a79eb7b5f879fc6d7ec5132ba9488e113618d180135298a370c3108e6efd35a7f39831887e5877e659c775fd737033f7f45c0dde4b601370882398c648945151a32990b967ea14a151961ca00a3881c4997d3f08b391b9721811d6b9cd93fdbc3ace165afb166934c4e0fb38610db83bb6fe0b8d1e1d4791c8f83933fe6e8ebfbcc66e7cf41736f1835caec7b6fec337abe8992a107aff79e9d8fa56b0f929bcfef1b6aedd9498fdca4eeb40cb563ef1b6afb9beffad7ce7b7a8324f619c9f966cf4d7ae44cdd69f9cd57fe1d74353f90fdf99fd9c1ddb2cfe9ebc110eecee7bb19c2ddb2d89fe94d0e672c4522fb9c3291fd9932c9f936c7d2366f9d7fe578f01dc949359ee9a46b9bd31547ea7886f31acf701c04d8f33c0fba46d93cd73c92370fcfeb63e68b3c9ffd5f1f83f17cf822cf67ff9799aee1b779f3ac7e235da7364f7ae3c3cfe611bc6f7c466fc462e91a8bc5beccb15c7ebe89c15326e64d7b8e7cefffbda7ebb84d26668c7c728da1a7c64337fff23f517f4dfd0dec71ec4917f265707a180f60b1c937dfc8a9f32f7475da259edda4abde37ef5edb791e9ef4e6775077da4dcadef3713ee765b27467b693ae7abf5eb0d71a7bf1757070fe447de7a04cdabc71522777c71ef6df77c4fcd8794d24b1587a1eb9794f6f503c9e1f731aec3d159d06fb21f84c9998fffdfa4ac9d7e7fe9cb6f9bbbded4dc2366ddaacd92d3ea2b3bf94097ecfa9df209fc2715f69905cf6f717cbfe1eafa5be1a63adcb5fb5d6bfeaad5b4d8e9fcd23e3af49f698f27ebbd075ea874d8e6beca91b3649a6bcdf63fa9948be2cf56eb246d945ae9aabed7bc5fbba89046ffc59cf20beaeec2c5e89a217b16a67576fe735a21a515f5c85a97055952a9b4714b14471a452452cad9458ca2ae5152a2f38916865c500f18a0ea296078058aa5439df24e2f96cde27f36845bc12adc42bd14abcb212afae402a4c44ae59a3abacd155d6ecfc55707ec55450918bc913963154ef7c2f4a8a5cf04cbc5ad3c689482c36d6d2869dd7328672859dcb2a58585858170b63b9b0ca2a632aaeb94244b1ac229655c4b28a58561171d25576938ae2d51a6295f88963f04cbcc279f32fece65f65c4325788177748e7f38f605c22b22afbe4bc8864bf8e6a6c229e5fc73423067650d1881b3423ab32110f4e8af34ad7bf71d3aac7294eea376815b76638d2bf91be0c9ba968a2be5f1062afa27895bf1462e7bfcac2f3a05526ca6b7a62952af18a95a8850a88a58a6c1cc7b1ac229656ca2a25969d9f079b6595b20a89456291582e1e9728f2a422095231b357511445114b144551144551144551144551144551144551e4e1e191f1f0f0c84451e4e1e111cd2c2f2496288aa2288aa2288aa2288aa2288aa2288aa2288a5859677986a528ba5cae711c499214c771748de337ba5ce338ba46d7388e233992e4389224398e6458966489b2791f489224499224cb922ccbf2659aafcf2ccbd22ccd0f964d190f8ff8899f298a3c2fe3e191a5a2288a595fc4ab2b56c42a62152a22152b51145f57727001ba8469b3b53d4c9b24367895894833a4183b3f8966e797f2657b98365a3bc776313d343b2cd12c3a0fa259766e5838d79bc6cebf8356bd91ce0d0be78a7b1b05b7b9e5575fc93ef9c12bf02a13816ab2cf5526a2da1203b1402f60161e02bf4a5f097bfbdc6bf4952fe19a34be7bd95c367896ef1a92f4d7ebc2cc0fa6ee4a3eec5e589310a3b0276f89abf0909329a6c23d3cf3722e719231348b4e8e78957df29b2888057a41c5ab9baa73c55665cce02131f0d09a4337f865c82d6ce535199c68ccca3e39eb06716bf1b0f358e6879d5fccd157b0682d5a084c8e599928b3c13ef9a9c050d1cabf8e65b095be82ad4678f29e77dc12b988426021cd90627c265966dccae2d66bdcda8aa15974de44c1ac9d1f04bfa42017ecd306cb46377edcd39b04cb312b138154c6ac555fa15ac7ac551cb333952d5ac14424d6f5c92fa6715e36d76795651ce793b93ccff3c9af3ccf27c192873f9df30b6f9c621af00c5bb90f9a44b0159ee53fb73857128b7cc289c832d9277f8ead2419328b9dc92af2f96b91507ef2dc31908b887585804578507e504d519190d06cf62fbb2726bb80574ecb7f23e5ecd93d371ddfc7f8405aa51ba7e018ab4c045e619ffcd86ae73f1d30aa7189b0551eb7ee58813b84f3f9470c5ca29dcf3fa6b9443a9f4918c829631b3bbf5f2137f51b743e7ef29ee41b8172c13ee0154e5759be412bb8b5f39f18e7878157378887d37c9224ef87e900984d825b78b6c219fbf2e017f0ea0a3191984faa2171bade8baea2d486bd930f4bd7fbb07fa1ee34927cd86314cc52636bbf9e7ca530f4fc6b22793df97aa12bb8a53713fc37ed3139c065c3fe9ac143690cd578b6b62f14cc42dbb0da7ac3ce87a5660a9a41412c223805b756df42035f768bf7cd9413e7bbf3f8f1182c6af6cf1863b06ce99f317ec6702d52c0b2dbbc85bd5a5598d12186d14eea2df62cccd0b05bbcf10a802c4e3936fe9b29e79a5260eb72682c36f937534e5edadb64aad3586c9c6a5581c5c6216663ad2abc6c12c9d5793265821f8798eda60738c46c9c36c96ddae864d149c57c21c46e331356e70b465bd88b6f0a8ae1b46ca2e41669862c836760169e652f7896c12d3c84d39e22fe050bfc72a3c2b98257bbc7887fc1fa826741f98dc03e6bd893249979248c5ccb8063f02cbf06c780654033a0185ed3c2437e85788ebd8257e0979dc93298e824b7f090d661afad157a46f9b2b1111b96b5041159ae44b9214a0c5156882265e39cbd424903a58c1828554069020a1728426c0ca5ca950dd342ca4b0f1bff2a85c38ebd4a55edb0610af0e204ad13b49c7005cc8a6a826ac3d66c20898daf36b64a63c37a69885a4fb0886a38796263ad1deb62c37c70b283931a5c6b583058176ecaec9b1c0e932c382476cc3025b1b4c48d2bd4246e587498d9309b0b435944c610d1bad92b912f448a9060af1c5c60632b7be5c08bbd1a6dedecd5a8776682bdeead8d9581d6be59b379a860afbb37e48a8da118d24503ad1d1b0204ce5e873860080c435a18d2dbd8ccc64f84c94a62c39e5862ea092a534ff4cee86d18666354e31b636be326ba68428ba926c278d9b12698d052b371145b5b52e02085980dfb2204135a4c3131c3c6696cd897351b36c51a1b576d98103b36c5d68e75a961e316f63aa5c49520a89461b3615dec75aa87dec6198c9a17d468c00a5b6c2ea0b5a58ba91db250c0cc864dedc0c2c660ec754a872c5e7b9db29266ca4a6fb6d7a91cb680f63a9503117e815e56d65ea770c8c261ac40b98261e32cd6e02eccec18192c7b85a2a5b7f1ef550a869e0b657cf165af522e7441c65ea55c68a167060b1430f72ac54217522c8051238b8526d0ec558a852e1b97b157a9aa2d1bff2aa5c2d5d05ea5a8d248515949a5a066639dc5a8c4981de361632b6cc8d8b014ac365e61af5229b420a50d1b306cd8a8a60d1ca488b1b1949427366e438c0d939282036daf525262d861c3a47a5b4eec26dea68d1a4348f57cd8784baa978354cf0605a869c363af0a50e30105a811d3c3101b26012414d0c5141b07d165c326d0c3c62b949929b8d82b54995e157b852203c40f3b6cd8175d9029f70ad5c5165d48e150ec15aa0b263c20a241468ebd427151012ea6e0228b8b24c6ec158a8b21ca38b105962da8c8b8428b2b2dac2250e3c65ea1b4ba58a1b4bcfcd82b94961119ec15ea09adde8ef5b081c318c970d9b17c04111bbf4339a001376cfc500ea0c14c1a3b9636f115ca012b38a0c7811ec2d878af503d7cb1f1af503d70b1f1af503bf42250f35a3ad4b0f1af50567a3f7b85ca418d6baf5039648562686d58dac4dbb4696363a82aab8dc5ab2a28641b96ae5154b6117205b3c98d94ddc2c80d4bd72632b535b88b0c6cdce47b2a5810450b528a8416b02a12122185288a622886a17880d70ae5003321113f6c2258d8f8cf18220b22aa8640620806b0110e2126dcf245b8058a8d1f082ac21f98d8f8b3c21f7a30024bffe0c40f5dc22d6b84444441a489189a1ec4362c5d150006065c2e97cbe5ca66bc56292a2afd4313590d275ce66b9562c102449ae4f454d0a607371b96ae522ea8d9f8d5b082c5a81143cb35e2de6b9dbad2c35d9cc96cb061c42780864fa0011bbf1767706166023b8ca1620519534b90e1e28b8d5da2cb25ba4451145d47af758a09361b3f155aa40833844bc1c2a8c16b9d6aa20d97a2ea4b0a0e85968ddf35448ad1e51ac7d135bac628af75ea8933409348139dddc27616921c47721c473283d77a640d0dbc442145348da06c8abc0fa36b748de393d76a6bc2080454741c91e3881e2f6e64c9c0e4308287861d3039606c2338616c5c9afab5320993330159151044b1b50902ce86a52b2fcb88b8c4f75a9f6cd9b82449b2244bf4b542ad404ed9589b20e0d8b0748db215aec1016348851d469b8d1ff66e82004bd39e22f89d881bf6be612e66072dbbc5ab9818d2d8ed8d12e260b48585a5b9dc820a46f8b0f1e7a23430013460300f48b3088e7d8efd79bf0579e7b72012a418fef3beffdc271fb69a4f5ef3c9afd4a608bad74b5370d2f98b66d1491dfb846611908b095e519d6b944d3edee47b98365ccc344b169d1f514c95061e5a837b99682705c1a45a3beba02017ec935f44412f60169e7dc133f0d4e2d45af00c0b9ee5d7a8a69dabbe828587566c45037885875c4c4f8c9d1fe472d315f4b2b3b975834bf800e9454e1fa3456bd3b68fd1c2c30677ac2e7757ec065ae4e3c382edef44e78b61087e3ae39d866813a88d499c86e0a7bde6e011517b06cbdece11b3c42c6c75c2de5fef66f93e62abd9f721d6f7615608e6fb50ebfbb0ccf7e1d6f7e199ef43355f900a2aa82008cecda32900b0d5e7e377529dd44a4cc174fd145bf940c132d9e70343437fd01ea8679f9ee781faa09e7d2088207c514b26faf1401e0700e92aa6e97aa2e92adbae4fd7dfae0fd79cb057497e60c02ab04ca8251349f06204e92a9e10a4eb29d391aeb2ef49d79fad33a11be92a248be5282a12129acdfe65b2f31479d2553c4fd9cfeeb5ca41efc64157f16aa560bb3e86aee0567805440209249849f03f509f1d3b76fcec77540d1949f089e229fb5999668d59526cd0a588220a2836e8506c9df5ad274687ecaf4accca3e9f15ae025e11b5dcab112b2b0db166b3d9ec7f4a91d1ecbf17b1bce6593e7cf860a37b463cd229e3959090d0fb3c8f74ca682494560d19f9f855d4e2e3e3e3e3e3e3e3f36b98c583070f1e3c78f0e0f16b08a6478f1e3d7af4e8d1e3d750ebe7e7e7e7e7e7e7e7d7b00c8d46a3d16834daafe116101010101010d04f293202fa353c13141414f441413fa5c828e8d750cd951b64604194b28dd6d0036bb8c6475a667f0f6aad11d564edefc77415cf606d5d95d1b23fadfd8157f627947af609424120f45b937dbea7a1df0ffa9dc93edff740bfadcf07fdb4d00f4cf6f9d4ec2a6be881fd3d296a99fd2a5e8959ae2b27f2f9d9af2e2c57b3fd03e3016cb3ae66e3194ad5e379bc4f249134013ef446b10308925ce87df9fc2e2802387e42401e1918914f2e0ac40ec031bc6172e0daa14370f1e4e49800c70e0fb18707234c2065148142202610a22349c2528738be5a1e72959a821f12bac251077d1ee8a221f7ebc9e8b723e41295b2054484c8c471c74524658310c820e24cefd03b40207a3c5e9a83afc707816b87ce01f39175a0d0030aba7929d374fe149026600627763a2286d0caba2202082c2d4c418162992ca94a480930c100250c40002424f920001dd48a80cc26900000afa998a2cd1968c6d0200309521c37c22b61b0c8e283084434a8e9d02949f08b972e5bb464c1e2001eaee820801905414039e0a08484366cd24832c019699e38dac51610036e29032163a845062b229704cafca0a566c1104f0e398a618e1e25923041bd21092100478a10a905f1e173137b997a07185858e166ab82268818028829020c80684896a3b5d0426300196030e40ac122a67431a2871a667041010dd861871890849081100acacb465e03afd16a3e0d3ea3c7c014f8b6ae17980ceec2c54539066b81b3c05e70119908cc44af77c78d40d4f1e5306f601eac73736238df0d8edd53bf30f98dd8258a380c3fac6fbe1774f7d187fc043f4b2bee3a1ccf2948bded9470baa71ff75b9639782d4ba261710ffa4936e1eea29f2412ee3e819f640c2749e548bfb47b9f9ffdab37dcb7b81712c5b3f16c4c4a6fc37d1b95b48624e1a68d0adcfde52749c4917e890ea7323d799f190617bc0eb7444d42001010d00198968016ccfc2be2eeade308f55c67ecb7879fae2dae2a3a5ccbaab767526a6d4ddfee74cfaabbdbfbcc5425ded38e759de0baf94da7b6b6675d1cdc74eae9ba71f933b54b746aee9776af2ae172ecf3764b3c5bcbbeba4467e3b980dc55c107e69e61818fcfe079bc27251e5f73c7b9bb939fa20f4f126279404d4f3a048c3fe36c06e5a18c3fa302fe15f0ffffee3803778c813bbe4048a8e66b5fb8f8d7ce5086f004773ffd0c57f7af8d5f6b97d4dcff88d4322dc1f1fedb253587c4da30c020e4e00c77b83f8b6b9f92fe599ccad47e8dbde99e7649bfb4fbf669f7eeb836d6beb5fd4d65a284d3d99c402feeea53cee6e49154dd2e04a4b7bd6ef7483e33271bef89eea956d3ed70ad1312a4bfa9b99c4de9003577c7e12758c57349b6f6a90984e25fbba9491f75b3a9bc9cad7d0281b7e3f1883cd9f1783b23b0047720a0d04fb0480747f4bfe082f7317b960774db3505b54d4b2a6fa7e4722fc1cf0f019e84cbf9001a02f24163d536aae5ed926c1fb8439930c45dc88420ee6ec2190ce0c438bfd3b19f9f764f93dcb105eeb8c81d13b9e321773fe2ae2dd03d6edaf8005710c41d0371c73fdcb10f772ce48e83dc31903ba6b9e31f77dce33a39e97ea9dcdd007e6a997adb29a9b70a8084684515b0ee35b8fbcdcf8c85e501a9b9245bcbdb39fd001202f241bb35e1d8a3a41f2a135bce557700b8bbea67d63154c10f1b132b0404c40790108d5599581bd3910eb7536a7149414b3e84842af0614b4afaf1e346e4636988b6c301b16a6be3290145f15a16b744e505ed96f06c4a3d774792ba6bd1dd13e038277cc158788bcb39b18fd4da9076fcc41cb8fb8d9ff88a7bcee664d7a4f270328072771e3fb1cedd73fcc40670770ca0d49b0ec753591dee59dcec756aee2f7835e96fb9a71d13fb3eeefec3fd3eb9bbce717a6adc3dc8fdd2dc3de7312caeb8631eeeef8e6feedee4e7a5a16d5af271f7eb78e6bc1d8fa7becef63755c77b6a555eab539bd49c5313ee5b9c8dd7fe121dae6d6d4dbcddd2dbfcf6e0ee3b3fdd8abb9b7ede77771d3f2fe9ee373587b32d3dabb65149ff4bbbd7ed70adeda378bb26b589e501f9e78e6beedec44f97b93bcb0352529d1c3db129ed72395b5014cfc64bc2e58294786c50122e9784cb053de996d0a09e74b81c4df764a763a27ba205193a1a2a82a20901111101f9086275381b93526eb76b839c9e5af68917c4536f39dc535290dab4630a72c714b8e309dcb104eeaeddbd74c73b729a60437a56afb73de740910449de34203f995126df6b9ca6443c20bd49fe7222150a522e7eacef6b9df3bd48903756eebd2413f09dc8d55aeb073f74e9f05de717c5bf79cdf7bece194badf7412de504bfd8e6175fcca25428a6d8ec20fccc0474a54c40fd53ebd45ef15f2660dae4bbefff5d91373864fc59fcbcc32749f723f9bf94d7ea079de0d76813d91ed326524d641b4cdbdcc1dd62da446a8769d66993bc99f8fb7e37919c1be32864e7fb8b37369cfa7f879889ccf712c54f6214f035cabd5c96b8c45e82694f91f04126619a250a3f986689caafc187821b892895fe5efefb1897495caf512a28e45f5739d5eb91e8140d8cf28f2855d535ca3f450323fdb94421ff7d13a532bfea8dc81751aa5efe9b52a1907ffc296f44a6bdfc634a55f54665eee5c75fa2289822e4cf8f93e027bd1cd1aa5b758d5c4fa2545334301abfea1a914f62b47c5d962914924a24c18dca14ca1625e4df87323e7ea3ef47d405fe35baa1e16ce26d369832c912853f4b947e9d04ff357b44d08f5f497e23f29a49f4bb9924ff670ca6408a99484a2a0afefc51eeeb97daa224fcf1a5a8aadee85302fef7515c5253dee84ba5a2b8d2aa374aa3802fa6556f943f4ca380f7bf3405581bff28e0d5195d14add2545553c44c848d303a650a4a55b5a589069b497edff7a7e8744b130d767ecf220079b383182a2b01989acdca1063fbdf31dbffc3b2fdc32f6262e0b261a47bcd49cf47a4e74b51f7473db8fb86260826085fba4ef9fbdf5b8271e21084f01dfdc97a8326081704d07b3c98edfb37457bee635da4e7de9fedffb3c507eff70249fe38a23d46240a8395ff4aa1bcbe3cdf8da0906ffe00c63f5328b01fdf4ca16c5102fbf1a1c49e7c377223f296234afeebefbf5e3f6524553e2cedb9698f08e4c35ec9c3c6375329d88f2f157b52c769e2f7dcff123d4599f9672a2575669f3c65044ba5b6ac59603f809b06892b7a4650a2a43dd8c3d2285086fcf9b15428fbe42f3fafe7999eb802f15f0ffb55a886579a24fbdc5439dd289b2983869e91396554a25346a493f8a24eae07257a7be59ba9949453c91bc18d98b24ffe2623b8d1eb477023f3f64a5763bedf2bd7ea4a952b47f0eb4b14e67a8dded4c9c55b3f0cbdd8e42981fd2b95927afd2bbd57d9273f2c1dc18da82e56f6c9ff8265d8bbd1ebd77bb5337e8ddec76f6454a2d7c8cd9be6e4643bccbe8dc64f23893f3826a2127fca1bb9a030a87105fa4574cbf76e2271a781e90ae5b40cd5e60fdf316cbcb4c1858d2d3666dcafe39cb3d6503ba34dc29dff83a98dc99c73ce396bcc06e93bd657cf6bf91f4bf1da04ef8f51f09a04ef8f53f0da8e7700bca7ef8fa7780d7d7fbc82d7fefd7195d722787fcc82d720787fdc82d774bc3f76c16b39de1ffbe317bc86e3fd310c5ebbf1fe3806afc9de79de1fcbe0b59df7c70bf09acefbe319bc96f3fe9806afe1bc3f6600aec16bb1f7c73678ed7c87bdbfde1fe38073c056b00e3be006784d7c7f7cc56b616faf622f7cf15d3f3ef9e59bff7ad89f1ffb9bc7f99cd7f99de779d9df781cdff3395ec743f011fca32900760cc93e1a0c02fa81673cb2ae7c07ceb5d4f2528b48e491aea24fba8ab3743d4f19907495a53e52cf024a677bf5ac34157b409f51a03fbd9c6b167fce1e321e3e3e730a9cf6a4ce82220a29be4c41451560acb80202588411938596165b44600c175d902923012fbe3033010d860ee35ce13c63383d6baf23047c8804efaf378b5df5c2c169fa41f455e506e9277b78e89583cff4eb2c48aaadff8ce1fcbd3e0673855cb186d3f467345c7383f48fbd4c24f6b08ffea22221a1d9ec0a96c9ceb3f72f24740661684e264ecf2281d82b78e5f9ba50d09f5d4eb177a5cbb9ea3069d480756ad929ce664242e92a1494ae455a2b78a5b744f13ccb10f5e0453400ef428ffc8876f0246ae44bb4c89b28917fa13e1e8672f027bac1c750a3bf41f7e3a0433e07d5e075d00c7e07fdf13c28062f432ff81ba890c781d6be07b5e073a045af03c847800efda3153c8a06f914157a00a041bf03bd2f010afe04687e65d1f20310af2c5e0b5fff4b8bd7c4d7fffac16baed7ff0282fcf2cd7f3deccf8fbdfe97ceef3ccfcbfec6e3f89ecff13a1e828fe01ffdf401f03b5e826f72db78cd83b43c23006999c6693a0aaf1d494b33a4f05a076989e68bd78ca465195378ad485a924185d788a46506aaf09a8fd49de6038cd73848cb31acf0da066989812bbc669496178080d7765a8a8185d786a4a505c2784d83b4ac80d3b418af65909614c8c26b3f5277da8fd718a465184ed317a425185b784d485a4e20025eaba5a599315eb3202dbfe0c26b4569e945175e234a4b0990f11a90d49d06a48cd786d2924c0569d985175e0b92965c7ce135a1b41c63c66b416919810978eda6e51660780d4c4b2d74185ecb69a97526716a1f3f803c980da6ebb7c19c41f0aa82206920516a0129b57e945a3ed255a6ff84e17c188e70a30ad2550c92aea76b187ed812faf1417fbe707e0d7b157bfa85d25514faa0743d6f7abe9006152aa7874163cddeca44b907245dc51fe97af2f0f9f567bfceb6b67c6409a5412950baa23b7f91dccb4423bca6a56b1192ff495731c9f748d75384e791aeb20fde275d3f849fa5eb0c84a7205d85f69a7b7b845484d45dbb8f76ede907e404e8ed4980965a3b50f00a00a84e51b20a45c7dea3792b02344380ea2b4ed3af03cd594ed39f03257b4ed3df83be9e709a7e1ce8cb09a7e9bf81be9a709a7e997e1ef4c584d3f4efa0af2e4ed3af83be96709afe1cf485e534fd38e84b09a7e9bf415f49384d7f0c7d21e134fd27fae2e234fd30f47584d3f4bfd097114ed36fa2af225e57e88b08a7e91fd1d7104ed3ef425f5b9ca65f445f42384d7f88be8270da2b0821b60c41c45511461cc105892494d82f2faf265e4ebc9e2073d695dccb5be3de7bbb6730a7eb6d02d2d6338973bdbdadff15c40b11c1160886d041448eab9e22701871e30819171e247692d05122070b67899b2e3126ce9717d8ab89d7cb09f3f54449f6c89c35ea2baedc13f35638f676d596d7aef4975abdfcfa7b65758081be90beb117f9ddb5d65a67d75a17f11be41863ec464e30fe6b997372be9cf9d7d2484e73f620ab70ba1371e77c3f5fa14fcd4d0ce7dd2608777f613ad0cb9afc40494f1dfcb3b857f28f84e433dbb58f54aba94e514a909ef635de6e0949758a6a796a2e67db80b7635595559da2da16a784cb3941a93f5445f373e80043b9022f1cf85901943b90ed752a135bcbd4e29efce89e9e985a56559778f000bad9d496d5e11e09e95556005fbba9ad4d49e539a9b76771af7b5a7a16c7a484070f2077e7e167053982847177223f830499e2ee23f81924883b90cac3b1ea8de5ed96a272bbf6919e06a4c335ddf64725fd4dc52de1945427f65f67036183123e4a67fb253adc8d675ba2f29e7dfaa8259cba44b7cbfd4ea7f43695877b6aa28ee0ee22f8f9038d1f687edc70fe58f223e6a30c1f35f890e2c3e714da72f700f8295483100c4245415beeaec4cf202a18f81984a403400d7077d44fa028ee3e003f8166ee2e003f691170f71e3f6958aabb93e027ede6fc9102023f7f1ee0ee04f0f3270420103628e1e769aabaf44d6a8bdbb53a1c0d88f589ca25e15876e7f4379d6df6aabaf451eaed29a7a4c3b13835a906f403c8ed08d2eb6c3c55a72ef9a5ddefafd180a26c3995f7bcddff3821c007086866e3d992fcffb43a95674b7200a45793704e51ede77238023c1053db42a9ad6ea7f4351ad04d675bda39d99440f81a12d2d38098b038deb73635e9a36c4dad4e657338db92eab4d3d96eaaaa7b16a7f2764dbfb47ba4a31629cae6f43ba7a7ff416a525bdc91aa2e212ded9eb76b955427eacdd6f42cee9ba26c4eafaa455ef7b3497896824dc2bfea64d7f4f37fd3d9580a9ed87837a616f7e47fd7ea784f49513c1b4e09b7a4ead424a4ff9bcada92682d8edd39a9b756876bfa5dfb7fe3d99ef0764b7fd3d9fed9dfb5ff5f7bda0321b5ac7a43fa5dfb6a5294ada9c9c6222101f1763f3cdc92a71d8fa9fd9c4d69976bdaf1969a76ecf3d425a6f6e8a6b31da94e51edd1121d8e7d3a3abaa92dae0997bb1d352941b147ed4da7b2eebe819f3cc600626d4c4a385687e3b5506a6b736271b7e7a94edfea6cb66775385babdb6d90c1fff814fdf0f1f43fb30b94d401dcd49bee6967c42af158281b083af57684a7363db1badd061f8cd03eedd4265cab3ac1b54ab6231697c10078473cf5a6db39b12aafb565e053f4f43f454fb30ba28ed4a4285b53d4cda68ec053714bb87617c2121deea65393b04fadad25218aa73e8900c4e29858565552b27d54126b63526ad5a5d7d9947e840752792ad3df54deae655bd696539f78edef9e2501e9008f43005293520923d46a6cad76d4a4048574805799da253a9c9aa4e6724bb86f7536276a4ee5a94a3a9eeaf43c5bee6989ca7bf5b6dbb1ffc3aaac4de955f669f7b99c8dcdd9969e8db2b5ecb7b8bfa92ccec9cdf62c052d8e3d9264ffeb70adcea6363d8b7b16d7846b3f97c33df9261b6fc98ee7a4db7d544e87e4816eea8db753527937d5094e55527951b9dc4d656a7749b36741d8a0845fa2c335ed947438deee792a53ab3211e081783ba6b6b5b5ac4e75629fd8cfd992763923afb6bb269577539928ed72aff26cac9a4bc22dd13d1579a0289ded9770ffa32eb1b5ecb314f096e09a784f4a27e05a1c6f87e339bded59dbdf54964909c7f4e47f6e6aebd4a4b6adeda653d9b77d6be3a98ff403c813d2eb6cff73535b9b122ee7f4c4c103edda5799e0724f5e7df2404bbb6f5299dabfa94dbcdd920ef7ea936775aa92cafb96dd35d93e97539f3c6bfb1f565da2b33129f19e76bf6b9f7d95676bd5253c5bdbb4e32de16eeaab4e6c4cb9dc7ea0679f7ddae1783627ff64a763f2ea936f6d4cedb31fdc70ac6ea7f42acff6ec8ee9c9b33a95a9fd9fa5230ff42c6e69f7206c50c2efda6f734fb625958307f2c9e56c2dee9770efe35bf669fda85ccef6c43ecdfea63ae95435e9591bd3b338a6f67f69f73c35a9e949a73ee56c0378201ecee6f43e482cd2df5427ffb39bfdd2ee99581bd30f55f0e359dc47a91e3c10eb64e3b5dfaa2c6b6371502aabe296e870bb25ba1d50144f7d6a92c3d99cd8be65d5d6967b9549c9abad9a736ada29e98ee8763822399bd2ee03f6a6b6bc9d93d3133b8200545600ba2337ddd3ae48bb0b8189d2ee03dd129cca539dec486877216c9689d2ee03a327bb0f5adece49cda94ada27244c9454a60d6e6a128ea7c4263d91b00be1839bcd48b7c3b13c7500379b910eb704c74372c3e56c4e7447d85665d9cddb3129e96cea081b44b54e4d44d45c12aee5a94e7447da5d081c80a0e6926c3a01e4704554de530e672b72d3a92408e0a6367921ea0938b6e9a90907656bdab59fb33d35515f4dfa5dfb37f5041cabf29ec53dd61e813b5875eae0c0bd4909ea888dd2d940d8ed942cc119ed764a72aa4e6763da6017422ef754447d529f44605a82539ff05427bb23b57d5252a1549e0f9a9a53591a6fd7da72b99dd2918ef794a42ae96c4f2c8b3bcad9da5d7bd4b24febcd76d4aa2cab643bcae56caa135cce76e4a4aa4c8e8e5425de6e89a9b5a9b7a327bb5cce76c45397589d8dd7b6b81cae7d626bb5a356a7b2499b83a5dd8e9724a72ae96c4a47aa931dcfc9c9e6e3e896db31b12a8fa93d3a82b9fb07dc5dc94f0898705f8263716deea9c9022034f5497d6a425397d858566db2f19e6c2f4dba639f1cee36c749a18ceb76b856c9c6ea6c3c253ad509c92e041fb42625a80928b8c0f11077bf8e35b818dcb8f29b7ac339d9f19cd8a7a69b7adb29e9d48fcab54a2a8fb753fa76a77b5b6bbb7dab53792a930ef74a849c6c2ddb846bd525bc2727364a6572d4aaac6e876359952502426355259d2d671b4177643bd99eb0514bb876a76b754f3ba52395b7533a6a59f5a989cacbd99ea83a9517c5dbb52c8ea97d4ac2f15add4e49b73b72daf16e2deea6f2948e54d6c6a4c47b72529f74aa139577d484b3e594744b702c6b53956c39dd0ed7e25a9ecd89557336f648656d6aeea85559dd93ea84a7b24b704bba9d920f20211a4f65b2e32535b1b627453f68aa926d89a636e15add138fe6c4560084e6d4646b59b509d76e3f6fdcdc5de5ed744b8edc5a257555794abaa79d4e6753794f4ebc27a723b5b5a93c2556c5a947b79b4d280a6ac773a2b56a2e6773ba1d45e59c9c6ceacdc65be2f474a49e80e3ed98da275bab539da84750b82595e794cbe198a84b6cbc5dcb32515299948edaa71d538b7b72a4de764a47adcab2ea129bd391934d653a6a559609cfd6a4f284808680d09ad4968d62696a938db764c44f1e20ee4037957d7a35e9db9d4e6753fa26b53dd2b46b7536a62447475f536f3ba55773493626259bcad33dfd4de5a9ad4efd5acea6e3a9ad0ee76a7a481e3685ae5d63703dbc726f5fec860ebbdfce7d5df18726f24c838620c4a2f8e97caff36020c77e45d01a5c0f857c0e03afdf7bbf2f0826b1df7b33eee0def0e27b337802bef8867867847bc11b3abe2650c620381bff6a8cb5d5d5f7e2f172005f977f57e3acdebbe27bb12800dc832f0635be65e07b417c45b83cf7662c4af1e5f07577eebd175b8075c47c75b8f882f85e9c8f2ed6a32b082e6f06e2fd76640c6e88b1f691478caf0f6ce82a40fb208be424d8b5e3075c9d67fac337fbc7805b81ebd8cff3f4eb575f7cd35c11f3b8177f18e77b81dca02bde12638cc10d2e05186b7c85321079439ce09a3738b00df7c337aebef93a4e827fbcda310ef447ba2f6447a5b83e840a9c2f787578cd9bab602cd6f22dca1763fdb874cdee776f28a47d442074f1088a38bcb77631fe2283d78575767253fc746fa65d1789af185e3c04438021c03ab267bfb00b627c4d7d668d6f608c2f78f5d275edb8e487ef775f443df84848baf04d12cb105f11ae786fc657df0eae0e6c01ce81491ce2efe68b2fbeb5ab039338d4377ff95ea29befecba6e79ef157221c0630eafa82fbeb7762fb88373aec6f7d3d7024cde8c21b83963acb1be16607c71e0f1baaea8f1bdb77675e031cc578bfade4bf457c643bc20f7f86ebeaeab65d7e5aa9107e881e002405f8c411caaf7d411223bb8175f7cefa579e84e6e80a9aba60069fc0bc0c319ad02a062a02180ad02430880025bae04f5c249e0410b95045b163e020f5e741801dd21118187554d125b90211ff000011a1dd8720859c0832cc802db2b08220a983b88d0f10e51e07e6008a9cd0e43df860321b101010d490bfed9f261f7f3423c7a5cf9392302a42f23789e1bbf82830047fa64f0d729b8675fc251e2ae802713581e90ea6453625577133c89bb43f1f3868badb5dd8e4c58eec94ef4f40b3dfd01c8e36fc80129a70fbdef2bc309bc90a279f0a1794a2c41802df4f407ae9d31872c859eab00da001902b2054bb18858048e59886cfc20d013b17a3926487111651d365086d1fbdaf4be124717bdb24d9de8b9642f1c0af9823e1aaa74c81b36a0a0a9f460280ccdb45890288bb541e1eb211685176458be40c4d2cb20f808024750885e26411cfa87f491af2ef5cf58e4eb111600c4018ee05ae694392e996b272c40fe000a208e7c85c207a627c1aa9bfe48021dba56c2c098be999637d0494a1e108704087bc2103e9dd705c9fce932e78c7398c5ec1a73e81c0a20bc812519632118de1cea203a485ff146ee91e28ae00ba2f712424a0cd43d220728e83050488fdedbb4318604ca68a1650598a72524c86045c448030c8876ba33d2045170c2c4f000064012421b3163b6882006a12a360cc1210c325954f18412446409029aa086a7360696c7010da831c3c5982990988a9200130c500292108c148900029e1d2f4c4c1131441428473a72ec7411bcc200a34953af4d1b1d50c38403f0ecc4c8544105144f2891441131c0c0c251020a400024211cc9420c15619aa8d2c1175e6451c5144f38e1031617544841036ac870f184135e8ca0eaa9b7234462a69a0b881186155330d185cb0f32bca0de8e1260c20108708488909a4fcccc428c153ae42003957a4b40008e142122c42f20461662aea082892e5c8af86101305021200047301052230ae2c3e726a6bdcd05c400e30a2ba89882892e45fcb00019607881aa0247083041832020366c7ce001ec35811c56545c288100423dbce04214d416579bdc06fec0c7065e430c4203817fc059ee0fec230b5d2092967be4df8900e7b83d218e7ce3ca34cfdd71e1dcdce453c3cad769e2286f9039c6982b14c11083e0f7e5eb20d52801047aa12b74e9973814bab28e528994105271068e62912e615797e0917e92cd1cd36552c0a107be70e8d23fa68ea2217ae300ca9bfe7949c1417a3e01e80b5e10450f830f3888a580f5a83ff0e9400f0328f0d09272bf5a2c822152b0c691694e17cde0ebc1450f04234842a5033352211b91b242ef868b00654e997342111d27911dd5e85dd0ba8c202de00c5dd9fc986816a84811f50bd4716140a48cd1838b471754e21a1b5e98fc86c4420bb407ea01ae50c0f96663917102d004cd23b4008525783082021031ec9c4254a6d2072d7ce9914f4b39a460fe80dd01e4c842c21b4352c6d00270145f99e88891ac632ca2466f0c8f9824881df4102932ba3a2849b85db0448f47ff0c8d600dc704c79f4ca8140bd60d63354b228668464404100000e31540304020108ac66259900661a0b7f814000d77be5a625096c8b328876110840c3284184208000600830088940c1105002bac06311f38aec58d633e266464ea74d90402e575d21899e916cbc940a901e98135c62a9392539dd60fe947713934a3d9566cd1eea365342af974b583e9d99dc0955e3319084cd4e9191409e480cf72456ee6bf56bdfcf7d0f1030f18cb52a2bdc5c04a97aa38aea42f191e9875e5a4c45067c6f87911ef950d7a6e511be10158bad61cb7061bf22e24d0ce84e513337cc6d4cefb3a06da4bbb84245001623d141de3eeaeeb2fc3da8d60a97a09a5ca901e555944ccb261a8aa77248489d7a154fd0cff5d291c0cc1fdc58b8a96a6259dd26021d93f987b686af73b7dec754922cb4abf6750c1597df7741733010195c7bd2c0813d9c37dfb446b5ec9ad2ff8cd06585bbaad78287eaa274d960c3bd9e6b2dc19b493e4922a69f1bb717787ed76149d45e30e6a9c41880fb5d833eed977a5c717fcfde334433bb3b6366d8aba36a5be5bbef8d3d3127cd07c8d71e480818d3a0986a23e24900429dac0b9e0de6075acf67459c058fc603a807a6d980686a92626b5e989682f2eafb941df499ec496cfc206ad921d7690015a755ad6fee33db77ef36110d68ca81f1050bca92c6c97f214bd7b8f8ba46b0a17de4a00c9865fa8a6483b6f4961732b8d83f5e3365a691ba17403e787901d2bfd4e07048230d7dbcad773c100f5bfe3978175796e13f69c7119e8d7f2c6431fa56c4da0c243687ca6dc4b6615c768d45dd60ffa16068b483541ea12231b2600d339194586d2f8265ceb8bdf5243a24be4e401911681c8eb50c02e418d1fac8afe4358d76be4f59c61a5aef9ce8e1748b5ac7413fc9b8c3b6ace6bd928d8afd4b8ddf29d52557a91e1cf4ab4fff7bb3f2f93a2370baf604f241fec01f782e14cdddf3ac358bf07110a0a1a3264de9b2a3117eada75e2cde803647e216ea2be2362aae06104faa3640816ec5471a3766e7e2033a98828a41f35d5fd424d93abc900326f037be7885489a26b3bf44079fc12fbc75ab9c8be63669c13f1ae6d84c4f31ed332e7af5e60f46b602101b31869a51a44ea09bef77f5003de00784d797263a67c70058b30e057aceb0d6ca7810d73cf3cf281e50dbfc1ec060176d57facc55299e1b1dacff5807ad462210a6e0456fe4c7052c6eac0edb8b1e1ddf32b1496cbd8199f8d95430747c03b974bcc9f4230565140548ad17fcd737db1b6560e8180abac222f359ed296db221d85ddbc9ba9ed054fc3431061c1491e6b8642f1ac8c9964a8c1bd58cd75cca559e2458d4e8c1dbc1bb84bd0410975c84d45e27b065f7ff3704405cfc6d377cbea9c7d08b146079ab922e4833838d311b50303d768236444a75dcd69d1fbfd1ffff16e031057154bfe51dad8dc75873a970ab77e332be81604b1c8ac30a1a3d7acbe274533362d55bedbf1dae1b5e3503d52fd63e5de14c644005e7f425debb85c55ba32eb44355cc61704f4a9d73860010d8edb442a6581c11a5af8eeab96ea19c639223236e3d8b1f5e09913bb98b28e4c863078705a4271a85f334306d11f0ee9bb40240bff022e7bd05b0dbc14daa991bda9940d0b28dc5d200273e16a6ca418cf2612414594213a38caeb1e8a4722e1565f9ac81f61acc42a6190e80c18794f76bc8d0c6e84354c3894a3b7cf3d2b50d3f292d950e9b798b814885d38aa3f58e2f5a68d04efe2a0c5690a964228c1693bd8f7106c1ee95bb3a0707e9ba1088a8ac542e8eae82888be0c19634810c0bafd2f277bef5db7597a3d074217f8f193513ef2c238f1908426cfe12b2ed05662b1bb6270ab7b2b71d2aaa9a487eaf49e2b16e5c8cd30e5d34fe151791f4ba37f7a5d742130f0a3f48e13dd86e1e6a09d819f5622b5bd607a90833827b0dbe78a0e8cae171f80e73b630dd34ac14e91dee5c8c7f6a8bb8ceb50420d39269252990ec3bf30cf7dbd28ac3250d22e0738f09e48afb63f5354f44ea11558719a52b4fa1a42b0d46bf59239fd818f971fedba497a76430e76b947e891bdf456e2c1781e67060539f07652241c4898c2773c3fafef1538c2281f9577e2ea2b9772686926ff233d482a86ea8a7815935b537b3986a4bb15a24df23d087ed3f3744a74e27e5b117d324e70b037bb75b0d431889c910645182a6ce3729dbc03aca027c49907121e204aa922b9e045307e34e69719ae39c2e490ec3b8ae3ad7bd065bb8664769ad955093f9e1344a25a0825681304a4703d61026d32aaeb458c1b8ad7e62642179b75dfbf8c2896199f83d60466f8929fe03af50f2b307fad86fd09aa5eeb06287e1883fe8e90ff9ee9ec059372fd129cc3591462728c907512949cccce0d74f6af3d6e7d372c7952feb1e98ffb77e585ecd4d93206a21baa7d22926ac55cbb76af097ae58761b80f3e497028c0815b412b99e80a4e9de6d7e6ffd2b4bfa11375152d0f0787e29845c434e7f874041cd0b55577d28c7820bb5896cfc2cd2d32b9c41164610e65d2f78c9d19a72326b8974e9dc73cb660708720fb766da1bd9ebd53487d0ec876b6a1da90f77167b75b2510ca1cfc97aef5f6b3ec8f171c69f63e28c60de29e777e72007cf89a5fbf6907e6c32b7fb2c9a5b85c9b8b037c1d95d90ba214ef995f2f97b8fd80f52b78ef07db6511bb6cadbca4f2893c3f2d4b9e5acc6ae90c9938cc42a7a760d3142f9b66e37a4ae26bcdbac1d39f82efed9ae0603c4bb925ce544b1f67e1fe85f705cb18cf163fb3f3c6626fb7118448e2250b48d287efd87e9d70f832b5fe1cc3ff286a6b0111e6a551ad36269566d945d649f020f850c66206cb5c480efefd827170ff0f1f5f8a68edc2260f6c3b008a844c4ae1b176d76fc22012135231df30a36f4a6e372297274f7c15d107679c091dba636bb5f846ea5e496bfd933ddd131b397fc6abcbc57f30687f1ab3beb045fed7abe610d34bcdc5efe26e733579090e919d850a4cb9745b4adf82ed9b1c31dca369ef2d2a113ef4d3b6ea7131b15797536aac8ec9802d9594adc38038611117ed92f5f64e77b056de6e323c00e58440986164b9302339ae60d1a145311ccdff6094b80e44f3522e39fa005da48489da750a30c8fdf51c2d540490addfdef2f99b7b59757ce6da84590b0ae794014265f9e45bbc61b30dd4e1220d7ee643e9c2d5d1619695be8457cd02b2d1907d703a758701fe9d2704a7df7a755600e5d49c5003751f26f337f876eb019602349e3254a0ee74d540c0bbdc90ca5d7984ce456e4be30d749fa18c56702fe84ba0bb67f06155b8b82a3acb23abdbe6417c4bb01766c9ed8d2c617ce2f3b3bb988c0946fd427382473c97b23b0bd46521405964c5c431e9f8a97329428e74134d256cde925870710a1b12fb8078cdd345b8184298fcbd657acbbed0a486049756efad183ca62cfb3ee939a65c1702750d3c5ba9c96990d55ee6afbec6bff0ad36c202d9b33d633b747498797ea3e1f40781cc7d0000eb06458ebaa61570f894b327b63a1961b5f80a88fe018d23dee4cd333b1ce6ebb5c29528011f7fa6ca33c0016138e8eda920fb11b0012a3414a52427da80bc5f13a7b8012c99a15c58d3a3bec67133616dee2618763be6aaf046b0960ac25809a1a66d22bf9889ced540aa8a31f3cea91825fc37448d2fee40f43e1147fe1d59ec8414cbb731d6b7b9f1b9402fdcce9036f6e17c47206d326937d43b820b6f11a39821cc1731374ff51e073d15df8c86d4084e34161eb68ea885f29911fa387b38cf5c953daac76327e32d4ae0f70442350c18ba1baa66d047bb191b785f729b64201366e3940e57b2ec61bdcba91653ad1404a3df66630deee561f789d528a94f7c0907db0e9b7cb4d7afd8fa251765d22535fa3224d0ed8a9b496003f481a2302b0716658382e3687384fb2d1c1eb6e0245906d98b215b3750508a6f5a4cd30e63373614e64bc00dbc24e8183fc40974ea29ccd623062b898e5d58781629136e965bad2e7bebe7819c4599f1cb65d656532d945297db1d862bd83355c9311d4af564da8c67937ef4409c1162ad021c1dd954d7e6d41d02fe58f48f94aa2eb8a0fe1ff2f46577c20692e9aab52099085c6a52f336b2a98085eb68f3f133c58f8c2b122725cd64597ebe9cc956885e7fef42381cb7f2d174d5c6c0b697595d79925926ff59cfc5df66f86e3d06e024eb01505f506bf84d3f33add699affb99d208451499937d77f71e5ae9ccf6c595af487e33178855fe62f02d37b5f83eadc072ad94544cd7c0d529888638674c0c31385cc99ca7e5178516b05c1b7b7c7ea3522ee19c15cf928aa86fb85c313946cbd515008c3ae3723b9f83fd9cf8a0dc0fe390968255b8b2ecc6d6062d8ae96f1d9a01fc689d9a98afb1c045c7598530a3559fbea4a6612ea0c260d02e01cd10aa9b6898bf48514b95f556d20131bd8cf004445eadbd3d076cc458b2c5839e6192e570d73818c6952db27aa0cb8608a6e664e8d84ab440809e7eb0b34e09b519b91ba4ac0b7ded11a41739b0fde06e9e1374b2830c165adf47429855da5785d6c7848930eabc78c5e0648949d018a2640724fc43f8b7101482c2b09514cbcb9561e69865ba80fe12d86cbace504a1d634eb53c903dff68c32ad4aafff9986c30ec36a207b1f4ff5c848ba159f3000af0052876dc612863f1dbb927dfb9be82421fce1a94091d73a280790e7e380f57f46e200a2883ef9afc8b625ad0cbb812de355445891074205968bf3727ceacc7be994f7751a07adee8abf5ae1a8761bd4aa79e89394e6adb66a9a1b23f80f7a53c10d492ad3c28038752dc359e20345d3c9cec3ab8ddf246edf1a8ed507bc0ad0bf5834019aab7751fe783be88e86d8dfccb115909cd21a8848454ba6b481f801cd41257e5ae51035b1aa689090975ff510c9d74e812eaa16807a0a18f54b50bf8c8587fc56b28f5b2c2d5cb1f054074d1c848f68c8851e175291fe74dd2fe5bef856641f87cb2d05941695d730a90e44ee3decd426eea59dc27f39a8c324ec0bed5f904515e5a01bd85bcfc651a43cdb186dcf1e5a3057dc2d533f6e39d2c1d843038c447be29729d8ed1b16b9d45254d2c9bec8176a0530ea8c86edab07a3a274474dc72b1ae2f50113abda289706c8df5d856f8b024ece46954778d238bea82d107bff792ecb7666ee183c57411eb23992f6b637725768c0e5e1095cae454f3a4a87cea8de0d2a846214274b721e26d7e23ea4edff4f281be23b1961d11ecece6dbcabf56be721f7ffbd1b9b2bdc654b0a70fe047eb69a63880e866900b61a160cefab91d3a1d845f7016fae90d339c8473f297fb37af0701e945e9fc09a8fe7c49e44eba3f4519590764a1b3803d0d5aac1397db557306cfd0af4f13d08512053bfd677706a08324f2b0cd426b8d8322dd000feb6ed9a1deee8200be216549e52c5791aa871a460e6921fd429b828069eb0a7896bf3926d68a2df2b97cf3c443d71e9eca3848c943e5c66f08d3066a5f2cb70208f530cd112dfe3f32c86aa523046d6947eac66aadab9d552dca97ebdcac904a5cf11e52bb6eb1c5b91c2e7627bd8cd6d12276ccb3f8d2a4f24ca2d52f6e4a98eebf65136abfe151d8a4c10d8b148845ab7e4624e2d578d7c9419edaa0de912b770f2115cd7d909c0bb5276de9f91367049909348200bb9c389f8ca1b933fa7b6f80229fe1669aa3bf6a384f046236aab04fba9445c1369d45c6c13209fc956773f866bc03abd18cf37464893bde974e31a68678ee6eefcacfaef4f59ad448ad7559c3d516088977beef05efdefbe3ac8ebac39c7808c74681e98d66e631b7394c8ca6ec1549bd65bb88eec713fef18707e306c8b5aee10a6cef56f2a36c8888b70858fb449823f7a1b1c35868045ff96e75cb6726da83a01b7efa259b2f53169ba022967f7e3b9b2c6644b99d2cf14b56b887242f45b21ebf096e38107b5e46aa078f4ac739fafee4abc8b787dfe8b7179272219390af1f3481ea6c77aa8052048fe1b78ccddcfb0770bde985d2c40081eae27a20af18102ea0e11784094f03e1c8e358fcdda076d40b422c733be885288fc3d163e35004023181315a02c701a0135ac91f78f0474b8f394332918635fd4c080c3fe1b765cc8954296125fab57bc0b621ace4723f5826df086b71619d442e4bc8eac5b0e72596844ff855bd80e6e81db873392ce4da621b5153406ed562d659be651ee1d5a5e51f85e244a5d6f261dd223836ba529253b0c9d5d8091affa62c316a803109b689e902b3918a983b86a2465ad70ae977c4c2fa2d1b00cee79cc4dca074a178910089dadffa3b58624d346bbef521d8d7c1171c624e9d0ba0d1c3a6adee7d6f6893cb0f033a24f8c1107c0fd324da34e07750583295e4381cc7379c158fe451fc959983c4f5ca6bc2a70da637aa162c22a1decf94f7c93ed4d4a3897ce6d353bc98dae0e8979d1168db554f25189eef2fec9fc790b2aa03e38a1b8c757e6ff6b388a34531f8874b36961fb228cde6014ec863c42c737b1fbad177a41d2cf3829839e17fd7e0cbfff6834ed171278b5c08280ad57be317b45731733138b012a76c8919ad4ba76d9a690e73de9f561387dc889e253ca14b4a7b5c21431c74ae60d65ac5a81fb4983de030027062cea6b04d2b99e1aa7c3027ca511c9f0c5df36e3d73efbe476036b140f7cfc67953e2ecc312aa3c348717f9d94ce8d813926c1629695f907215e4189c215f1aff2cfa5530df4c9688bc7b50565dda698d1ce8708570a0de8ec0c82ba1b7cc4e2a7c6ea971876448f0243913c8c4cb0f48a71b9217151221f412883eb27d0c3518da3c8d2562f45854f65686741f65c19c4c841c77a1131d025c3cacb20b13f009bb06b42d0ad3ef2fff44fbbdb3029a6ac3c0ae5f3a6a68b13af81704c8b04b7568632135c4bffc3d5978fb70bcf2c85429d30d75cd14079974a3338f84a89d6371c96b4a15f3451512706718783e53d4225cb003fd21aa24f939c6943aca644b8231ce4b18eaa45252bd270c63595d7e3e77938eea18133769d3c68f2cefc7f2df83e80f67afa535a83a9424739f9b6843a4b7d09e884de523dd31c48cd98cb6a65129bce286a00c406aac6778dc016f7ba5aa178e10fd7f41d69085f1d22cc05b310def0f455c171728622f64a0095f98c4f408cf32c39134acd61d96e81c4ccea654768db0081b6cb62841c281b69f06770c2d4caa7ce284524632d40f91f93be4ef944d636aa556c18105148112149bb46ea93f8aa2f5998ac27c2352068cf902db251b5829a2ae4d5c83c505706471dadb8a1d2fbc3bcbb1a8880727899057b692d2f74db2b55bf39b7387525b984d116816c77caeb7245e20fb6d588f1a29610b645374fda00b3a1c13d85caf7f4461198d6666b1b93e117aee382addebdb53020377117e3c4cfded0a263fd3c6fe420a8890c57317d258a3052b9425cded3d3e99eab00e09c10ac0b2d8f00885afa6b6ba84fc4a0b8b31a3459a0ac3ba09063515d43b87aa07e4a46286eda75c598e9dc5f4438b80dfa8eb573c1bfa166d672f95cf55d7c664c265b232bb20d978132c526834de30bdd5990f628db60f816d9ef57fe105baa6873962c2719220f65d3530d8001944955b67bba0bdd506f4decaad6603b0a97108e680ff5647a88fdff07ef396cf1fa22694634c8eb95b8b52eb313027463cfd60631195a92a561a1822b7b1a0a7150f67f566fc78f2598734113c9e540f51ce9b5e30de15077beb1ccb1851c4c28dc466462ce6ccf98cd36aefc70e8460ddb02755af1d062384429ac50bef7064be1008797336e92113a198294b4917641556610aef770da223351c3c560d4adf62fea795172ab31e643908e225042712828e29d8f8005c22c51fbe4896db0fa6b5343a4dbc7750b35a491f41c25a7512a3a68e86c9538b4eed0ca9bb41e7c95b6d92beb961682c47520ec5c4588e468067e60406c53f0bb9ccb244013032bdd4c45ade115cc1fe17672c6e823e04d03076befac32fd1a8a7f1aa6d8e0ed738704a46ac175d19f36a3acbddd61e94d4a6e44db6357ed71ac402843257bce95c662236ed02ac7c2984f1c8f9e9fc5d6b6b99d6c4abcc56730589270e7839161acf55085fb11a59a51442e35959db9311bc8dfd60be56b72828bc6409a66b6af0e01905ba652f039a5f25457242a3769f6c023f05a820ab2e1ad89c95a50d5b91764ae560f4f056810f71b7e8ac3840e6fa1deefb40e809cd37bd7c9645168fe3ca6498ee4412728cf05e964d6e158fc74aadff9f64a3fbb178e805e16defdde2d9e8aa561b3847f4f9c06714ef3613c4740b8a7fdfd79a35cc908f1d88b73094feac7cc55b62694888870cface81010574c2d81bde3c0934bd730c96d769417aa2f1fc3c28c5a377cb0a2b1e8e5d121c0bb1c9c7ae541e32b8f0a3cd63a4845761ba1de26a345377c6654c7a69f140fbd0e82691d0c75b66a32db66585d361e4755e630f38a0e04925f26a0e0940958d391d85e98c7ca9aa9f611f9d3933f25bdaa5010895d5d9a79500c593b00281422c50e09d5334a32c0de115a7f37ff0203a57cd0434150cd0e4b3e05bc66493234a5f82811bcc3ca21959218421c6a6d927cdbbcc6eb63771344e03a9df9037ad20143174f6ae5e007e7b2415ac96026d34df7ff5886534e3d069f94224eabc2c681a02a9e852e0800a0f1d571c165a0c0dc5c8aebbb945e0aa7a69992d92789712eb5da8ff001563fe59662b625e673803f8ea947ea2d4e96bc2e7eb6a05f4312221d6b2d88adbd3fefe33a495d53618808c25f2d609d3460bc73b13494b71f681a5d42b2d6b022d4a82ce935b77335295b5841149950f90a19b9f64505cc71ad31527daf99ad0232407d5906404cdbb11dac61d1e6120b0b252243618bde155a725a087ce293a7f7af12165df75e2a700758ce70aa35163ceb05c0dd8755230d6ff6e232dffcea296207a9a9a6c8fa76eb0ecb3f13ae2c8d0d17798e5fa7eb65d705d7bb5c59deb18bfa978f28b7588dde571a76eeb9893d0c6326fb73c39c39eb589ce348aaec93cbe9389a3bad34db163fcc9cdf4b74025407bef90260670621d131ab72d1bc35aeedcdd3017035eb0388072a42c0d54b290d4316e959871c5c5d1fd5a75f2c5544f8f8f8fa9ac63d63f7836f32396efd4a568c64ff7e337daca2de1782165f9ac63206d9ceaef9a05675577f2d6316eac61c0ef17670798e4d3181bd8684b5523e0f333c0fabcefc251d7324964f89d81fa8cc4e8114870778866c45a9f0ef65224d158d8858741661d83cd4c01b26085816e0a5d741d2b9b94fc8e669001778cb917ec246218a1177fa59814b21f3dba3ec7785f1d83401fe9db3258fe123c5644489ac33e0832bfa06a8893d4ffc76833583b216b40247ef896dd3c70312185a072dd7dd980ce573d1309b2450936468d016c39d690984ad4b20578688402f6b03bbcf2eec6e1136ea95bae1fff09b0df890d37c3a4051d2821d299885fc07771437c0c2e4ba6486a855a878ce0fb1c3190ecbc6264be13db2ff2ed15b8ec415825be4733f916564a9e72f3ba6a34acd8d8e6b7102a64d8f08cc28261af662c8453a04a99e7075ba0dc45bc52cb28c9de4e96df0cff2946dc320aff01c87dc2c4829f31fbeea2ba011ff59eb177f3c6cb239f41c013a5b3a88af442670fae59a2348943710ba142e34ec9a4340115099a7f2596cb87ec4661ba158d280689c8183532d1047d15399636cd1681b0631a104c4084f98e2755fec128056c476da53c1d9108321b6b80739f879a43551216fcec171a2c29325305649e18c1ad31d6b7c9579879505a0f9dc7fea0a772ea31cc5ee76489d450247558099107f3c4878d70263327a4ef4505a4bd298608fbcedb536be4c8da20bedc91ae0b6927b79b5188377b50c4d59b8b23119e71a7c5eb38b9c27243113e39e04720b4adaa2cdf51d645288cb013d0661e2fee236cad1725950cdd5f91105e6ecbe623f4183813474b0ab349762d5214437b80a41258d102925643a4486020a6e0da06a94463b4a549e343d8f7657404762830e74315d9b357d73ac56debc536003c5c70e455936b4175cb58ab40a48d09ffdb924120eac301cadd2a61b6fd202280a0e4379dfa94cfcb412a2e0934b34e2657ef268b7baa0a0e31e4863d464b4030a7e84c411fb8ea666edd87e570be5040fd2c80b0d039a5159766ff13bdbd431cb2c3d2865835b5b68c8ef410dadf97f002bb45ce608aa737fbe288108e357af5452c04fca5a72035ebebd750e338454cf5e0600da7b4566cba2b6241ace172f172de44cd21ad15809a26e4ffabbd11c266c4314e4b5785933c336ab81775c7e7e69d363729108a96988878297e11a9681aa9e3804762f81c832d2d47e6065f64a2d6ad4781d35df9bba797412904147f27b6bc2d26228d3d8c0471f84ed6eb4c469d12e928a64476be46e60b027bac3cdf94dc410b0854a92ea1645c5c322c003469be87967eb1f6790b20b1f56b06d5c6d059da9c623b6ede2ab0617d1c6c2182943f2f0c1f2e4e3e1fd1388d566c5028c4bac0c7ac96981dbb0654347b671a71c0b964d78ef99fff580d149eae758b8465f83bd1b1b57d1f9a664fdc1b57e3820cb556704f47d16a05e01117f14c7c7c95d4de0e7c4c1846f8553a2668687427751130237a73341862b8a4cc2c4318cb617b8f1632620b9927ccbda3dfcb263bd7532a0add78cf293b0b4c31a651f048b2ec5113b90cda4d896005d0b573063497a7141d922c8b195c3b2c7764fd65a066912de629bca532b3a6b7666b05cb33f82dcc06f8ed6217b58d43d49dba08524e4d871315ac8cd19e99f21179a3c9374ad1bf7a82a19dbcaa95e8ec830265baae6e7e2ecfff539b06b25b1941d79c6f4a71a9c053aad471cd52c816d85b5b078c762476ee20ac54c60b63128b91b519442e7d6af2cbabc8c88e5a6b9fb831f83eb46e23cbab824c0ee97a6233dc80ec25774e08d2973afbb4559ce7bb5bf019a84426f3f6bc2770926937764093cce7c7c08d9e60983103849aea5df564a4d80a61ccc7499f3d2acc2c1259679047e3f8f4313835cfb735003c0da33adec1180aaa675d56e91e05f6ac71eea2057aa87cf5a808278ff121ee708aae6e10af2483c0688f62566cb1fca2928fe65968e719a969e052a9fa5f97d9a9dc2171e26f6726d8c9677c9157fcc5acdce1b176e3ed7dc31f0871fd4fc963c2b0dcb99f88e556fa2691348368ffe79387cbc754c1e8c20df5b44dc3687c890a81a90003a4757050651ba9240ee0890192b8477cf346194e657d7e96148ab30da76923266cc397f3cca22052fcb712f3c05c5faa8904e1d527cf568c7af6bf250964f0234221a9ee48f4eb742181c27121605033c9d1ec1421cd169613c6c2ebcfb384b750360fe7b6ff16ccb213225de9dac0a338c241a3360ae4eebfda45ce366f7f311c8b8417ddc57968b7c1ab25e54c3919756428d30816eadced4b55ca2b692e47193cb4b2707aa4999d95b14258fc8b8f13752ca10e5afa33c0caf02b7509ec5ea413ce34bf3c3c1a457c287421b43913538a57185c289c10c0e0a1e88a18083d9f3ef8682b6a1a6269ed2573a4b60c6e81f34a2175b37013d485c9fc559125a48c1461a5818dfdb15917ecd4cd285902f0171cee89399ea74aba28a8f60bef19b722793d1554d2750d3b755183be24d86f94645a3ca39e1aa08272a2f87e61bf4ff4cb631fa39968c66622d00abe11b39f07e5a4a26d9d0b02f779af9175e1bdce5e4ca3caee6897560bce9eed24b438a4b1e48166f783342dccb793551e5c72f248822663dddeec751bf6ec37018ac7630de31a193c7680250e28d912b87698e2b539c40f66ada6e4e76b205a21676bf297167324f3f9f847e557d5add7872b586c992ca50510787074476756a3499184450ad3f25c5ae2c18864720edf09446b70ec48e50ea9e51371420c80250100b806945bace3a25418542ff764f60c50acdd9b92eb3b23eba14b04138750da3b21cec9ec856593fc87f76d9dfb26ea8c43dc439cd613c607f1986aaeafcdaf2ebbac8a5337ac27d85bce0c713507034472cbb3715bf4afb09720fcc5edc96a491b6d6aa67dbb8e3bc7f9b8d75840a0c88b4aeeb6c8c1d946cdddfa6cbb53f32851d8c3a6841a590d4566de678ccc5510358c6c6289d6aa621dedb20e39c592356ce9d0ae0e00b6238885984f56002f6180e036cf67e399e68472dcb0370b61d9e1d119a2c12c9fe28e5398d55c923bbd6b90bda16efddd5f74b383fe5a41de84940a5ea33aab603f4ada77d211a6caee505afe59a39640f4981ad97e4fdc209a3c753594c14881c7c099c62ee3ebf0c27d716703ba6937b3c45afd9d95c42b0fa9faa27a7106a86c24d339cb9f23fc399bec7ca5e8002101db5b879e4f4997dcd3ab1a930a9f91411f08028e991e21a310a9405a972e8385d6c5744c891e6b30b80fbf0da81d7931d1900dfc3bbb3c59e714b8f615675c60718cbe2635f94aa601021035f67facdb2edabb6dec289e949d75a3ac77f6a6486e68f886c0cafd0a8d3b218f5ff985af4865b1ceaabb2ad7a93666d8c9030112ac80a82160b31d039849defbe00e00a0151a9986eae59ea2690ca95e8dcaceb5035ff8ee8cffbcee826a51d1ae169c73669a8b2443bb1e8bf9f8536ad98fde0ecd6c6a8bec989add078cd87bc42c832887a4b5cc47420ddcb27854ee318e63ec1f94496d2f4e371d17ed4e7301b9e7b8a86121d7b5a07e1ccdaa524e8a29075a66dcea1f329c7e49ed1e490bf4f4c229555d6dd3834499d1f6c242186f2b50b5bea640b5a646db33195d96e561aca512dff7f6e337d0dba016abe47197371a4492feb9dac0a1768c2433b8f379ee234144c9f7372f7a66f01623f6cf3131d30b2caea803cfe738aada0dae9012c362aa38a7bf160d0263abad0c01e59442259da29f9703a3619b8f16bb20f32ec2f001184c40abc6bfa311d758ce78bef664061c1cf2e9ef61010978725cef9aad4efe0c04ee9162b462b53ccb1d615897a1dc94e0fb6d2665c25ba4d9703094d0a943b879f984991d6540fd7912a95239a260fb44f0ee87c174a0b990c3f7057e7326ffe8d198400a9517676814bd82683da582f17716e1a69bc03aa9823103685935965680df9ab5e34dcdd143f628dbc77c434f33489dd086b62d965a0e0466eb72c42350c40406341f23284619cd4a2ed2ffc35f0b45a5c1b873f105096c6380f548eb20f31fec5d4483ab8924ca74ec12d505b11b0fffa05fa45c68e2326e796ecb171a7d128634e423bb2cc30c7062fb1ae19fe80caccff7d3efc9429bfc1ff529fc3e109bd4a9ff2521ef0b6c2187370a8fa4c1467bd1945d3b6ba99361f4542313952d0215b956d3d3e2a5049e74b5658cbb6d803f844043ead9b64d88f781580986a39442d04677725050d2a2d0815ed2d02e218128402f131d955bdc4540153cc323c422418c5c5a3da1bbab90510ea13dd31c31c20bdef1d8ce025b8a3983338d383d80d21b8ac936279948319bcb2e40a408dc87650a33028af4d48c19c879352224a2357d03287c5014520334428d6a710161165eaa0bda62a7a0d5dc4e2cb85849f43d5da8b0bdce38008b7643833da42483217da900ef5f725c4ec36974b8b17248429d07f839aca8f07e13f4457117aec65f6f98f4f86c02110d21a0cd011c4f5e6d0ef2789ac179837c0a9bc0a6b3040cdde9c96041ce6621005ca1d044295e920eebea95f763a125b0a8b739cc8c486f8aa5f37d08276afb70b6f4f8e9c1e8e6aff0eb7462583cb73dc7e12b81384eb64ad1167e530679d80c5ae4cc3c000ddb72b06e8f8bf7d444351f424a1e2c8e55bb7581f1531ee8490e00ecc2fc8c21d73aec5cc2d8dc36d7541d77afb1331a011bb067e05c2dc6f4c5911b5096d78e89824d5e900ded089d7bab98836d17ae9868c97ec26bd1a1e0168446f0707ec3a971f295a4785b9813380b26031578b0c4526230a528cc2a00ab88c12ae2308bb86157d804f0f2343ddc568d86db619fe995c98f7e2b56d566d06471db314b322ffc2c1250b674275029bdf3d6a0559bc44d9f7b520bf4265e60f926ff5ee443465e9bfb7c9adb85280878058866f0db2211601421c7dc618f8ac850c03186b3053132ffcbf4b3a4c56af55a749841ad2781ebe923b10047303ebb6f7f6624a5bc7b2decec5a694a2d506b363e64be7f5f37a12175031128d5af3c50d19b4fc8a5419b396f7652757837c40732f49a0cdd4240beecf35988c930b81e2fa6579a9fbb4de7ebeec5975927a9599fe1e4272f0d830adab971fa0dcec6acd6609ac62be4f95b273af52e8b0e527b7e7e43940c84ed62be4326b1586eee2cedb9ad0b76c8377e8191500931d32b03825c77eda9af8b4e00fcb686095a609ce6822573b8571aea2fc8fa5e8757687acc399cd17dbdeeb77eaf3b579af1fce55a40b1ac680278d0a3f4bdd2ca21ad57482d971d7f13a07a091a937f5e64eacee9e17767dd49c9eb71d1784ee0abd4d88fd6409e5cbe14af4fb7a74de0f7029021e15f569464bf7fab9880741a87cb70be5971a46c6f41ed13032f8962736a76ce6035e6dcf5545a42c39dadcf7024f090faabc44b1895b3f4087986798ef23ee0eb4de089d2cf7dffd5247474130b597645c2363eb6a102177bcad65d6222f81084c8b9745f33122f388717694d0aa6c976d1eb296b72837b64315b192f0e2b503ec738302e6e945a935d67eb5cd82b9dacdc736410a15c058901566b4366e2b1cc5e6df51da5b210c3028259375127716163ccc2b6baeca0643216932fd473c1371d1518ec34beb0b374a7f82d7e00339e2abe7000f7aa651d8d9e3304b87c82bce2d64730b242ae0efb816a61a7cf7de0fd38d51e2714de543f034161ebb353e9c1b2d64d3378688ceb49c9475be861ce3c0413a5f77a88b62bf6634c9aaf0fb6ab3a850d3050741ac07cf369d05660dbba4fa30b4dda321d0897bd1df0a252e0cc6b71a1c5e68e880d411e0ccbd5c9dab6873618471d0caf7dc94811a41830f404f1a0cd70221de8350881402e769087a7cc262ae82144a021cb11dfc4cf97e4b8d04107ee50934165cc871aa0533115ef301e289ae5829e30b658bacdf8444fe3ce905cb7b11a895f2062145177b059a15d344ec7e226f7bf442d3fb8e00df091477d8193023cb618ed1b926d3a2a3b5ec361528cd4b890a1ce85c8793ce460fb6227574587a2bd764fcd0be7ba0670a8428b5b2c6a7c54402393cdc97a54a7a41f7445f76e4b6303783dc5e6e4a96eef88898a8e3a6b8187033c9b56dd764444c8bda58a6c05ebe6655de634d540ea1e6f3562b2be8037c67c110c2fae1254989681a8056f1dea894d3e5d67205801864ae84f93adf79cbd663c036f3653bb269571a192fb9edbaf0209b63b98b7a63a00749de02204baf65ff84d59eb846b89d9e5ac4ddec7fdffe7505b3191cdd5b78169c416de7f4fa8ad836e27a6515bbf25376bfc2ba5ba1f92676d2420df0936b80401e98b2ee3bc202fa29f1876427b49d6e8c93c8bc68431ad6262712a6a3c7108e4f754711859103a8100071670090f69f3a3d05a216964db1f772dcba9f879f618a19e1e01526b7cf3868a8f91eb666b2bc4f729bebd3b1511b734f5858698653e05c80ecd9e11f23b2ff434d8b5c7c7315a26bf4a8c112a3aa2ad57178493143da74cd95af2f1150661ca330ed0e8166390760ccb415df9290f5623f74f76d90a84da1623527e723813f9c3195174436d4b7ccacf8bab74128d328a877af480a1c171a67971f5858097f983da71acacba030c6f8396a6d8a52b63df139f26e014d109563b9362077251db49ee549185fb43cd90a48f60e5cfa166bc08c07f07073fb375e492a578b8aabec79a282a38b1a791bc7a26a1c6fd04d0d525294250eeabc112f98d7c0579c832fc8a6a9a0397d239bc45e68a29cf80a3face8f6b4baed22507ed6aad68322ebe3c163ee2ae32ace84909b6c8c94319e92af574bc3be9d84371c543bd5e306429c0a1cef03d91daba8007204ab7032339672d10c8fd412609ea86f419cf1386b752933bcd1ba0745f497bc91922ca56fc6bce88b4af41ea54f40ba1983c4bddc8c6457f2a16e1c43bea86f36c5f9a371bc585d786dee488a5f986fba236ce066dd6980a3a691574f43b51d00476028a87724aed051c710f3a5bb7c8d8fe8dff51c7e225a924f324a12b18d4aae5d5c9974cc47e6fadc184b439a0cf754ad2d01427c04b4d66ebcda636b6d2602c16d5ae266839621efe0ac0d0e377d109b4f75f88e5428ec46d58739d04dc1cec1b82dcf8f18df90473ae93fa67563e7ce9f71d100ae7a8168fd44a4cb9212493e9af9354772cf828a175fdd143fc358d3fe6a6af0b8eb42f91c06654cfa21ac2b896ed1fb03a5efcd1d9115722ebf4590df3497ae85fe066a01e202a91ddce018c639ddc77798b2e7e0cab6303060c705b8e824033af98e70064929a2b39a7d554caea3ab8d9990dc345810f24c1c2a9353e8de10f3f5465f60c7e98e8b77cb88f8eb32f8c782c757e4d0eab89d09f9971a72d383954327c83bb3e09b6db670c31e3c7bc063368ed9e75dd957f3c8d0dd12fdc30c1193ecebe95758af43ab1a17e149dc48fd669b001f4d63a95754878ebb54ea867a1c26f607249155720e50b68d8afb432b020dcde4bc00a39f441feabeed21552d31d80977431a89977f62eaf07d68052cef71adb0153d0ee4e3c7b066b14f51d7f9ebb932e6791be36307bd99a6aee847e8a678b8eee6443b003437594f0403cdf995ccc6f07561b957a7f9d5fd9e5a17a405edd69cf43b4d8c067a68d94c9ef4811c8ee0c42a49c3c42758509fb37c278da8a30376224ac3b19e028eac9401615977a101630a57af3c3ab3b39152b3e1f4266806ee85454a0b5d93e8efd3a893937281d47d257e5b9f41bfbcad5ee72645ac48b8f3c45afdd38771c3a075a2f7f820f774cc322ef3bffb6f6b8132c93a4701bc0514822e644cfac0880121d4be8cf9def88400c7fa9a2eeb149e0c43a14496a417af03749aa04c68ac2fc621a5f51249b0b4aa2a4da164b4aaaf013b74a85354aebe1f1c41b38cc55b6733bd0f5d6a4f1068e8aed21b47a8ec14c8de92ff07170c89efcef0dae479ccc58534f85050d542b671237327722a615aeb021f74ca879444f5c1115d144a59ae5940b2720fa78adcb7c03cc36800b632b8c34568d20bb267e80ac2644e2e895cabe309bf38d14d885aefb9a642398bec6e14310f76d5842375405853509ddb9e141ea76be3f41e9935a397746714941b42a5594cf340251ec18d6d450f7634114578ab64b81eec2755d915c57976e85eb0f9b5d81b66a7b13672ea4dc0a7ffab7c2273fb3181579f19c8fc97b86e00a2771e2388962f6faf489693ca308707571c599395653851c8bb149b53eaec1d3816f85a3bef2eaea1ad3e64a29d19d4da4eff7ebe4959c08390d72070abf39f64424baf3c18c2cab857ddc97d5976f2723d6cc47e76c1a4851b95b08b39fa761ca8eaa500477da423d83d32ec0f1139f8f02503e91e2c28cf5a402aeb24dab7f81d9bb14e6d4fbaf2932b1708c5a74a9cca096e6ea8382ae148006ca6dca2e1bcd1a4392402263b9ec368deac5a800e3ce4188560ce8c319316c5c620b8af92a48d309e76b37c586da4d512397cbcad6efcb4f60784d3b892e04ae6b0df341322b08240546a5666fb945bfe27e715a1262161503fa58e2c90d1065595402178095aa8f633352dfab604827d962792e4ab93f71dfc58dc138790d1840d7a237b7c69aabe9477043c6d6b78e9c00b6e96eaf248e75de27a0b740925f5c751fc4ca157a07522f02a1610bdd1abacb0969f5dfe6b852403e88b73752bd5255ef65bd560f3d523d9f6f3dd311c47a71a1e249677b1f60b0b390af6609c5c3e38b87e812b5050528c568b391c8ac0b35cef574a1eb2c3f516958344da74e3273b2c5565ce42cf3cfd4a2fa21ff8b6fd3053b93ab8103c37e10959fc5ecb9a226cedfcfffeff2a672f011085093a6584406f137d2a56a7afb82f58ebb9a80326009ce4fc7cd290c1dce9c65d1472482561141e3a6edb79729852f86fba50fb098016852d662c832941ffb426bcf01c95714726d94f828d216c5dfca135e085e80f6db71f30f693e4bf12d2d478c76bfdc91c9495c10aadd0ea7b97349030da502e8ee0d32696c2078536c2e5a5adecfafeece7249dac9d5e93c6822f9b98d99ed223e157693d810350e531e3587298cd20194a2ce11e5a36afe9c88b787abba1e30e7d9904ac6f083ef91ff881d5129c2aa0f1717ccd6f17b7f1b0e56c10cb28bdc7514ee9cd268cfc9ff2293ad799f73196e2fa7d315a5f065a005df07dc503c9369a7ab25a13d8cce431ffdf633751ee96cde57ba2ddc7d6955f01311d46e39c971c1d2d349485ff90dbf691094506eab0b7e7251d0063994a8726f14dd013d6856b8cc5a3df1f27dfeef56e04eb31a9ff0021a0f19ed808cffac7c46638cb2e07238fc329b83a0835ddda3d76421d8911ff055082a52669dadf00ff4c140e8f1cc31f5dd567a61f4c6bfbcd6248387e3bcdc8eb8f63238be3bed2673bc2605ef9074bc9f4e608cb7a30c666bd70a32e5b595e6176dd5a1d5e42bb2a58169b5bd537d600f923e3406928f7074081a5870a45285a3fc678536db9f6809580fea48f71e7552fce6f3a1299efbe91e5d3347670c1f36a88c50d5a7aa62a918cbf3b5c8103e3d0e737f83cc8d85ce7c1a36ae7713bed62dbfe852c20d74a620ae09248f189ad5ea6487e15d842992babe5c8196a88448cd7250ef2450ba1341b566580e44a50315a75427de60578adcce6a73eb15d8da1bd0122bd9f39fe4cd4f0ab2efac94a38118c12ec758d6f3e4ed998c63b32cabc346185a64925b44d63b446632b977c19d6a8ab561fbc122cd5ce309d6f4aeabff132d69474312ff4f502de0f034e5034e3e156553fa9531399cc74f6c1c2467fc20c1c4a7a9f8725a9681d3f4dfc851b0194b45d5561542c168091ea0643167fdb88a0f391bbd9654ff27171750fb914caa7fbd020ba8be7df640a691f8470884cf41939012d43b8f2371c7129fdd65de40fd554164f646b564cdac9318ab8603fa6ea73c45377fe2555e1fdb0502410d87ce511113194c159dd9329a82424d4a41ac3fc01efb5d2d8013c4ae706251e60f4bdf57c4035cd61b8f3c0509c4250ea7f47007beb8efa2e8a8a2b57e83fcb59a880042b538df4d42c58005d263a14c80ea5e1a1e0f2181ec10710f3853f9d7c998731ede05188bdc9d23979c86905204230078547930fa01d2511ecffd16e8098c33a4297247dc0bc21d18b32ad6fef2ea042a98dd33eb6cdc9950901eb08f048a9734cbbbdee6eb8db22514de24195f5ade531cb69b5a0e8d003386910d06e07bb280353a392f185345b32b359fafcadfc5847a9a9ca4827137812f44f7cdc41cc7743578c22917dbfa11217df0a8986fcd44d9a1962630dcee10e2fa1012fd0e80715869e91cb35be814be2a8452f60e9908334b11cf096e9ba3373d9944f9063091f64b39a72a418998ab4318d823c9390bfa2f6f7c89eedd710885c2537eb7a76ee37a76435c9ba21447aa5aa56cbd030104f49f952e11242ad8a1fdbc4093dc0e418d26bc520433c6fd33da3888c9479763930efe028707ad342e66db5c4e06cccf4db8d232a65edd6cd66a658b024de4a90f5114e72a5868462c6169b2339854e7018c5b746d660cc03eb8836edda2d2e033c9d3bcfd0e63697fbbd6f1d10d66dab557abf4bae92a4994ddc25569aead3b850bdbf3cf0f40f1330fb95d9c6fe9a5122748583137a0ad96e2bd07f1e5d063f6722f1f8dce184e49e96f0b636aaa1141d0245c3c46bcb008082e2e596085f9633253ecbcc58219ba5c48062ae9a17bae4ca7fefd71efa07e0d6d97ebbc55701a10afee21739bf45f2d8cbe8dc8cf4ad0bf253442ae7b6bacd2086fcdebb5bbf8c0aeb46f5d4a6dbdc06d6dd17f47b9e0bcc2e148d095815482c74b3f45802d7310f14a46412c4296b993fff54c8f1ec7437b25860415ce654deff8383709e253daf61481eb16f984ad04bbc35784dde19521e686db8efcb17c2c31604f014aba30ff1547c0a28cb1d9a9cbfd17b36ad2ca071bc4aeb6afd831708bb0223159441759354c42224bdbf4102eb7991ed3bc1c0102c00a1a0ace92240557cf82f320418b65cfbe47307fe338bd8a2dbafb149ab93ba95732f577565bacf15ccbb17a482cbadf9b5b8c9edcde71c232f531f00d6e14b13fa6739cbe52433969c790d65e48950f783240a61b7a6bdfe25a89093e889f99d00d86eeff554fa046a220a2642033841d573bf4e4a8542fa8c2f8cb9d4845a0fc1767beafd19f7eef684996d82a683a37dc3f9dc4768e1f606cb34e7e8ce46f8c100d8e8c0c11900064de651e3290f054326d4af5c1fd04fc2c1ae2330b135c91a2abaad95ad8671a38447880220a4892608195970b060897d8a00321ea2df894f20da0ab587623269239b43db32104569836af0024b5068453d18b866fb33fa862263331ce472d190022d4cd55af0b957d4e84d1d74c503541fb4e680dc3666d7eadb7c5ce3405c9c97716b15a509f4766ff1a2681955c0f95076e8915c138050ddaecccf3ae39e70ec9349580470369ea2abb0834861f67edcfadb68d40e05645c9815eb483c349094cbb2dec663ada820f0a204a504b036de6dd66a8fe325084d87759d185c2fc355a4c6f7d4e148093a11668edb01b34733faac3be3f8938928076e9daa2e996039b0679e989018afa0a73f14c9635e4c57d6de821e73f5d7d9f39b1077badec7f2e13e5eccc8fbf8d933467aabe87aa0515e1b049a7935a10b9dd44ba82ac7f4bf8b02fea7f8eeb0f3ae928c3a98bd2d17b3a9acb7f129894d643e177a6b9b0811b90904b02e118fedc775a49bd433639705ded929ef3c6f062943d3dcbbd8085df5ca977ff76e3a85d770712b9ceb151d973c7f64a66f597a396f778a85e92b39152b9fd4388dda10c9837649c59243b90b367569e16c669027983cc8663ae3cd1f4aec3ef96a1b0071d740ac49b5080656e7f7025fc29ffc04c989d722969dcf17571173c7f2bf5f61b763c1e422277bcae3a7752f6a202297c9550f0c1a610aefa1ecb79b687fd13c4fa1139e7133ef2277c45a71b07e1a9870de21fc41e0c8bcad4fbc3037c0450b81bbc46d5164f1a945ccaeefe8a3f2aa7baa6984628a5e92839f471db753e5bcb2d9db33fa08a4bd08768c8af2073c1baf44356f8a5888dd5bf37b9ed60c1a433acf3d05f353fd19b1ccb5ba6e2c4357664ad298b426e257dc0891bb9233dc410f7808b607716a1d67b44c3d985dc72f881409d8bd40f8bb2c2a8dc431f7a14b42bb78bf507474c24a8af079012217f3b10877431b2d6650504695d8f379185d415e7441d1e8ef8f3a988040aaa7dfc918e893e7b8e7e26a0c375257a1f33c637b790ffc97b42cfc97bff8b150660ea03963cf96bc52959618882070f62b3ce78ea2102fc666905c330327a74220587faf070e30ca6db2000676be276a591772cfcff04ed0303d93c11b902aed254a4ccfd4aa9de2f804b53707b458593c7f0fae2951c1210f7a1406fcb2b575cc7df9168c725ee51e225092df283808c94916aaf8608fc567f7629c073bf952da13a55e5b3ea3fa4bbda4db250e570c5dca1fd565df52f8845a3d9c09ee9af92831046c06efe4cbea47848c20b662b4837ba14eb4c622bce19ecc4b468762972bd0dc11db017a2d0c5b0916d0e67845dc845573832bd770c20585ffdc8c0e232ea21305ff5d085a8a3e075e326b078c86905dd6ba7c7711ece99de3cd31159c40108f88cc1f954433871db39cbef0c4438ac198251a6628c07b9847a97130c8522fb33f685bf3cab31951206c7a7fafc34c3b9b8a4b9922aa012138d1d2ba67b1572b8a45ed0bf523211b2e6a16723ac6785f66a5cd1891278560d2df4ac379ebaa50c54cf088f38f7ec8438be47049280a27d70d627e8b1652a6c4baa9b98bc17c6253758e0c270e617f30862075d0de2f80e7a5671a2911268c47a6ecf0b45e852029392e9486d7b5c3b67ee59d46f32769ca6d3573b15082d0c8b00a654d3f17b00ede2f09c5c2cf8954b5a33810d178f06a5297a80d4b9b1c08ea2474c049fc54a65330231c38490a64a45a272305181faed90bc34c996969b26284d7d988757c7c84df9818fc9b7dbf7899f7b17700a666b872cdc5742242998042fbfc41192720a712046226dd9c9d1f8faeff6888a318a739476be103083bcdcaa834209275c91cfab8e2e9be923decb9f855f6e4b7c737357b3e0708495d5878232baf49ff0b152b6221a0c692c0ba4cc83d0134a1eeab13d45ef1f4b47a844bad34358270eada079c8cac86f5907209a813a563976a966624c1793599607d59a5df1c2e5564733b8fa9cbb6d85ed64d40cb5be96267a13b3cf89c07937a36d5581ed383153fd4279152bceafdfb622bf9870eb2293ae1cf2b78c0f6370925588bcb9eceedf723f4def20338440507f6021d6660e76929b92a3a41ab35d77fb1d71e7c02a4ad0742cdba6e97221a549f45e2ae0137095b6aac3e7c682965ab43823eab46be25b966c80f7eac5f19f1d798aab552915d0ee3279811349c4291c20d812b2a9ade4de0cda81be4cae3a14a06e1ae42e026599c890c69533f25fd1b9ea3ace255527a8520e530833c1b3c3626f546a12960e725580f6c218337b05682aa3e5c10a91eb1826559cae0a2cf034833ba65fe15902574d605284086d33b0b228ad667718eb9f18f7ae419d756034cb271fdf532ba7864974c5c67e895d9cc20efe65244050e75dc8ca56f2ca6c23a4ea5d297e1e5240d25bc8a78c0b0a5fbea97f6a52c157dc2dfcb3475bc895f7176f09724813cfa9025b96ccc26b8708b848df956862f82d5c1201d005afe14a86fc7ec2d433183abe78c0661d6f09ce610315e6185e3e93fe325d2a3456a4e033c227304ba53875c5aba4e80c33749d7c9084fd57b8722a7962e8e1398027621bf78da48b9aeed2fa909351514a69ba27e54058a772a13d11564a75c155a7dafbe59c5c744e018842ecc14c915f2ccca37bfcc44d0c860e222af4d02baf25920b9d5f68fb7b8885b1308906499b74303173ecb06873ef85d3ea9927c83ab2bd2b74390833d3d962d5eef029c173856786bb86695c6fe76486a6e043ab152f1063d5d1d2044f82c799338b3a2317f0d6340d33a93eb4758968344968888098d3100c874e3a256d79f1c32697ba8c8e891538b3b93f27195db08e2cba1daa723a15f40e300632862d9addaa990432f9ae19ddf4a098c87bc8e6f88cf28202ede0a6aa48f509059ad300100de2061afda4409936302961d889c5116e61790a9a5270c52d34e36928d6a0e557e5d9b7444e89ef0d5a11616a3d14351383edc749120eedd4bd3d56dd1ee17a119cdaac4470d2a858106ac7b31b791fb3c6a336826c41ec9d31269631b44253add6238608eddec4f289f462ec37cb7e4efc08d87498f7a9f547d9d24d49d3a8b463458230ee7a33d372bd8443db5d00d91120b276a44d482b92ece453e45385cdbe32ffd9959c1f58209cae39140edacf4efe9078d06611f285d4569241a6ba21f07c27cc29b6824a61287685902d81cd477b944f0e49f30cca08262fa74f1baf4d49375cdd0b8b3ada042c43f7bd0fd5ec1146d73e5b9cd78ff07a639c38bbff8a5c0ff37ce9ea1c4616a52f3008d2f50e6cfcc2e7309359a475d5fa88eb7ade20bb7467bbfe7344b494be58ec95396f1b5fd752e0a69d1657a83bd5a83f9dfe3b588dd8d4e6d9f8d237c9f0689eb3c5e5d0d48865e46c2a18e118218ab03ea6c2d313c9c75c67e8c49e8af8b4f25afb12d7bd2b3a1fa646623cee44372cdcbd0ace5f061db6b8099abb3c055afcd903720b362bbb0c96bced173ad7e13305315b44ee0b53d61c9512067c725cc58ba7a6ae3cf1b0b5969963f0859d5d5d3b6fd0a7f97e9a2c33da6b7cdf3da32fe6f8fcb6b221339c97c07cf4e421cede48a0224634175dc3a11ce449cbab1217f6b7ad690d4984fae28ce1130f167e2ebdf9f69c0416195b224fc810ab94a29fac3399fe0067c47c7329b1316ab92258e71c9c078ebdc8b41ca02518d79793d0394d85a5a6cc231312466c1a15aa17a0a4c1f7de0b2708e752d6401a555f55ea4c1298147d5c753f7fe56d3fd1a86476d182fbaa51aad114ce6c7f4b0e2df8d8484e9ad042cda153960bcf910c1ee50f74f3c0d5dc0b20bbf382ef50e1c9c2d3e48acbbbe6d5808d108f92f31e79b66d0d08248c6952810b4ad1007d51270a919270a801d3f73ce6f0786190b6255a5f34d0110948bdbc6fe5cc6ba72fe1343d26de315c9604459af5d0e7a51f00b7c151a8a6eff22397ba569229e6894bc9fc5a1ae644c7ad4ddafb77a3cf8bbb63de667e96b7a7d270c043216b76c28a560e949c7059b2b41964def3274581e1513dae82258eb37374373bcc5414e473704c54a7273775f005195f4d63436c1b70d189bdf622105eccc5418ff3222ce4dc70fb5ede7db4d2eec4a2cc743de6a7b6f090e8368c21803ee5bf749f8531380e3e6877c6a938861ac6eb4446f5894c4e0013d25276c26850a129f043499af14bdf1e48d278f120e8d061c1233055e6c883988ac12fa3615b9e056ffcf23ce19898ac71c76455149f383f561ac12efff253140531bf11a5835486f303278fc19344e1f8e1ab6d249faa04376d515ba5f770510fefa504674771695065e7ce96f39835c9b3001528fd9ce42fb355f2d9aef0900b387cae5ef2435e286009700d5c2e660fd962c8e11b3946b27bf1e9f4eff08e85369663b4a1fa2ee1fe92d2c07145dbb8109cb02d698ec910a7f6e928ab96b1bd2a8c58ee1e3dac7d47ed59774dc67098770e6f17f463c23711b88a1f7bf5876c6e8c471f7ec3321de5e68d79e5a188704d76ca92d0e18f2d8acea666a2c7ce94253a14a2c95ce8e96a34a30e95d3c8b750145abf438cc6a14a5188341615068e82701a13211135c7e434c67c83625c97e1a56ba0335c4330c8120b665a81dc05240a386a74db2d391ca01380318ab70f86b131519335ab160fd8f22d6386164fc833c60f1c8f0d1ad734035dea0fbf9abb26255566a614acc0bf2319f9f3ea32ebf86a40682a6ede8d98f8c01a61e47eb5a1504fba73223e6f84ee67b1dadf516dd25e6060b310a0339168f09af875b6556516418869650fb49838f0a72e0c9c6e454db16cbd865ce7a29a6a14a0c48a55090d9cf8365e964f5183afb71a9dfa20420ee7fed997063fc49a250dcf920a3a098db1a327725a78cea57e678bf62c560c9f2aa667f1b7f349e975e3843ee4041a7df2499276a30628af90ad5e1270bf9d96169d1e605301f4d30d93f52a50bc5248706f3833bfb81baf9ef0140acddce9a874546c495202c90630c86a1cb02bb89fdd29926ddd2878d282d4700d022fc2f9f2dd610db1f2917f24f2c80fc1031bc9ded7182ce5e38fa0445c8b828bc907c9088dd53691f686634e50a00f00bb9b9e459cbd1028c536f8bdd9148534c6c6d1d372b9df60ab7a969b0ae5b82ad967a4d22d29a568b4ed407c6a4e3add36dd2da774bccbe43e20c452612f898e813bca91f51b22730fc581a3c4ad99f829fce2c8ba7691f5926f6a14733d10d9bc809e9a0c14324e9c4693b85de9aa726fc50108b3581731d013881b5499068ee906a1183b1020b6a60323e301742240394f24f4c076109e6c225147a28fdfb63d4d76c71f7d22db9a3c5ddcb3d99e87844b73e2a2cdad6199fd16c67edb52a670169116711aea70e3f5dc1eb7dd70df993db9ed3a8ade7df48ca64596e7bed3616ae42b1f6e95dd68363b77ebc99d83bb25770e1465eb01e46e38035cdd9aaac2540e55d322da94ffc7f25e15d47d70a76cbbc26ec85531cdb6796a724124ba571e14a0a2c00f054528d060b662d52a4eab216fb6ca6f3e2a0e59bd3b2b0258c1bdc9ea1f6ff77455a2556a7e958d9e22d7b45df9b0cd335b55beb9598e6606363606238017c4b7deb21acea84a4dd7aa8ae6aa8d7c5515a6aabb6fbc5497525d4965232e95867f93f584862acebf89c75754475426fee5da53ac4c55f3f97f7eaac913f9a9b5531cdedcc63605fbea787b3595e7d7b4ced4d1d49a29afff2f45a8540b66aa9c6df5fa66969a2bd5c024f588549c374935e1445274a4f2ff04accc0914fd3faa0651954f206602b809be463d4237918f6af2ffe628ba515b28ead29ba928336f8a6a459d08e5e9df044baac723d7624475f7dd75a83f7166a19a84b2f11faac1b6d3a03869bc20aa72504d4c50638faa5c0258ffa6047c246822019d045fffff09d67f4af4a909aa3f7dfdbf2756ff789b0fd05313d6538c08489911a8f9efba18411e30021b66274fbdf361272a9d74fc739ac5290e8ebc8b6dcae9680e819cd6fe337013abff38550fd34d7cfe3751f93757645ddd9a0e37d9784db0d6d46854b5a6479ab680b5ac3eaefc0ba809eebfd52830132bde02333d32d17567de536026336fa29bdd2c47eb68dacad33a3434d570c6ddb2c5adc8ba7db6fb56b59c1abad58dea3654e51c0edd281bbae1d0cda2972ef5f4529eff37f97a89eeff5fa857249af2bb4b63739658fdbfc9f24c96596e49cdbfe965892eaddb96c686793bf734674b0dccbc8b739504fd7f9b2b9dffbb1da66ccb2be59eae404a829ead5b53232531bf724a474a3526917ab07627bf36cbd1e626f1f99f545cb7ce6bca8f394f8a51b7a67c9d9494c4e75f5293779364a338d49b9514294e3adc2d31d2954831de1ca6755cd5b9ef48dfee646ef90ae9d19bc916d20290ceadeadee6edf56bee51dba39f23508ef8fc9b68aa8feb0accbf3d4c5947261a496ad4a4d157a35e52b5e35ccdbcddc2dca258395e4eea9eae6513e5914de36e5334080a80002ec90816a3358cee189158e468511cae0e397f3fa2bae5ce2067ef99b7fd776af6a8723617dbb3453e16e1075c3dd0e6011f0ff47227f29c6cd38c6e231f1e91765d1b73924daa2a2fe229621579487428119b4846a20b8818fd9bc654abb9efb0deb7aaeabb146964e7a4aac78279da9822da8188c3a15066de45bc43560ec138e4c0102243584c30bcdcf262ee28386f7f67521f3b32c3191a61a8f79f54db75afdac8ee338b990ee071608d030238d0fb67f9902db33c6579deaa962bc4a690903737aa8d762c5f0b3120e488909a3783787b3db398d939de5e09ed2054f66716431b4469d097ff9d55cee1066d2024680141a77a241279cbef52deaaecbee31c4f4e50d9c0010d0869a046031fe89209b65996af38a6ab3682115375e7bc857af8d5d3acd9f2ef4cb6695dda751ba84ca01b811800d203e8f86fee3add9148f4cc62783c178769d61a0e50095b7f2e9968baa67639b9ea2367d3fe40f913e6c7023f86fc6cf123a38facffcf2bd7a9caea9df1f89ac5cccdf2a15dd7ed99d5b45d916752ddad31ed29ba6a3613dd3ae7e936b3a3a179bacd64f7319b288743a27bb37cc872e266f35633cb73ddba893cab9cc3b56bd747bc76d769aedab11ccf98b61ccdc5a1de79c5cb762cc70343d7dd57e417da998969cec8365dd31cb655dd79db85c8bd6a55ddc39e1a91ad6e7b72a8ebb68aa68d765db75fb9b71cd5762a879be36e6c7acc53e36e68aaea1eaa9ab672edba8dc0ced3f81cbaf200b6dc6d1bd3b9ba95b33e5ae41baa723994dd776f7dab5a8ebbad5a3ddab1dc477b44b22c1a9af21e796a4995d56abbd53a389f6bf95d0ac7ee15efade56e2b4febae1d2f0e4a3e0e5db571a7ad6fc1e11eaefa0ed51b45b2fcc8d963d04e450acd36cbd182472352184c6a75c70555bdd11455790f22a0094dd115aaaab51adcaaeee8ba5b2457d5dd1110f22aefbb381764f5d47a13ec2baf69712ea8eab1f3ffbaaf704379b06f960ff59c794d51bd75e496a833cbd1b29d8a3ccb449e87297b0ca2ebe6c4956dab5ace0c4de3569ec67138b316c993fab85b7d97b2c7bea6720ea7dbbeb2ed9dd4682de771783aee170cbabdb1c9c5a9f508aae100c6bdcdc905833f20caf6ff15deeb80b4cf3f215785d9ed4d4e8077e1954750674d761fd55d0c06c5f09bdb9be836924d3b96e365dee62299b7aa4ed9dd1ff9047c171f2d37ffb8ed1febf8c739feb100ff98ed1faffd631c0d3eb859e471f3221ecbf1087a138f1a3c50fef1bce1d1f066b14d0a81772c3d104551208aa26d160987ee3be79d18ac1d582ae76d98aada79d4a4a3d8ea9a02ed8cb13ca37bce4e83645e26aabb6714d5b98e02755b4def52f6987b9bea61df1aadd8f59aaa6ab43c7e663657467c3b22ed38d761f6fbcdd13944e74867ee59649bdc731ccd49d2bfda751b93eb11b5f79213480e52ce8a38a9e2c88973c6710027d1a9eacdf2b5ebb3f3cc44ae8da93e8f69b1ab9b4d87bc779db3307774b3c81649cc5b4c1522e1e0dc80e3e08da037c53765ff6fa23b27c1cef3abb369abc1d0af36d300040c2862808d37b72ad4ebde6eaab8296bb3aa8d016d8ab4f1c0a6111b396ce4d8dc586381d71a465d17378b3c9e70dc2dcdc1366f9f59ac6bbc62f1cc6270d9e4ad3ef236b36716d36b9a5c8179b3c86346f78aa78fbdc7ad515b73d5e431577eb2bbb7a9aaeae1302ecda83482d2a85cef95a62e8d85343d3476d0fc4033e30ca9335fce24cffccca0fa1549cc6bdac37658eee9baf779c3e4ec3e821e7352ab6367de1d89d7766411244fe673b328903cb9f2c826d8591cb65af8fb9d611a59970a8dbaa7e899c5ccbcad12799bf7be4b7b4f8dec3eb69d1b55bd8b6d163bcface3cc3313799be6ddae9ad5c8ba62df2c31ebbce2e9a39c19a3990b65229541e455c64f99e2ff9b6226baea2c977fe799c54c22cf445517b730af3c6b55ff3a9c799efd54553d3cc97822b3c58bcc103263ff295270608a14d9dcaa704d5b628ac78b39ecdc2cf278e6a49007e582665f795c30dbc8e5720fd6a545369b678c65b33c03ddc58e3ce6b623058801e32c76bd76e6b0d3048341f695cfac7351af7927b5ba51b83cdd66aa756bda891ceccbcc2a328b1667b6d327cbdba1be11d691c5b96c8a6df698f5796622578f99450b14d599ddc7aecfcce4b63bc1be887c1f49fb997b9a358a6a961393c3e4b6fbca467517c336ab733feab63cce1e89ea96e36ee8ca63ab42bdf268b95b090d5efe0aff7f7daf2f27589863621dddc1607045f2a8a2dcb88fc976b7ba6eab1bddc7e4dcca893b2d99c759b862d745e0461a495724fafbcd792902bdc4f8fff7a2e13c59fdffaf6f5d1e1d351eb04b9e0776b1024437bae3b6aaa639f0b61bba5b8dca7569c0251477e3b2c5a8b9acbd39145bf99abb2e6e341da648b92da4d89e8a9dbdd7962b5be0fedf64793e37cbd154550fd5b67869e1c31e39bb8f88ad735aa0fca3bb6d53a310a9e5d4e265eebd72024efdcba99925b79d5972e7281bee1ec99d830022077b2f02e8dad4b83fbc170163ff6f6e782f021a64b993e58b9964f7dd4633579eb79167299205c38df7c2f2e85f44dd9a8af1b56bee669e9da3781b0b112c5e8cfedfdcad6691c3e1c6cbec4657a410d99199b799cddb474e9122450ab31b77abaa698e2b765750588164258c15b52aacde5455a089447771d8336f598eb7533427b5f1dc72ebdb993b8a3413d5c515b9ee30bbe1de604956afa9f02bef3a6d4c377b84aba2a58a1c955a54de50514365edbfe53c2ef7cda6aaaa87e7eec8ac6a23cb5774dd46231f6eb6e56ed9ece99a87fbd88f469db793ed26eade22f9b6d3fd87c38dcd2d0ee8c36477ab5574a7aaf03c75e6ed11b3f79a32eb77317714edba8d2b67d33ba171f70e146e1f7270315ec8c9b1bbb5d3c68db23cafa95077bde3b2996cd97de4ec318fb811e345ef23369b63bc9093cbe8dea858bb7164619ab9edf8dd9ae6550f75ab512237c1dacea6c48cee15c91e73db37aaf2af7cdcdbce98dbb2d9b530fffa899ccd731bb9dc99cdbcf2b4786e36b77ccdc434837dddc8e6c97b4691ecb98d7a3dcfcdf2562e9b6de76a5d5dcab63c6faf7c78ae1c2f13d3132ccc6cae8ce537bece5dec9bc889dcd8d3551787db38d46cbb53b6cd7dd7ad759add775b4dd5a1585bac4bd5dc8769db53550596f5add7dd377bdcec57ce3aaf7be3b55b98ebd6f478d46dd96c7baae6d36caecc88eacdae7c4d895cac45f2ccebf2f85936f791b48bad69de75ac56b3eec1e1886bca964dbd594e5c8dbb4dd5e6dedcb299597d0c4bcb66d76bd93cdb2d97cd21678f601dc56afcca3d9bbaad8eb7b9451ec1f29aa6eaf1a85555f7acea96cf65338d4d5591c4beebf4f1a877f1b7f24037775bdfbaaa6a34ce6e202b77db468eb6b643cdb6796a7caeebcd0e57363ed7d1bdddb651bb0df751aeec71be570628c5ae8bdc6da37cf84b1e39dbae3ba8d9764d7fc5aed77ebfb962d7451e1be5687d981e79bbbb2e1edb75dfa9f576dde930251291bdb3c41da75b9dc689ec66f791e0cad7e13ef6bcd6f8fbcdede1cad7ce9a9ddd29cb6ae12d7ce33e12b48ed6d936ab6165435e471ebd972362fa568fb9ce11f5ff55bdfbc739f23b92f32fffaf79111bfeedde6b1dfb179155bd7bd67148604e898f7bc9afff37d7ddf55673abf316268b2da6d0a1813c56ff664ffb9ae67facf6ff0d705ab04d8bb09be545b4aacdf89a1a83c13535a26b6a671cd2a1a93eae3cba4e55e0303df2617ae473ed96db286feb7bdf68b7dc56857ab7dc8d87dd90eb6859aeeaceeebb74e501c7e66963dbb931d8ea60cfd3c6cef294ddedcab6f2c84bd1751f8fc076cbb15b4557bdb6e53a523db24839744512775cdd9aa22a5fcb538b43d73dec3b8dd55b45ab5bd3ce553474b7c73d1c028f46766dddc737bced7634b26b3bcd87195bd970f768c1e2ceaccc38a4fbfd5cdc19877441546dab42244f4b8f401f6f667ad499dd762cc70b1289dccc765db7598c48e4b95bfaef3b35dbbef2e19093e3cdf194dd7046a4315dd3b63348bab3306d7547598d6737d4c2ccf21beb8adc6a5ac46b379188ae7cb87baaaaa9316c45b29a450233120daefcfc4c5eac1011e83e92fe268cf6ffbdc733fa4e5b609e6efbfde6f216ee9e932d9b66913a780930c2c0d972550eedcc2d573b67f791dcd3bb14cd3ab31b5d53bd5bad2287aa1e66dee6ff12ff577fb0e0bf1dea75a3acc63b2283e8ba7b90a3c81e6c77170b06836247e330ef681ce605835f96f9fffe14f0d81759d3dc931a4fd53d80162dc8d29eecbf5f6ff3b6d9ee9e799bc34e76d7e955b79d392954b9db1df4ffc2f7f230e21f9f59e8759a45ea366f8b75a48a342287c1ba35ad537b70b71a3d225154b7eb6ed734b8eff456bb0ea22b170693c356a329baea6150156a76af2af26e4d83edeebf604fee1c667ad472d9dcc7649b93db2eabc8789a4dd56070b7da1ca6aa107954d30144935b77ab872b1fc665b36e4dd9b6e73306b1459e194dd52d44f7ee6d9ab7757ad716db146ff78ee4c96d97778b23b77dd7a568cb391b6c91c762d76bedbad935b7a259d970f7e86aec46f55eb31b7255f8fb0d5376afbbce6ec8d5b3649baaba65795bb1eb35dd6a54b7bb2d6fce474ffb5e370e76a3eb96e3ec0602b7eb585d44dbc395bbd90df7701fe538714d836de7474e5cd93871656b3b872bb6a98f31162c5ae0e9b8bc8daefaf70b1e7bef9b455160daaac8b656082b6415421422142244c80821ff6fe3bf818816ff0dccae07ff1f7caf54c777d66477077e6f0b99e21f176464e6984926b6f25e24b85176f7629bffdf44ddfbdec460304f1bcd548d6c875a55f5d038a47bfbfd7e3fe3306f1bf9b045f2ba354dea63b1eb62b1eb35e3900e4d83754ceabe9ee88aa84613d31e5df736b5bffcbfd981669baebc8887ecbab8e7f0f6eab543a00fa0ed9b2d9267741bd396e5390ccd3a6dd31cc6eebbbd26b596f15e681e3e30cc5391bddd752892adcb3d25f5ffec4e85080889081bfc9b2b3f2233badb216fc37218abb525dde44d55d543be06b79d6ecf5c6c533bdda25adb781da71b68bcb3d4a9e6cdde91fdc8db9e7d9c4762069e1ac0d62d77ee96c8bffade37829e826f827066525591c4ae8b41104412ed62cb79ee1a4f67541781ff6fbe17112cffab9623e23604d610376f9e60390c653b378b6cf59d2e22d119a7aa6ab433eb2d5c91fb8e1b57bd63887088857fd3987566f9306db591b748bd6aa131f794c877714ec824214b84a808a2290895202ede1472f6b8ee3b76b7ab5ed97dc7ebb2aa91ab6ecb26ba8f47ceee62db919cdd75ab46c1789b51bdd935cd5f1969a29bdd4634a976244fef367bece950ab47b5ff8ff15e403e0111f3ff269830ef95e5c2afdca67c050241768d074477b1a76bda530dd7f7beb1f288d3addc0e35d537dae6e4d0e6fabaedf4dcee71e856537d23ed2d892651bdfbca8fc37dccdb2b8f76dd72403a28ffa6dd90ab5955d316dd7b3844f6159977718ece06ab7f93dd44225238d4c79e893cdb0db59a75ee475d64379cb9a619ccdc66fa9537b2f774b31adecb47221f792658dfec316fb7a95967eeafec23e8a3c1ffd7ad698be46c26f29cd478b9a76d58d778e866d31e97de2492bd570f31df23469fe341ca4477bbd9a32a3ce7786ce1623c62982b2ff6144c55f5575ef1f81c296431b769dec335ed7354bea37363dbc8f256a76d8aa6fa88b27372acde44777a9e794e2ed1bfc972a25c93ffba35dd7643ae02e5e8cc647b34eebc59a15cfe7fb3a3988b4bf45babc38d66de8a6d16edffc7de2b6e8b6967cc9cb77bc5e339aef8e6ffaf78afb8b137d115c9897bb8eeb2f7da412acd613bb6b09cb8ceed101a91ebce27cb5956edccbb385796c7cfcccccc5455a3fd7e6668b9eb3c57666606f655661cd295e5f133e390aeed1b35eb66285b1e3f7b0bf356e4cdc2fff714cdbccd756b7a663113ecb88743ad5b5dfc22ea2de7c6c5cdcecd425ba9b62fff66dec522de0abc5d6ccdcd6ebca1cedb2b4fae401689def196b8d58ed4c32decc654ab683a08707b2f1d36ccdea699b7195db55d07e62895a3ceff9bdb88ec9b3d7318926dcffc3bd18d9e59ac2eefe28acc2ccfed961340150002db1fb6edc51684adc1daa43737aa8d7cd5ed4672a3ee7dabb9a7e919ccd3c693bb994955d549754daa75dd78e2e5ba155d816618bad7bab4e730adf3c691074790ffcff15e38bc6e84bab1e5ff5fbcd78d20377a6a76d4c6a8a1a59dfa7fdf7ba5659a92e6c22bad015a1eb4241ad8ffb7bdd7d92617ef7516e7df04fb22a303fb62419773ce62d9ecfc684c734f8d69d8ee291ecb91ea306f7fe50cd66e36efe1ba8b5fd9ec672dbc80b5bc80aa8066deec67163337da864697891c5d9146231f6655e56d9e9aa11abcc607334a6652ccd868c8d2f15e34eed000e24563c6ffd778af325a5e6575cae650bc5799d88c4d33a0cc30ff7fedbd7ea37e5c7eeff5f3f1ebfa7f13ef25e3930c2c325c748a4125868bff9ff15e01c40aa04d00550270f1bdef3535b2bbdd68e65dd5c7ddf6be37717dcb3f31f36c43a3e3c35d87a6ac30ebdc3b67873a838199660e0333cdaf8eecebee2b47fbca612cf2af1fbf4c76175b1649ccebbedbf9ecc3b4e579bb6e4d8bed36f23e0c3bc1862809030303fb5ab76a7ea1bb5d911c4d55815fa89afac83f246941440cf7dd97714877e6fceb3af7dc75368fbbed59d56b4a5c7946b26d1fb2bbdd46cdea6370e546de76e4f0cc7be5684e0e57ceeee351175b5dc4cb4974ef75987771ce3c83e716e6550ffb66f37a4cb699484cd1a4369a44def654cdebbe4bebd0cd227bde76bacd3cd134d8f72606cf34a39be549a490b767127976e6b07ee4c59c64758a6e634eaefa6d45fefd0218c9e34eecea12fcd1459612d3acae5d6066ddca59bcdcae1b4f2e9b5d88ec99af79d5ec59579c0a65889e2bb0af604fcde4b60b8ac07efe719f060c7c24578df27fccc03fe6b3c03fdee3e692578fdb80253ee68dabffdfb02dcf4223921d669d3b9608babab23aef3b2e64db28773b4bdb293b97d155ff7e4223924553a4a69a78b64863b0ed5a3df71d8a8d378512a9ce9a2c6f35bbdb3492290bf851a1f74dbaf4c955eb24cb8ddb98798b6aae227b6a6ae1aa8793dcfce3042c24d16d64a8db56f7df0fdd2bdedb46b9dbd0c73f46e01febb96d6c8d4d71abf024ec7fa37ccd7b4dd535bdfbd28dd8ec312785970d96e08bb4e9807f9c07cf3fbef38fedfce33aff98ce3f9ef38fe5fce3a8208cf38fe3fc6338ffdf7bf3f745052669d7ebfa6cfddff24162f4e84ed956060b734c4c888aed946d7f68409b799e596d75303d4db9f4a8f369eebb6c62376dd85c07be02ca72bcde355c5e4adc2c1c5e7323c68b2e87eab3182f76d767a83efbc76ad2a03963068247ff15cce6e481e04b86c04ec8fff71382b23febd60666b828a84024c8604c67cd610ab8ff7f01e52bac78e85675ab6ffce3325db73aedff376032633688b11026022e000ccc3ffef28fbd74f97f1d668b3caee9b9f78d13796c917b13c3f2dec4e0dec424abb679482e5bb01603c0beec78b0a72658cf49c08239ac7f9d48f295dc76c1a430183483bd6fb66e4d7ba168d0c82c72d8abd241b3fac8852287b528d6aef1b8622042f5ff7ce5928068cdffb77c352b76bd6644ae4123461451e6ed214fa6ef509c43c77cc8cbd02161bb6eb9b41b3e431b1c4864ae4836f336c5eba990ab42d401eee6c05a2158ffc04262d44267a19c7237428a902ca1b741af7c83d49861c5ae8b72d934ea55abc47c0e82214895a03e82c4085a13f4f5e636f2e17177b403cf73351b28d4806a668db269569bc536c54b63c30d02917afc6586854515b7018af3398cbbddc8bcce3c75abd1dfefcc44de816979173bdf78a62ee2c1b5f138f2303bcbea61ee1ad55c453b2f1a8bfda8d7ae33efaa6ecf6dd6993b871dc3e6549dcd76f7629b168729bb8dbb78dc756b9a1ab31e729e41d4be026bacdc473677cab628da21a76e751aca6eb83348de5ba8aedce443bcbd0e514b7b43b30b065575e3fd7ebfdfb1071912242c8bf1babeefd2ba95f39e3bb0b76baa5916efb8b7dd90d38ef3b5649b0ef5510fbb9890dfd5ad28cbf1d0ddae1a5db78e8cb6dc0dd87237ddeab4e419badb549816390bd7df1af5ff1fe07db77aff66db6ea8d9b9628ba678e8aa85417477a4d95ab1eb6250b3fa6886ea5d04721cbf5f5045f52eea20af0b0e57e03f26e01f67f9c758fef1957f6ce51f57f9c754fef1947f2ce51f47f9c750fef1937f3cc03f76f28f9bfc6326ff78c93f56f28f93fc6324fff8c83f4e62bc7f7cf78f91ffb8fdc7fd1fb3ffd8c83f3efe63fe8fd77f6cfcc7ea3fb6fbc745705abcf83cff23d511ff2be1ad0338e7f606842dee6dad5df99194e5c5b38d875b500d07504e4ead2d981607f401c72348076c3bf31f62fa9fa44e026f12b9b23c852fab33e5bfd835aa373f1af92eb88d72e35f1e672e2a743310911e817bb89abb38679e449edfe6f0f95173dab71b725578e5a3ccf9c9cd6120e7807f33290cdb9b988bc334eccc7b138168aae1f6be6176def663da1e8da97a34eaf33c1a91c29c6c775d1897cbe6316d4fded5e20c301579464e9155e4af4c231fd336d86e7506d8316dbf86aa1e0669e464bbd5152b7258db91ec799ed93ca6ad3087811dd336f3f52b8b255556aba97145b262edcad953d546b6af3c5d577e4cdbac75de9b184cfb7976ce471cd3964def46e4dd228fc653d56f27706f6270ab5d0bf7bec1b689c1b4bd6ffc3f8a7f7ceba8c03fba72a11b5b6ef8b841bac96bda7672166d579ed3daac6993a34d053685d8a82bce53ecc8db9e666df258e36acd99356bffa6c60bab43776e35309b9b45d6a5c5bab4b8c556beaa59a5268e1aba7fdee6134cf805a6bfd2ac4a43264d5a1a086816408386a52d1f26856766b70f8d893391ce2c3973fea3abb6d3be332ecc6cfa6f3bd2689a71f3959955064e99b7583e3276c8f420f363188d69f2e619b6a6c9363d4f60364fdef6547722cfc176f7e0b1eb1c4caac1dccff6b86788ad7c0de32b47fb574ec784f9c48812f32569f6b39f28901357b660105db9104d8362bebefbc4fcfbc25cfa30793ade760b534c0a7d61d6de44f79af222394ce66705702a80ca9bedee3db9ed523427fb16ae059c0f2c800610ddc7636a2787b7ddf0b61b8aa240bced06060e6f13d754c7175847e470c8bf246a77328d2da739def7c5c617afff7f2fa4bc88f102b77261c6db6e5ebedecc6b17586f764924c0fbba1ceb589d7689f1dfa5c13f974b6fa271a9f31ac2f0ff1fa7fd9b76432dccc933f715dd79e98d6cf6216736935debc86652d5a8ce6b9aeff2768b34f6339b3d838525db74451a8d7cf815569c1b763674e56746cede687baa83cfb1b5dc8de56a78fb06bf287c59e2b29468917c05b26369000b132c61a7d1a88dbdf55d497465ce15baff1355796bc5022b96ac286005fdd3d4ad4edb6c1a975c75c6db6dca5aa902c93cb7aa72352cb8f23458ecba5825c49b7d05b6eb96a3c2eacdba5498396f7d54d4acbd4ddb2d47a5a8a9c4a022e14d344fb7994876dff19eeece72bcdc83c389a1ac2e265555b7c06cc2b5c5ada93a4fb799690f0ea7553ee42d678f59e57dd7addc980e39397165437751efa4aaf3749bb9553decb9d543ce33485ed34c2472b18dce486a3b3e363646f6c58b68d94c6a63525591c6b4278599b79a08d79ac3d4a8d724da856daaf15ace8579af75a9985e67ec9567f3458b172f60f035e371b62e7723d24736cf73d548239cc88d5d8cdd771c5d75cb5761ee31c79e5b7335a98f9aa37ab378e751b7b57cedec2eb6bcab7b856b43a3cb291aa7cf463cee42b791197af865ac72964f49a25ef92517af23d73f27ae6c68c1e0a9d1727ad42c129816ebb8aa730f6b595eecc462d88811994db7669140363860d2c872221b1c30b8eaa14954776fd3ace6aeed869c3d5b9d69d0387358cfc8aebb50ed61c2a358d749d588ec5de730d4cca63e7b703855ef8eaa1c8fa7783d6fbcb64dd9cdea3417875a2ef7beeb345c6777daefd4dec27747f76fbc3345dc5978145df9d9ff47f84fdf8b8305a403ff2f620e096c2d68d9b44df48eb6144975abbca7f0d19e2d8921d298f1f81aba3b32d859fdff3fea797cbdc99f305eb00876b663c76c1e568cd9b7b06bb64e6caf695b3637d2883ef29c6cdbcd1e873cefff4d96eed9af37a2e8df043b597ea2ac1ef614cd71bb4ec3a9c11969718425d6a643ee3bc6f93737724df97aec7b4d81be238cff8f7b1f47559eb67ce57cb8d0c76324d976b37ddfa9bb1bb7cac3d096afc52152ae5d59997c5df9f474a502b7ee3892cafd7e73bb4e1f8d89dedcaa91849182fa473da2aab0eb64a7c8eece6747678265155f6ff9d7ed586d2c0ed77d66d6e9e35dcab23ab8f261ee1a6df366bbd647ce1e9366e791cd73ab689a0e39f374db5651b676f7b3efdfaf9f4975a7755b4dd175e7af8c6e644eeeb0357dcbe6aa87c82158cf449edb3ee424bead5b479e6e3b83e07135275555ab9a98ee2369df48ceeb32309bedba7baa1e8de9aed3c314d54763daf1c0c456be7ea16b4fd3f65dda59e470c7a1bb23359c2a545356b3fc68dc732890bbed3a0dc7dd765ccbdde6a0fc75f237c8ff17df0b84dcdf6df698b7d1956dd7693833b332b7ce22b9b05897ee359844f566dbae5533b37d2495cb61652c5af0e0edca96c362208f7a17f79154eea8f7da3e92caed3a0de743a93cca8666b2bb25a2bbae4ecf9d6652a8cfb6afa9aed37965f71d8bec2babb7ca39ca8b6dce6bcde6cadadd837bb3adf0f70ba27bcbedcdce9df576dd461fc99d83e537ec383febc99da3cddb6ebdb3f5634777d73afa1cdb0e35bbdbde53cdd74c64cb43cedf8fdd671036d8eeba7c9e794dd75ddcacde603062b408604864bf32baebd86dccbbd8733e92b66bcaa65a2cb9ed32d89a93ea57ca1e8d1ceceb348b812c1bee1e6746772f0e913d13d3368cada7e79a8ab53bc8d71cd65175a77a85cb26bad5541359ce967b8b54d58daab501e36e64bd93eb997d4084986d687440d08058f82142f28f0a7e101a222487a1690e22c4fcc7c21f7b67b29ef6fedfc80b49029f1e819a45024dcd2275b0e56a37d1dd5b1d9654594eccff58b740d99daac77da3cddb6e5b3da6ec1c9f438b5dafd9b1dac8ee56fb381ad9b575ebe8aa0a5cd958b4e0c1e7d024bbeff8b0a7691d4ddb2847db2847c3db6eec06b2b59daf1d8d433a23c6b4b66b1d289b6e755a8b7c3b1ad9b5347473b476cba169dade37d23aaaf7dad0489bd6d13b57e5d49e866e54ce38a46bbbd691c7852b1baaf75a0fb4add556ae6e4dd9ba3a60db531d3db973a07b6593db5d17e1d4563c9e0687c7dd78b01baea329cbeebbb7a3714887eabdb687e91dbbe5da751bebb61cbaf233b40f009f387c7236febfe56a47f7abc959280e5b6deca9f1ff1797e53b1b44f56689e911681a91ebce4382e4ddf634d9759bb62a32b7bdcd2bab76e655f76d34793a62b37b6d44988a62c488bca67984719837c238cccb615fb9abc23a74d5624916350ef356241b4cb268924dcbc6615eb05df79d5a709b810d778f1cf6651ce605d11dec6a2b322ec9a6198774e911681cd29de911a8aafacc6143cedfaf67332c450e4b917f756bbaa6ba1572930d0e080ce6bc3b322d9bbccdbf1439b3baf73ddc6b0e6b57146b4adce8b09fc354dde8dec4206f83289b8f13ef44d936d138cc3b8d43badf79e614d96c43a3cbc160d0ec9122776026a1d32370ae23896c70c06cf2b6a365adc48e98f7ffc5f7ad39d059b3cddbc17f4ce4ff7b8f87fc6321ff38c83f06f28f7fd0fd631fffb88717e6f18fe7e2fc63b9ffef807c1f8e263f5c77ef3b88aedb6ea8858fc5dcc01166f6235f53cda6bdfaa7f5ff49be42507dfc163c0b29aaa409c75d52bf59544d8bc39beaffefdea726e43f88eea40b734c6caf6971a88d6262c71de83692671c77dbedd2dd373a776ff868a130737ad4f93c0ba2e56e26597d6766b3e56b07e6751b79dbef3777e6e2ac04d011f00950089460a38f8d2a3e1bdc46881aa66a80f1d530cf76f76031673bddfe7e689a7666221f6e940fbf32d24896c5c0da2db7be9d99c8d3afdcf39a6eb62375316f0fd36072d56dc1aed5610eee95bb05879c6790a4306b5dcc5d178f6cec3ea25d3562dc06668efebf9b95b1c3353d1a756bf6bc86a9c2b4efce8bc3cc22f98f6cf68db2fa78d419e5ec31a748a1f174de67d9ec769c17f3b98f0457bed1a45045128f48dedeede1ba914676cffa1fc3b1c8fb48da8fc836ebffba7f21ff0ff65fe57d33ca74d6dcc5a11ed6ad69dd9ace589ba1e17f97feb79177e0af0ede76f3fd623cde26fa7e32f2cc90d1e4cd0eb6596efccac955cb00fa625cfa17e616c5ea8b212606dd9b311a70b700f27c0079be00341ebb51be866560006ffa60446279666166f6828117575ee47041cb459c37d1807cbd99c79415d36c2d78b48860e17d2c62f9581062616491c1470689cc8a8fac4856668a99eb56c5f2800079539181641559eeebed3ccfb094e5690f7bcbe64976e6a41a67eab0936d2ccf37e6634cc60a09561cb0428829b6f21ce6334b8841f28919129b2216e71323111629ec49185c180e9f0a593e1588547451619682540a08522849f1e54bd1018c11981330b42f55ff39c526a66aee6d6adc6dff89ed968c2cffde56b081c9651559ae8eab5f6fe7576ed94de427b2aa93c2a1994fb9338785f5ae8f62a6d8ca796431f3049353d554d52bba7ee5b4a76c790b7f67db964d5ec4cb605fb98ba52cefe9d9cfb0f43cf3cf3c832759163b339113539df3749bd9dbdca62bd204cb61615f44ae22bb38b3d9f1b80ecee77ce4e9361edc6d656337cabe11df5ae4db4e598eb76f703ee7c6ddd03d07c84cb3dcacac5d537edcc674aeab1a98a7dbf66689bc0de2edd5c859a20938d3c4d897206582b13d359610c3ee56ab25ea7c2534fcbfd93b57738be4612d52d8359e8f8403ff4f0209897d4cb62db28ed5374c58c64b663b22ce9b422ae6ba8f7096e81bb1362229cc2d70844f8420130cddad082affa64fc49830c352552431e7e1ed1d377e6554ce97a9f832dc87a8f56fa2c28d860d597e03dd78ba38876e74a36b3aec3b8e0ddd7167e86ed7b40dddc5ae5539b6d5c71dd76eb9b6731d6d47a6a11b8fa3ab9a2fc4d7992f840f049f7ff3344355be3657467ceb01a2098818ff0fe27d20bcfeecc3a357f507356fa6c5cc79ebfb70de65a0efc3d8bf799e1f56f83cf03153941bf316ea56dde8ef37b7374b0ce2ed75e5a959d970f7f07998eb40ebffcdb0dc759e9aafc31b5f871e1dae8f03271f07413e0e6e4cb09545ee627ba7e6ede32b8f91ed7d976a3ccec3c7e1c40659be0d7b7c1bd20d2ede64397be3d4a0e7ffe7dea781884f83075f06495ffecd20ba874874b32b8b149e614975b3db185c59a4309875188bc4e3ea16e62312c5ca22cf7c19b42fc3081f060bfecd53b7ea9d4e6abc5cb7b6ba238ba0400c787c18a0bc79b69cb367eee905092e60b920c302280b7e7c16aef82c20f15948514192af02920a267c144221d9f62befbb54d599b7f908b6bbaa895f7a98c3ba2e66553df556391733777a4c87a6396c770eebbb4e278f9b3d86d965cd0a7318d899d78eee5617dbe056853a9beddae17d14f25080f2e6dcdb5b5c4e0a735298d9dd9159f3f051b8e0f369d2f03e5f219fcff8ff26ca8bed3692508d6dd134f30fc9932664c830533b3de43c836839369fefc7fff73cdd6626575523d1f42e45d7744ddbbe91616816b1576ec7d3f26fafa99a8dbc6df95936799b7b9a6616c9d33b5ddcc276d725b5b1efe1ba913cc9964d399e96b3ce47645ef791ff56bc6447f76691c3ddd7f4dcc4550fc338bb8fa429d81422dee2d4806437e0da5c8e539b03aac5e1c86a67794e8e475b36d77de4d944555edc46ed96cf15642f64b0205ba1aac8e38add6ab6ed42645f511cee36d9a6ac3eae30f2a1294416dbb46ec58a5f3fb66df1cc7bcb659388e4779a98d73413b9dd6689195dc38472aad9cfb3233babcf1ed68944be51cd6ee3d989e43ff2799e5949ce66bad569398ccd616665c6b42877d46dbb4ed3c8396724396f353d1af990afe779aa6512d9247234d7b5bd357b9bae48ce6e20ba55771ada0ef54e63ebfbc8deec0d54e56b7aa7c9d5ad69db851a5df7314f2d4d8d2d2ed9a6688a3492d6f538b2e3ec865c4767ed865cc7de2cb1d76995a800cef335b0f1ff3a63ee6b00a12c43f13e068cd2d27c0cb698c2943df33180932b5df8ba36fdbe4bc57c5d6afe4db4ff2ff967f24dfe33bcafcbedffcd7ebfae87bbc7dafbb88270e53789c454a8aa9cdd2a2fe21977b135f256f7b6af3796f69587afcce22b7fa27c780c4ef3612fdfa5f4ff3cde779bf82e0bdf6bf23d1edf6f314f34abeadea836869dc561abcf4e24bbea1c3697cdb61b9143361d6655d5766f67460343fb7acb6226d8b9bb3efbfd4e7d4c0a73df684f8b735fd9447256b79cb39960c6fc95cf1fcf2d5ff37672d5b74edd62742bef56b6e5c8961a5b396c59304f21b207d1ad4f166429e0ead27e9e2b98d8bad976d578489e1c6a7477246f339227cd6da65f44b6bca2752b57756ed5f486694cae1c45ee7ecce8cedb6846d72d572b53ad38645f6fb570d4da40ab11ad3366b0e53c2ed84d5d6c533cadaa3ab8efd28d06bb0e9a1ddd686f26c41f2d44762f022298f5c82472ce1e6789e16eb38a9fc696dd66a15b16ab7fb3efbb54cd2d5f770e598b4c59b23cc422156b4dace29bbd4dcdb327b7dd9683f50956175851609db020bc52e0d5f96aec025b6fa23b2f259e605f1d4d83c96df7fbf5e4cef1fbcd9d190566b383917dbd9d592cd9a67999040913acd875dbbf54bdea6dfcca3ab7696c39ddc2dca258fbe628bbfb9799dc762af289b2ed1c6772db65adcf5d64f50e5c283a9d9535d40cea655f119b4778d9664acb28734fca068670d7968ebb58b8fe5c380b6a384d038f1f920942e985499b4ff381d28d206fd56d05098921180bf4647a10c1a74815fa220b9c25a52c653753b90abb4283e05e2f87ee982fba07258057496452852b248f9bde5c9742cfa481578f2a93a81245a60b7a6655f48541875489c4520b8ce6e5761d4bc8bd9951103e0b9b4a9f0492959b424cceade5075229378470e99888ca1f7acefed08930180a193817801568b8d20708d71432c69f9994dce2213ccc67ced5437143e133f8f972b75ce9704ff432e04a6f60bdb50d0fca35305e5d17a08de10799349430f8dcfb8892182eb916fa8ae8da509e8c9adc0a02387bb4296afe7c65e3baf5b8f31e206428d180e7cd95ae943a862cde800b3860240114efa614e1924282bc3b0241ba193a45b8e55d18de70409b3f41c74c294104432e223255b0ddd7bd665d8dcb0801164c8695865242998c3d8cd19520baaa604873537a0a24ef94198c2429096df9725dd9aa00dbca0cfec69d8bbbc8003cf7876e282d8469f03e30a5dda01787471d7cbda51010fc025590df67a8769388992a8b732c782d42f460396553be8f8350f791d1d1a78a93e21c87bc2c4eb195050ce76011145e6109ec54702d4805567e4a6078f11c25e829e923e1c23021a577a7ad712dc060f0a5ba665d1b4756fd0620679e78a960628a083e8810062e6127ce15a476044b2104e119fda9742dc09a753321cb1e9104a3fe8e04396e9c4e767749201af8cd29bcd885a63b6499e0555d214a374bf8941d3c84e121330ac0663048ea8a61cac63ddbd062427042dd0a02f8c196e21edc3b266a17562726df6580347c632d797d44f6dff199f2b05699c25f1ed8f2694adc9477e0eefe8aa827f890ac3e5f2a89a4ac4002c35bc0f3058f49cddc07a89cc2b0c4d878326a6fb892ad1cb8142721d621e5014c25142cece7b7e78b3f2038918d35586d0234372c0e211c659d543e2a0bddb30672a5012da07fc1e4c85d5355e96692ebf2582ea0aba40957f9b7d26ea5146fae221d58feed5190af8443f89be6c0c16ee856dc1225b4945ae0fcbd90628097a0ce83cfc2460b46036b51c975c8cb8df103cbed81c549a94748a10ba882235da172fc6e2b0352dcafaf1a18ce2b225e6b8df92ba3fee96c68f46e369efc243461de83ad27d7850d085e0107e6674822ea2558d2029379e1f4427208296f50a9721b58f8f05910b1dd1f48e4e00be038dd1d423ce1fec5d56de3baa09c40023365180d4fbad8424430d8a5bc1e140130fc9b56b6bab82c404129e1d697267e2c70a439b7b003248d7e02455eca4917fe0e378dfa1e16316f6656056b3851e1ae619254961883eaef885cae020ad47a2686227853183add2301482a9b78357aba5043f089afba0ba296a38f23a7f85b168dc04728b2f0874228f0006e1bca1a6316ba9878fadd39176a7131a9e1f5b6c8807701384bf098903d65da123e5fcac125cca2ce86d734e7d5e5a423d0bd93e9dc3e82f6dc178e1e3c1f55112e01a58e4ff3e9caaf9870f61b0420bb0a0873dc376f1cbcaa15661ecc9bb8776b787cd20ecb5f5f9b3f9326d1bdf382515e70e8826f75f703df0e6b6e059896743588e0e456606115035f0bb8d286191e8c47d6e7a7b012e0b3c80017c3fd282da91d793ac03365291376bec11d15d8d511b147d52212be0137f66a408df8212041f70b098bdbc0417bd7f37f8e2fce0513e996c641493ec570c14f91b507cf12f4a5d4a307821bc294a99e19b731f7347189eb018a001e342a50ef48cbe8763a0e5fc7024fb702200d4a3947b65eed64e89af0fa7143f98eefe128e9368a73010e9390e99f18cddc3c3d71df240b58998706828b6602a84f53c09b6f3441c9079b00ba3e861029ada03a71d750928323d409e2524a12e0ea0000aceb684a5baf0792705b4c28731b18740613b9a256f6803b551ae1742aa318a2f4775c28aef440d2d5a0c9d27312222ea43f4ab01a2a75df4101f3f270e2e046e2f2b74d835a17001774f82741c6036972ebc69900cd7da065d235b601de05f28cdc501dd8fe436a0086f0000bfe214509d6e4630d5eb37301179b5247aea2490bf43208028181b332707dd13104775923eb81b1b0e4668855a8848b43fb086736fc86449afe0605284abb58f829316077d9704df90793325dcd16e376c043a574206ad5253501052ea005a99f50e6cfa39015e65e39cd4a281d5a5cb8bb4fa55b952c78ce4a9c3243962d4f050f2b2e589be04a35766e30de124db8d7294df9e600f523e51dc262274c5c285b2e954056577e440d2f980f21132f844b74816a1edd08da09ee982db56fe4f505e7f02abe033f5a613e3322b80e941dc16d84440fc4e4011f3585f80a24ccc00cd86273cb405a52be353984ed4c48b9b75032efe878067bf244e5367955888bafa882d16cfcae9c038a708e46702e9235f38c8ac22b628c91eb9629c2fd9445cbb379824059498ff16a69f4e67eea30e1dee92ff7b702ce6399400766401c75a7a4a995a6d618f80f444e94544c4979de96a3340f69781d2568fd8f1f7aee26e3e330101db000410b7a9e9200cb38b528e13152e06ed7aa065c4456e07bb4d5aa572139c18ba2c0d44b2971fedac7cd654097e14db889f34243266e7ee0e93f82399f2613a12fcfecb840347af01e469c3e102d18f7161c047ed487f52d999df9251d28941672607844434d1f81060db84992455c3242f5c01138ac612f3630e0092734955abc12c05c77687a4d2cf44a2f4460b70047f7269da0c23baca03d051a309556f29f777441f1d1800c5c595d6ddfe4d3da5dabb3402908918def7266813765e5d42b11f4df458f939760155e9946908d47b96cbab1c284dd13c3da3b90a40afe936356a9c3109ddb674e9c8b774502fe308a03de2087c85780670357188e2498c9017f7e025632aed18991c7c18ac5eddd35c00e586af31ec87c57102709ee1d5100940974a5ba502285c23b287cba6c6b448c4306d7b3ea332c6da532f1381060722bf8d37b0348d8c29d5eb5b98b2001fa4cb61c6137b08ebd510eb0b988e48a3d0a2d36e05aa64c2e11c02ed8b784abeb47190ee55b724c979c0d2fee9b1d6c4a4d98ec2e72148b07e1a7cb5df22ad60f42d5bb52f284e5841522eea10f444a39374a9eae0d53ca199a7a236aee7c143042eedc0f07ca09b74e5d31019c17c3ac523fc3c924eca891dbe7993009f72094e791cc68b974b83272313148833b4d32f377161cf4ba9be1b7b920a8978756761f050086296991f01cd07a37cf0c082e251cc7af9067ca1fd0e0013c6450d82bd9d00a3f200cf43a4a797a063100b879544b1f24001457100be3d6b2b3e3aad870e7c75654f0096c015e8f0e42778d40bd24ba985c2e2f6d4a10937adc369684e5061627ee05556bc9c0d802658944695c1324535c3ee4c28f3d292f036862dd364a1ebca0178dca0c65487c9241246e2b06c07e519b2edd0cc5bdd78404eea307eaae21176f8f8a45fa0c40a9f9072eb0ba3bc59292021b8bcbe9961b1663c4a46464a25c138e74b88feae49e78b0a25c6401904b3785e319957e5c241240503a90e0d33585aac30f203cf26924689f4e92a79b650cc2d7b900d25504a7f60c30845bc4dcf12e90213e03bae7d25158833735f0e636ea42770538c57c92081e4a2325f2460145022b2152b827a08cc0411c4dc21ca490e97d4969b10ad6c59583e4ebfa189ec26030385c6218b0476a049d4bd7860136e3a3c345614955e9a68045d7d204784a1e1560b90fe69428a35471bb6d1a287021fc6e3c94076ef8549755ffaa899f4f12e7d54dae6ebfc1d3dbb3f240e56e516aef2a98e672b961e216c2913f8132b2ee08238d5e5df978879a7b0dca9c380d57aa924b983097120461ef89241858dd4464a04181211735a6b9656ea1ba1922a8282d3c09fb392ab47c8e0646fd8914492906048b07fbf0f5508adcbd1e0da81e0059aefe889e31374f935a1fb78788a7901abb660c6d287bdce9f1782c41b925ce5260ae022ae1b5558af0963037f8041302a34961414f0a9b4c78ad6ec41d9587049673b2a77450c480dbabd0fe8a52877e55dd8897b3b6e59e298bc36352e2fed35dbb5e30bc78320c40bd1719c4bfb588d43d4549d33d72e5cb77ba9076b586b9cb8148e1be4a205f1e5f936e094068ae0b5628309a1a77f02453a570002d62f5a2ec41a03ce360e10a12e142b909c718ae09b404b88b2220e25048201fd7a2d24bd281e66e9140039335a8f07e9cf8c06dac483fc94a9b3bc194599f001709ccaa92a777e31a82c192149e0f0e2fbfe88ea2ff34e501fc746a0faa015d49094d9f0793448195545ae0ba0ac10187cda282bb2c8460b04242abcba5a22dc37420851779f8d4cb446a0946b3c3530f0452c3cf3223e829280e792c4eea7ca556102e25346517823b7c6ea44ea65e47d7f946625cf9a74a95fbdbd4e629d0d27403ad9a72d520c0dd19a7d92349d8bd1a3b722e1ae7c387d8f1e1d16e04c2293ce0f58e87e4dc427e8270a152385c01648de2ea32a2bda4e6c4f0980985b0240cc127722973bb548928173801e9cf96a07aba245abe0b938c32068d52f713090b250c2718de4889c47380c5ea96e892536a28dbf166e71a962096f64c1e45fa2914f4b9be2c767815a32ab70758976b0b44daed710a7589b6e85c116d627a650d1b1882081dbd3a0d645c37680e95837004fa04e01c7b25dcd4bec68cdeb302813f45d88bef0179f5b7ec88652f0b8e9be55589d20b89530fc8890afce60ed78d64d45416308bc247906503cb18bb75bf0ce9db6894833b84932995dc317bb35626302d506d17902cb75e4eaaffca37a5e7e6e9773d5c21c4255646c7e5304010ae114842e1081ccce1e220babc00966061593d02ee1e4a404a52b41e3d170aa23cde91104fc90d03e50306597e6dd1c63434b9785b4dc0ca092a0c70cff4c1f285d282f7138346d86c78e63d2d9155ea7800c59d3500217c26ee0bfe216e5c6992502ac55421d2f39d68f2750610a06c2341dcc592a7acfc5169c4dd90e843b98208a477b0d6cbba372fae001098ba769bda9e0d19b8df80995c40953efcaa4116dc4e65791718c4ca2f910506173a40cb6df1a55e5860005c5633ee60461e7edc240bbcc13d51affe0d13b8876407d3c553c9065777c0e072f172a80740175ae9f5e72cffacc1e1df03007e53a70e9b601de895d85b7e13a7338f3cb4e37a92d26222c049379013301f8907015f9762c21b4285f9b0535f309e4e9cbe813a0f57d42bba37fb206ce641f351485db907408a71a3c4217102b7487da74f264a4e6008e10cac54f5d45e542ab92554984e1d117f786b528e0972771fe0228169a16597d0a2485d465019f07100741ca786a16f8b02ebc60a42e3ea0a202e1aebc89f70b3f1004c40e6428a42e6467935e91b0de16f00aa42ef874401f802060d5c269134610a6404793b699ddeca8843b7148a097e500317584e5598d245f287d5daee359263941c54d1dd03b6a45d1f62f4f460b5214ab8354630901580ee0f053e611774ebab7dc15082b860d45328baba80b4a3bbd92cc25a1049f0b00468f174c4de8711c3e24095f81d2442d0f350247e4c6dcb8f419add220476e5241d5dbe9583086ea060272cc1d68a5dc135794eaab0e054d287eb4bb5e9c671a5e27680847fedd3976b254e2ee91051f0681c845c0aa4d07b878180eb28cec56dab71a24cf506e47b5fecca546254ae8f2bd1ed0a85e80b0d5100b3a241f60b8cadc1132cb073cf203980d784a1f0b088b8781c43e8fc254a14ee9c1d44ee026d16f4c8bcaa3b418a20a5260524e1e1111f5c38ce3061041258eb1a6100a61b009a987285990f4f06d8ea1242e5c08d5447c50e204cf93d29586009b218ba0e10d92f8b16ac5772c75e365c38bc291b5d7085ece9636c28a59a4a017c19895998ce55a66710614e19a26cee5a4292a4ece202132ee1e30ceeead0e80db8c00457a3162b1e53a1115c7a1841281bb08086b9aebc282bfd70f29ccc925c534b50dd436a62bd05537238730a7b3c3b15604611947c0c3057ef85c8d86b62fff24180cd23fd206f2451644fab02a03b0385a2df10f6bb7a9a44f9319c207dfbaaed2e3a3107b31071c0d5c0e5e02e4f12e1b7462fb8c08015f09cb9c60549d0a44bc94caa1e8736210ee3a244cfad4d037f06cacd95c3aad297154982779880f23306a5e158859a3c870811ca734c810b618c206c2a8791970013d9730895e17a85d16ff62daf26c7c01dc2adbc08332e3f01588bbf76d0944ab0b8b938c0a4625024acd0cd90e7419726172afe01e745976aa6c7af75a9dd560c8077461f4ee505236457c29c58b80004fcbca41e4f2e0251b25c4070c4de4c8cfc665154e11a61c2fc05b4ce9b628028cf032c56697680021f28491dce2340c8a36aa694732ff7567ec8b8305651f0679e528fa3521f6c4006160c2cac213027246cb85c4909c30da04ac32f80233088dfabab66eeecf1f86ecf003ff9da6a599417bc90bb86ecfc5c5549a8bc8ab42fb74f9ea8579169d49b39cede962c237f77cbc1a3d151eb11a501792f744adec8a827af06aebf225926ae150418943c5834709d9c39b9450d0d5c353fb41e0b10741f2a965ad70d34b52e005df63d13a6c36db38a540f0e97849fb40e312d07696e510d19dc4a45991793c97c96cda1a740938e2b84c372bd3edd29759c81b96e1c15ba8f2e55f939a17a3c9207a0ab03eec4c7cae275735402f4abdadeee2a3342f93687d1e39005f6b422ed4bea478ccb401c0925083123b8e2dcb77bebc87a0e8a78dd29692cae111ea1ae9a82424f5598b12bc8ae5259c114dc5d8bdcf2d119d7a55184ec3571e17953a82add550d98fc9cb052b7cc91e0bd53a907269ae9f273fe54287d0a5cba6b007370d99dde0b3bd7450349ee533a2f9c00a538171dd350ca9a12e91a5a3457b6b160752b31c0580987bb47e01113bc470f5a29e35689b2cc88a5fb472d15aec2ead02bf000ed0612a5eaa2fdc0f265ead45c10bbc77ba194fc3427ba3c32a7782b5810e8826971f23a2ce0f38438b8782d561c94168c4af2a615aeb25267f31280c8d20617331cc2ac2ddbd0c87175b8f1c4e5460719a67146a834d3b10707b0e9bc854c5c509668f5870bcb0cf6b97a46101ada4078508588466fc5a9241b584b4e2cd408c0c7852c4c6880b0d021c383e0190a62ff5ff7141c68f8ce9ae826335d88d96d55a36232c85ebc60b1efd8fb494f891722ebd6fd7276e9d24874ebef251bb8fd43f77d223dc03e24c9f81a45d4fc101ba6fe0e842edfa3f5e17eda296ea203cd9d6509ce038270fe8b94c96bd170e64a5114094b509367c2ad5bb742d4da2b862459dd84240eb070d0bdf4b5c54af5c5aed7cccc8266bfdf4ed93678ecbad5685d37fe7ec37df7fba16d0f1e7bab83edbafb1d3f06f3b4d16d2d1834db284743d9fabe63b50a830c19ec69db0c0cf85536dc3d824323c6a011232ab78fa43d88166796c350b6a36eab5b535ed72279f068648bc1a31159d78dc1a3117986eece23f8fb051f0e05dce6ff45f495b77ce5c6b8330867b9dbd9eeac0fc553b83a2250b85e711028fca6ff2f0e5b6d64b1c26c77574b6540854d946ab029254a511c4c65412e743169a000003e051c71c97e8ee0b5823d1ea280e2636242141d63446c89c002cc99041210a34043011a1a519293b624d29c151a9dba56a1e7ee28926cbbee3141b1763d1ad9229a47c2de5027fd634949fe712406652ae0fdf9c7901e6de02282c47ff96a95486ebb374fc72433c9c454dda7b3e6baefda75dfdd25f3387b4453dee3f70b0601f8fd58394e5cd9ccc2ca58be55a156558df6fb01f0fbfd7ec1e0ba51342d06d3a30e1e7b6ff5f6ddc78ad22722ac1a79ad6528e8a1f3a98dd59f4f3658d9f9ffaea7b007d0ebde0fdf2a5aab187dfead32937fabd0555f9f7f3e55b0f2cf38cc53e580aaeaa12a2cf9a76a4e9585fffc4bf528ff527df9fc4b75e65faaaffc63957f7b1351f1f9cf3f54543eff50cdfde71f2a0fa73cfde73af977ea78aa2cff4ef5f24fbfcf94987f5368fe9582d54b09cabf20ef2b25a45489ffcf3f52acfef3cf470a0b29bacfbf0958612ea22e4d80fc7fb3af3f549f0d7f3d6f07358bd448e20ad42c12d8e6a96916093c1a8774c6219d7148c7c3d13f6e8421f8c78cfef1a207feb1a24488fef121432c1bc76e3bce9ea171b7096db0feff869a0b75d6a4d22a1c9618c03334ebffaf994a31e000819b95e6ffb130a5c15b81f262eaff6f97535fc303121dc0e5ff2f2043a3e84899aef8ff973f8e71742859b855c1fff7e2b0910357c14fa200feffd6e812e46d4b95337ffeff5e3915028ce52aa3f3ff58875a0d5157505c79f5ffe5052d8f548511f327d2ff77910103eb4efad9b2f1ff38942b2e4968850115c0ff739539198c292e2a12c1ffdf1de10196cd254096feff4e12b23024248b114e2ff6fb990d8f47209ffbfd4ae4aecb823e508d3ec976b65d913c7e01ed5edbbedba0aa875c6dd76d3c7275df6df4c78d225a0685da6446801b8c8a2469e94eb684b4a2604c160e3c34d11ddb242931f4846210221525de20709ce109d104a840b1dd50332a448a2117940871a00312b12759c15ff87150e3605402654e4c68bbee20280ac5f08e0e48a85205a64a0d350224a0baf0b0e47864421c5906183096e86c519115bb27e33cd103411c91ba28add883126a00d031c646c3d80aae18396a30204a4b19ad14a69a54299517610b51059e98f4529506b294e8ea42c405426d839c48f5179c129314a4d1429ada4e2afa4c1052a00a0ca24bd94229117dcc447958d56912dc87063634fc59e009a751769eda70b5a85b936267ea092ba44b13a207354e83ad4b122c973888b27486541e97893c355376460567fd4055a6972212933b2a7688ac582517ea8c075890a6548243e97420ce9b510bb651d4f160cea6186f4b3020bbb0ba50c7c1d2937d05422481429b8b493df2230486a4d0810df022cec0435149847a96a702a444a28e4820fc41621880959c9fb083ab472c18104a3d62356c40803292f4f059c3b321a2acc8185b0d366f8ce688800312830bb49045e480a67886a90c12852172feb0aa7327079042ab70ac71d243a8a9eb921128c44c1250ed490508b10614ad2eb20c566aa951e21476e44f95072614fac4060f173eaf6cf5a8e0e86a4f13119fd2ffce068d433a394e5cd96ecf13aaefac7914dbc6b4ed622b7ecdaf119e3739e32fd8ff0f799f270f313867f55ed3ac86fbbfa8d48e46762d6fa36c4723bba66a54efb87fec40926d573554efb8b696f3b87f5c080b22246890a00602fd1dac898fe3ccddb93acc79ed9173d58703007abee10000d3c887c4acea1e40e28a70c8f0dfeaa600fe8d757b9afe13f9d64e35d56aff18d0e58250895305e1094d43b73f7299d0b8b823335d909d17969a0f875260b0467d2eaba4ac7290a21f5c004041713bc8a175d99c88dd0e34c8be6762f5324885b9466c4cb917383040e9a80b9d370323d52533ebee7d6801b0060d47ee940afee0adc5d0657106e8226ad575992091705be9c97b3220717098872eff874f58a9874d89bbe64d83de90176298c19e39aeb91b5aee9e376365161eecdb729386eb7745d4a522e596b05c61b9a8f02295bdca8497f268c713d082c67f61650136a0819327f1a5ee97a8a0711158e2761f7c7af47a30192adf2e00e05774522b39242bd7151d908fe04dc895f48b542af1b5b74f0538ca080750c154e620749597083d022bccb8c2bab4282d956a89fc8af57ee45e70f5f861c07540284f8f460a7c3465f2931125e907d0e0051f61dbf303a05529e9906df8b14275bf06c397e7c13ef090b250f08a9cf878577aee6e5e0f2e9cc1c8d8d330d5020f800478553d808411f843e7114911be9fa41f0c80241fb804588d32acc90e4c653575dfc010bbe4623970c79e48979621187e1366ea81a9b0e34a5034e52aba93c07d40c6e1521824e13a7891a4749281ce1dc000f43e4173e67ee852a39cd428c4b75dd6e04b01007f586150090098840b8407fb8d2eb4793a4845659c2e437f27afd67dd1e2149610c7012e069f44bd292d5db808253a75d7bc05e9699073e7faf179784b233476a16905d79919692e900bfee0189a6c5c30583afd1949155c4564454a203b5a7da71d81708e60d7fd800243c9068c9a3b810a5c396b10a72b4814963b8aacd295c26847598648bd17fcd0ab408f31778aab006e1f2e666e154ce74fe1616fdb008dca1c951c6127b1c67a1ce6fce05daa8e6b8a9994bff2834b2f960d06be440560c9072f79175532f2243a21af0d0ae77d222bc50719a2ba723a70fc1d2b0f0f01a4026e2552a9ca30b21cf89f55088b1df35c3fb5a06039450eee9318672fc744ab37b402ef91cd177c856ec42523a4bb5b262180ef44aaf22704a04a2616526c24cd778d121101572091ecad8204f6b26aa8291fb160f5275c59f0b6ee48708127d0301002aac79084ff1464d76d23888047134c7409e8e4a3446487c5d3694340f944bd72e3c46a802910937265254181935bec9a3252f7a6039d3008297337ce13980b09c80a9cc1499da7152ad815e6069252ca25d183e0881eac6182b28b531e2eb7d3222d2597a452f7c52c09be8cf0c3071153032f29d2f6acb64c70836622ae893a119411f8315f87fe53829001f60c64c88013f072e58751346119696a5d20238694051431f500446a545a60db8173bca9d193128a169e22a7d8a3e586081e7bf5a68bcc8d0a4eb2a50c9b203bf13b4c3d5006727376d57c79b76d13a52722460d135105e3da9df8cbb5aad12d5ba4a964591470ab24935c1e8310b80be4c9805dd44973cf2e3cbf86c2d7e563ea0ccfca02e12bf05af16e4e35d78f8c0d2f8bdfba67da60ae9e81cffd31c50b03192162dd42ab7074d9715a738d9736744d9064a044606e458fd2a31e0fe228e4014199f146ca22375559d7fb28d42a2b98d0e9c19e84ba59b4016fc2c19727928af05fe424c1a4405cbaaa36b8282f3868f10e301cb99b15a9674327c69b67cedc387e2c5d36473e5d13a7861ea9321ef79e69ea81c113c1079962a2d71ad1fc2b06887f0005ff9006b1e89df942f43d260c299f0c5a74590d8a2b01f030e4ee9060155ef416e172702079115dca7011edb33f9393e33f04dd8701f4fb05b0b8294b74d073e9408c613439245e820b5a65103015bc861619ff7892e5dec910de07bcf095e4e6002e0d312cb873da20c025b70450e9e8c1a81e8b4aae6e1451747a9a5855f01aa65c79252d7a770294fc702ae8f33a3c81952b4a0ff7139055b8919889fb07cec48f10c5e18f165c28918f543d041468b89a9ed10f00aa808f3545c2753068d0b73872f82fa5f0fca23602e04f93c6ca3d460af7813d5db0026f0f2e22075a7a4b505dc1a4c0886230015302b8c323065c76826efc71d200f88529f4de0d9a5376b990ea8618d400feda50e75a9a70f772c75b6f6b8215df8013850f41c448492a109d17b1c78835ec09e0cff894958958f3390d10a33c21bdb94012b12a258de9294144608bcb04a71ad8ca0f463d46c13df8010843f79c758693a9fcfc2459b56e2855834a3c616c7c29ea5e109e6c7c1c9ede22a34abd005450612f768a7e6dcd9bbbc09d077ed05d9352d32b51b748dbe709f5dfa76910c096e4ee5dd509634336e2fc0f0e5f77538948778d8489d2498337cc848159bdb7d5f553186cc1152cfaf28fd2deb84cc9798159cc4ad1ebf4b570ad2c6d99e2cec25dc4e5813b560186db00915397d328041e530e18f7c86dfd9aaf2765026243ae2a38107c9c142a77c78b12b80b2d298f82929be78252ebfe32e879b3b8b9ebc4969f7fd225eb7eb17508c3212b00abb94cb0fac4c61d60d6977760cf892baa4fa82e186711601818b8f40441f981e9d4cad10b63d79fc91f2f659c67f55a08e30e2954e533647050b662fcdccb03ce758483c8f55132b9ac861470c36694791186b4bc9808ca3c20f4820b05098d12ce24dfbf9ae5a4ec79c53fb400975b0382991b05c7e5925581c37da01498de9f4c63302a3868ba54e2aacb4acea4ae2e1458504a1812a637aa68ea2dcc00bb277684ba884218ba8450d17923615cdd431408ff920a16980e00b2bbfe908563fd0cdd0972662e05086a5c35bfc0254247cebf6ad1e90b4529f4161e482b17e823e673891158c30a47b8c957b204358005655957a1fce0e5c2add245a894f007474903ec4c39e3419c8bc77786dd58527587207183dda869e09a6ad4e372323ade4bc498bb51b0b7b047cae50327e78661a080120312542f812c82b2b896e1599c8183f30a88725fa04db097a82d77432a01ca1e1ed897f0e50e8f45b000239184eafec0c1858d4752e14a64a46e83b9b6976200b15ba88103ca1444f23092f30b533a63f120b8a99b674a971fe0ae15e6d3f374df1220e241a6a03ba555efb63925e5f1ba54f0292a387449f4319652bca87903eea294097ca68b45d2181ed207828f83e3c4dd4001a5f762d57655857000635942e4fee16470113d1ab8d004f001af2a4576331915bd1d4aa1b85071817a0a01c4c039ee20f09764112f274c486e1b1017600efcf122f418f6c2b5af7a26ed2287387a3b0c90ba5f798cdc208e380f658023770515bc8f75e473b5a8cd784d6761de501a19e589a1ad1fb5465f4f47e3e57308f7c4ecc171f55010c5e58747262cd6f5a577a80e808b80a0240c00c882e4fac113a607a5cc710778ebff070cee32d202e1b1aef286384db9954484ba629818cb4848c63cac4e596e5ada93cb65a8e441a0f8dd4a3fc1e799eaf084da5e7d1e03f05c204814c078077cfd95081628f91c695d5414f0f0035d2ab02d38922e57250a5788044cf7479c19170957965e0b32a6301de29e5bc1a02b8f4ac172edee00fa77f4170b8a72ed54c2f46f4e31d7c1830617469ba2fbaa89dabd2088a8a765e629b948aaf04d7cb4f882556c2f04af16c6e263c84d82ba7b55609b6e4fcfa88f106bcc3d03740a5f122016f62241c675638cdc4d54c44e6901a822b88211665d3854135ca9c5b187e7a6ccfd0a11e5459102f3b1b0a8c16374a0ba70abaa32ac0edbb729a0ecbb18d5b99c641929316d7d8b68d973310121f142205170333989746920b05e3b2ba47e48911b8f818bd9050a0978152d2ac0ab2d453d3731afae9c4c3f4a1e6379ee026a46701d3920d19309a572dd49a300a708a4f616e0a1eaa18c8af10f6078f14cea7cba0f1c02f280ce50958e2058b814d44af10c0c882e8e36b90bb61eba4f4acdc205d4cabba4e8ca3e0df0c5357ea972d12952efe44f960f14c8c39351a1e1c29901c01da48342a9258f838f63b3c615e6c7d3278274ab8424c70c3f1acbeb9db1487311c07387abcd928a3b04561c761188d48751a3863df4665d46a7225db208067c9d4e9eca38bacab33a026c80989b0be9800330a44b256e04a5427d2c514f6e47d747b4c9c3656527447963805af7518c429f45c600d884295825964488aea70630dc628a04f0981b427a660d44506a8802a20b491e1a7803253a5c1d4849b83c12609f2c85a50f404e100c070abd7637a0e0304bb3de0d160d3809092d17882a49e5111f78374d9c9e2f8456eaef6e0a8ca5549b0b81844ca5580af671f0ac1f87bf5d12635294b788aaa480c0f758001dc067b28af428bc0954ded832a34bceb0cd7de3a604830ef7ebc66d6a834520275c5a6702bf2691e0cab9cd288f8e68dd37636695741022dc4a6a426ed8af807b0292147c0192403f298fedfee921e60e5242a33c40d4884b83c78f321540c9a3994c49764195ab4646809bc9d11c0c288bab920d84113778e37323d02a855b35c95c96fe8072468a4fb80ebf812f44c982cbb4f579b501d6be800b6bffe0048abf6237e9e6892457aa5ad1765f2930e716313a7857776cca567bd4ae04022a5c459e82dc3d2832952ba85cf0237474782d2f6edc0ba2da9e93da73dba4e97205a5c8801f58882bf5b0b9c3b060c8e97d41eb7323bc8080c199480dd681a6829e871375572c6d124ec0450417139122463380d8634eb270306fce53e2a6b0114e18ee282ab88c72b6e25eb0e8061e42c6ccd5208a979bec60fa43454add015e20f05a22752a599c427b209858949c7c4feea34b08c341ab8445b14dba2b1302aea48fd21502d58772485a872d75d054e2e0e0840199daa3442145511790077a2f0a615e0d9e3e1756bb067f2823e0ff04ddbc00a9ac7c0f0e901e0d870f978bcd4559c00d1ab7558f15d70347af04592aae13194aae9e485a70223279f7824d66ffe0c7768d58d0a10cd1c351a673d41200b2b6bb250fd12de5a0c81562684eefc51ee04dc52ad4753384cfcd711c2ea094c58fc531f27f327d5d0660a0957986e03c900a2856037c70d546a4dd3c31879e8805899e858e50f704e4c348b137bf64824e0fc3568c6b86c7c3f5e201ebae88a3ef2a4c62ae58ab54780d71040b91e07509c043bca7a0902a2dc99a81296c407b852e8dc15e4e90e1065847f7c2ef462fce0c9f1bb60342297c94fc416aa03c06213b9e4c9f302ce7cfd70d53c57e240de8f1283151ea2031e819d884e9e26020eb2fa844e1deea47ee9bb5aed29404115c3c03e67ec6083c8fe5875719800b07f84284283d35176e61018615b87ea538bd041fecbaa8bc40bf4f933b3d330572e0267ce4f09058ebeff130eb4e80c778e54e755d41b6ecfc1d94247748fbea9a184002ec82c9aadf3196e14b85b1e02de94871cbb4fdf8b13b194a190f3cf0acdc08ba33d0387027a80b96432e7b4f802871f59fe10f1054759521e0e522d944a29c0181ca35953c54e6654975ed2a50520a7ae4e3ead801898bc28457bfa6a151c986c3d17de00c0e0c2804acfbc6c8a2afe064ee03c19175b159ebbef9c953ae3274e41b688487a3ac053f021566eea02444fe44a1382509139ade14a6b88a66e00b41285a986848081e51ca00ac0019a58ba48bf7d69ec0ba6217fa25e10710c693232cc9a234f0954c8961428ac0dc21128d29e4a17ab3557c30006e52fd020f707da249ecbd8324a0ecf160fd30fad065314680b7a3520166e074f47d38b0795053f8bd3c34957254310f645156e609c0d41730c54aa9281120dc21cada35374853c9e097777b9f406164115918d19a6fd9224ef80884aebb0025445c92dc7c5c486d03a51c418ebe6c0293520a5da7277b91076f38a4e96a2a97caa3178a37c7e0f8202a70dc3375c4fc952c628f0e693fcd1328af06e775a3f4d05faa8aac9b25ced64d33b43f824865ca062b1ae16402246e964277259aa5df4de1094d979a8c36984eccaa9e222862984821b4520ed9e4157840e5363035e93d81a9baa3defe3c9a3bb43e4426b23f6320c54bcef2dc4058e8cae290d07d2f84b00267ec7e824a14ee5103a71bc44801ee9046cca5f3d5a664a0cd836bc01559f8003989ee1850ae309706153c2c1c79e52c211c7c24cb533611950a67789556ae2a04a1f4e1a542cf1685027e058b54658a1452beced8a71b400f52ff658757e902cf9fc7a283d06590b76f5b16277f210388aba2811ae5021cca7fa024f234b8665d48442c614c94285c3a528e659e1f2a3f06d559ba95f071699c49c2354662cc9d65ba2a4191e592c21d59b7cd15dbc551034f29ebd6a5bbaa34c1808b47791542c25c3d6816bd0f1c68f7c8154aafc4cd85cf44c1996ba34b966b000b4997810bbc4246ad79280f2e28d9e889f00fdc124f43029f7ba41503d72d58e4df54e0c16d8a8c78500780ba17e4b175c18800f57dc230ba35ee6094673a19ff4f97a81b77420d972989eb8190e1068f49722a21b9baf06d1ace709105323d2aae219700302d7e4595e1c3d648f0a0024871159815eb7d6d6edc1349f87c026190603d3d0ef809d29ebca54ee513bac2e90f0881e50b4d6952ee58b4e47d0820e3aa3854045f48d1a524b38b71fdb009d1cbab4302df64bf8838bdc2712cbd3dad4972d753d98f1e849f930baa58e335831aafe4b8eace4a375d379d505c297c10b83b2651c16cd209778f5cd75d318accafc19a51da79c1af51602ae1caae3c02050470b36888731510c1e5afb805b99be82cba653ed0f81f3374f40e7072624f78d34541b6e39eb92abbe4a424cb22b4a694967ea4c0460e18d29bb2c7ce034180c8351285ecde49f41802204997cc202f4fc893950b80a20a8fe7c58c67134707168288e06bf091f5463e51f9ebb384d7acf1f38efe447a0a22c13da1b14c170cc38b9fa1c60877aa2b845db474ca34b61228d940fac2ab1841ba7d3ad4e15af30005ac0145a41e0554cc700900a00b4b08dd774185e5a61813f75c5ae4bd2656606e0c2e24ca5e93375f36880ce65280d597f1ba82618cb0e00a8a40018f4130e6ded984e2b2da30e97661c238a84ac57330335312e023e04dc870556e4a9502233275010b4b74c0700eb0e2b23201d3954440081c01a058a59e3aa9ba3499f952625125c0db4120e272858a7211bd707349d5a8509a521f7d284de6a5934bdd18680e7ca60965b7d62a067e0d02f4f9981d8061c858fb2e7a465d4fb110b89eca0829f93020eb0a00830526ab53e7b70514254725013ff0094ae9a488dc35f3a7496fd0b1eb463131e47af4c50feee10781de8ab8df359166fd544bfe167a70f770ec60f84db15c3d9986225fc6d6a3240449c6ad00509bb7a026ee7e029a70021be43c8c160c7eed0f86cbcacbad87652b824ff96c794a802a3d945764d80e0c3a97d4042b185203b93f934512574fb2a314e0015f29d688c15d832570e1a516e00b1e88954818f0b90e7499285bd559e066a1c4ef2f0cff01f817ae42418c27200a24acdf20b9708a7fbeecc28eef43c11126a426c89f51702c2930a275796845f8015d913bf6e50a0fbab37603bd91c16d807657c82594b5aeb4ba858624ea6529b3712a40af70562104b399077055f86000ce6400092e306b0fb73aa6f93b46df7d62c10fa6c00f99373385121e32cacdbf6da92b4f75b8f16d234b180b28b42bc2d4a0d26d01022e08143b9e2bcaf037e2ceaf2bf5e1c25120764fc47af531ec08b8724a2dfa353958f74b011cb09d0adf838187a9bce1a709d72d4a5b6e318f0b068504799fa0adc242444d789da3075792035c5d2651acfc5a231157569f553a62b55c2c799bae10327497031d86720c1afb7791d09b800a04ae28bbfc901a402f152250fed3a03017548c350c00b2c5c56f5a3c728da3f7b24ac707d5f8b9833014e15a20932b7cea8cb0eb4fa83fef0151282e474c4edc37202270a18261bc663a04ea257101e58dd070548615f1713f0511737b2037f745a5228fc7aeba653229f0a54ef340446c4c6b0bdd3db3409092911d2897c8a817252828e983c8d008aff100e67f183174bf44c0f46dc6847903cc202a87b9130335a4e0dc173202e14d38c660211e9daecd60821be2c8839e0d4268ee141f467a3ac234b9248a14703903252e8fa0995fdfe43ba19450a672f4e4b63d82fbed067d2a151ae10b43e0536112f7dd5183b76333856f7588f45376c095b5547d7a5d01cf937861e232424d943d74c4b907a87d75b5482bf14a7e5e7ab4d8e45c4e807c7491a125f779fc4479342e0495c60bc2b0151c90ded02145784eab54b70b10cf755362c8c59144cd4d64a7784fc1627f80bb3ecfca025d86b102ebc6f1d071063e5db789667325f5062f02040d3cc00ce177c949507639207231119001db11b380dd4278f5424c2a711b941e5cb7c088e02e4724e1415a8efc052450f5ae5440e7cdd8802bc78ea8df404a84eb1528cb0da30657a28afb74fdd059f9324ce6a3ba35e83a0901760340552cb90041ba9638b9b9029055794e4040612a65105d237506fd1620a82e2207d2e00a76d66e9e95126ec0e3d4b73550e45a1046e40a2958e8810112701d0db9d2e5866c004ff91240d98a1325bc66c78843913874e5041a754fb19a74c55278fd91411e6e090a77f8caa5e43f3a64e50d3d91f57ade2294a4a6f0476fdde1394b70dcb42048ca1860665d551022b80758aa745958b9e5004f40dc592d585d25211461096cc9f500a012ebba8a70a9d424fc955cee0071dd51a372694469d4d5667fba013031d1d3b261c8257bf1a774b1e4c2bd1834dd235081dbe702286c8b6eec52b8e0ee49944979b243141e968626df40ed0136a3c2c3ef1844eae2b135e9befa61abdc704e70792801e0cb183170cb8c6a74e5f67cc097d1921291162a259a97230ee2c1f48a0281b938d690fa58980e7149f061edb530f09533e00add4381c8dc32be0bff677d7319581974433119f158cee4ae0b58c192cd9fb84390fc286f026317161f190fa700a32b224386ffb440a6abc76c82bf2324cc1b29c35c3f98085d40915add320b64384bac0b4a4f5c6a975de280cb6b4a90ab26538912898e096f00199e9e2c49b34a2b8f24953724907ab448a76c512cb91c78d02a33a48a707d78a2832d0811e68b201a71510028fd8b59b2ee201879fe851450659e1e3a0f4bcca5b21f9af0458434506668eb741958c440a904887e025c4529f560895272d599bb748e29feb1d117003c9f5e0238b04a04d84a95249474e2eab26bdcaf84b930a5a5ba638268950c6efd3d4335e841dcd273f3605db83f2d30ded78dd789234d0f818b33dc87cb8c5bc74d90e74ac3f2315aa5ba5d0e6df8140584c07b648cf95a3bd3138df8790bf88c95112060c235de7c2807d0d1e909785b817f08e0e0ba8280ace787805397d8a51103b1149c2e0663d99be18980bfc204c65d640bcd4b0203069f0dd8dcb64e6c1792051cbe85059bfe80268cae1db552e58e0366dcbba4106c27ca9e6bb636e30d01d12eabc52c5db145fa01e478dc4e6a8267126204f604c2f712e4d95c4b36059fd5991f3d496f43f00fad7b1d8054ba280858d31332aa011ee18ad285f340d0ff6173545e2800c723e151e2cf090c9ed7395e0d448cb70bd38c9b34027c166e29f0200851aea60707fcb14d883fa724703f39e2726975f1801f4302af299301bf838183ef6c50940f3e65c085147d292148f36021b52c05ac0ba834f3a6c6a19a640cd10c00000010006310000030402c1c8f4805e3019d9f0314000067c260a65ea00ac42ce79432c68011000100000100000800a700f8f44f749763af405bdd0fc5ba92eeeddb0514c1e8fc3df9b244eca122279d212954d994468cbc52aba95e133c64e7ed85f4323181d2b280e7c56d500abea0ccb090bd187d4e6d477c4a91554f5811233081909e6049417446045e2f8e198b8070d1e19999c78cef186dc84af3b9e97ddb5c1f76dfdf4d83f7aad04935b2e48bc525bef35bb0a9504333ece602fd66f0eb0582a43eb9d48ed9d000b9d76a9842c4ece1519f06ec1b5c6499927fcb10b92b381ee27430651d505a1956ccbcb404b15576b8d06a4cde37c670785ff59d1043908d55877a6b5da82b53c9d67d63b0ca585108df5dc0e97b707bfd3c38691715396dd5736b03eddc10c82df34e5c8fc9195cb248cfdd78cc40c9d8ab076880dad5b6e93ad904588e52f99f29e4813c48ea7cd2f1257214bd3424c854b080b20276013d4f83204e147ad60f965a8f4c049ef50b77ca8d41b0cbe8570792c35828f6cf6a2ecc03b6fba965460972648109d451c8c8fbee8d5fb2a0a5fe1add0b1d66b4dc95540506bbff4de448494c7523f6a152d7762f5e6756b0211a065fdd13de07d897e8b7b26db2f4f93d5e4e162bb777acd13be27df9211f4fa7f7e92d78bd9b52afd805ac8a8a4ac73ac31717d24ad92096e67b8c7a3eeaf4f36b58854f529303cee02f1393a9ff2245a1ccf981358db7f4470ff9939a8d383a773877ba6bc71152fb033b117fbf59a51beca5a08c2529a875832e9d8a83d02dc5784f9c56e68d36549bfb45168636969e6f6ffca9eeab880cde1809baa07b667e7e5c8f6513a2345227370bfbbe259fb8cfbb1b05ba6d717881b7701c6603e79a8c85d1dfac5abe23c501ac2e522779b50ab724e96bba46601343098bf72105c07ae913327eb0206cfe8a272cd43b877415799d734bb52d1f2139d39ba827fe34e8fd637381ce32bc05ec5b262ede020bf6c9e386e988c301acbf84e04fdea8a9d218cf6cb6fdeec069404568dbd99b087819e0fcdee6c1f5f23a0428b9418648a405cefb7bee6e7cb6718c129429757d791fcf05ec6e124feaef69331d08508228a038cdcad8944e9eb6f185d6f677aa0db0abb2cde3af6e44fde603b98342862d6efad88f00ad82f5ad5d4bd9c132b5fe12398e31a0417d4df922c65800e1a089c4dec6e8f25e59763e0c8e5e393b51b2e2a0a8710ab4c122380341baf6cca66f706f42934bf3b4eef8b435df904887dd4c3b4ff58ebad3475922455bbe0a1e8780cd1a401a48bc6079f0411174d800d00a85bcace1df02318b65b84149c4e193f6a6c01ba7a3170345ee6c9655a5045cc4cf18b8a29eacaca68898dc844053c51d36c5f92e4137bb570eb0a03b4673f07b07af6022a435efc69e4ee377d0ab49dc37bf567f6935b6e2ce7f930859a5751427ef938cb86b00e4df96f218a0fc7dfb668d76f27bfc55c7a94763ca8f946bc2801a152bf9cfe3be95747ba3ab487dc5cdb46669fa9ddd99d22cc7bfa1e1a52eabafc30755e047a75d1f5b989eac6320186c279a4e618b97685f7d09bd0032f07ffae836855c92449f4d4b622f25e30393b133eae186cc4d665a8d033b1d3915070bbe57736ed1d7c9b7e9b8e2e4564ee228f368b273c7967e75f1f002b16e64db13d5c3176e823e50afce306eec1516373417d8553461e4e42ee0819faf93592bb2c073e659e8bd82e2d30dafd755d1616a0f59ffe06c2a72b3683d8d1aa3cddbfc96f54e7135411df08f46205350d270b2e6387ddc5ebfd99426d691b5c009696dc209ced1e6cfa2795de6564e5bdcdfe6074b785304edcd7ba197a3a2e82fb903fba8e943b8eb23e2991bc769e637f52f7fc5c03b63e715176b660e5bb2a9d7bb4f94b23adf167c2c73cae51bffa8e340b9399e77cb18ee2ab2fa5a145cee3c5e7b7850f6f99c4a7e8a339629a87e0d30996ed11ed7ac5c7d81be8555ab14b1cd7b414789761436f49b27e1498d31b3127a004433da30742003f8986f9b0c54d7a1d54ecd8ec0449a8a685569893b14d7c5830c20478e6b3171b1b675722f09ea4cc420234daea67d36a3bec6a34be6f9b92bd6901eda3b0afad977df666bcf0250cf1873aca6b7ff697307adebda7f27df1b722af821ee9bc8deffec6a8e0ef2c25a5b58340fc59dbe01345ec54f56da5cd2c433812077e9bc438a71c18f48abbca3dd9c7c4a8f547be9981b8177fac38d87e7357e4b95d4d1e4dacac812020e47c454f61a5a6700aca670c6aba64e3b489b6abd3bcce08dbd1e918d4aafab7d0a1cc8d460ec44182f2305805772b586e35ed9864b304f348b38c15745238c1501aa88c2d16be3dad4863a83a6939706bff6ec6a734f9802899dbd0a982a3175f07b878f38bfbb406c3bd6e69ef0081b8ea1b61a0f08ecdd2fefa4b620b2c8a25dced290a612a8cdd0c5a171073272e2bd6ee98cb12d7e8fe9148cb465e09eb14bd8701b74d90cee6399ee562479fc5e6664f8aa613070b4e1a7555ddd4c46785b85c069e6960cb2c202f3e495e4057d49b5ea8a9295da4841d79ee765442302718126e417824ff32c06b82c91d29a75a95868023b6125d09a56cd6cd7838295f3ac76b6b10c5ab930088f9694fff220f4f0a4361b92c560add9b16b7e438054d71b74cde4d5b0de37c4bc6557430daeece45dafe09cd3c7f0b7a1c37583cd39c39f210806b78a9c29b7112eecd8e1b59d414e4ffd9cf0a3e85e788c7b5ff2256d3ebda0dfb21d8af80f62fa566de3e39e00c8f0b7b1c60499457710a05881cfdd61ea0422fd6b80d20bafbd925eba7f323e18986a5405e1d7048ba9a86411ef75ed5e74d773f9c2f15a88c0597db25b1730f566be49f6919de82eda4f89e3fb7994f6ac94fe0c8efd9cf99d85c3288dc16f3675410cd08e9512d6a1ee1ee9731256bbde9b4566aa5149d647df6eef5e1eef2679fafb90692ac6215ef629f2fbaee2481ba00b3e8552265f380965f92ddef9fef4ef2d3b77a8ffb73cc0b9675c3d77a9652f1e8e7bddd755b01532b9dab14c9fc2ff49c37e07d25a9bf3adee26fcb6f2c68bb51561e630ab8b37837f637a3e2689870b8e977eda59a93d8ad5749cf1469fc7c7d20bcf735fede7b8b759d9f48915907ec31406a690878c63c27700c9b8f50d049f5d9fce4f6bd4eea8299ef665c65538c4fe3aeb93293e1e8ce26f452d2c6d30631fe1b28bbb1846b95de42946141d44734c8235fc6c17868b350ef343f05243a5f700e56068b85ed2cfcb07d83d46ee00ee25febfc613fe9fdc6e42d65b350d6b018e4128cd7b9163fd33df2259db38d066491b04cc7cac7dc3b77414327eab17a24b1ea2ae6b1cc3fa511a2a6800a6ce694f6d521e4ae13b5d2f850f4cb9017c8fc73796c72710aed409fbdf9cd2dbd8a0312531ef40b9899a10ac1e950d19839e6a566ac7e69d639e9229a131782c1bdf7dbbcead0e3d1b799765ede6658a6a8c88a82a7014f0e9bc5e535bdb07caafdbab4dc3f18866cfe80e0589874a39fb0f62793946b60857fadab34b201327b6e4faf88d7e84f27a5bc4c6c63f70b4f6683e15f5c1a2dd9fee465a606d833ce7577da91970ac811b97b0bdb92b15c68d7425e0857e84e344c27f0bb9ec3b031b3ddab9d1e58665d2402085866ac39e6674df73bf21c18de0da3f8700f230e1cb0df2bb6fb6504fd2ed981d3225d372d75dcc54110a00da6cd66db86fafd326d4d743c7b9ddacd4b516d6a0f21e5b3302ba80fb83366bc7e7154e10db1705c211a5958411755af089b1be189d0ed9e94e70b200e3ded256f924c609471d2166819025882568c718c58f9bba18f82288e2de2e96f5ea1e2755c6ed4821278173816b3426909273a02e9d5f9df285124fcf99799472c78c8ef5493c918de5ebd00758ef107ffbce34c94c8f084cf879685d9f0919ab0131f00e38f8fbbd19d54b6c9f45c1a80fcab86c0c401f3bcf8f5a57e0167dc3f5e575cf543130d5f526957d1f7424d60fcd3d871ce4f6f7f5b5bd58c669d698db8dba4a9d5dda3be5a12cba8f6f2858950fbc91b84479149d2f20331047eecc3ce07a347efb75e53b8bccf495e588d1fd4304d419ddc384d1344d31f71986aba9aeb019457bb59d99fde26cafb7e72779292da9303a33d8833447530cfbe1e96fb76ad3e55687a0911d62d9b8435c74869857138f920952238c4e0718e4a93208ffebbd90dac46a4f6d7b795dd11ba9c4c9b2ef2ccd803107b49efc5f701cabcdf0b2b7db631311a780d3b38b151c4a4d079c160c99b03a30a9afe97f3613d976eb419055979129a7f19afada077676aa3e8f0c7765c15781e0fb608eeebfecf41c32aae93d538a100cfcc6057d42c3fb23ef63d2a6b71c3328f51a1a103bc07198e9cc4ece678de4e8001f192422cbcad68b459aadaa9c7bf778f7bbfdf56c96cd2aa038e9c6c4bac2b1ce1d3e5f87f36072ed0f40fdd3d8697967c3bbeb6788b15ed638e020308b40df04cfbac05d24f6d03f78c0bad91c99f1f47039fd3f11dc76f2d9951a40c9c0926a7b9f311082be1de42e2df1c91235a4698a6e42e4c668ca4caffa9a1a3b1b9f63d634c823541d1dff14c289e7503df70ec75e4e3efd109a6aff2f78c78d8b185864b96d2538f1e6f7e8f40c4dbb97bc3941fabf48abe15eadce671aa44696b88710f55f41dbd69c0583db875bce0b385f53ff768dc01d6fe1a048f86116285776b1cb05620dd8acac31ad93fce7e55417862fc4e6497e7bae9e52f75cb9afe5f52a6bf7e7810e0dca5f7e36ad56fe953a06aa6ec024f01ec919b767fda7e332ed7d45dbc7d6d8e84efe827ebb0119176c0a686e17093435114eb6e0ebbc112db2202127ae830f5101589cd4d323781b75fb75014cd870d9e7e14a6da975af085b3ff9f4a471c1b2f842a21daca19175cc98e761c9ccd41e5d12b4abe0f8fc98343125b1fd2d6e106800add5cfbf05cffbca6f8bff30c330912dc568e8473acf4489f9e687abb8146f69dcfae331770a50055ccd334e77ae197d3b3936dadb8fa6c0647c428f9d075ef8b829b5a3dfb8490c3b4f4c429efe4180b3817283a439a9704368f1879823d9855030cd225bf23b5b2664a807442259cb492b0d0b3928b774c7369a6bea92687f9b133b6daadbd04425835eb5fdbb77dc2ae9103ea797a8974d634f40e766638d5acfa1013c491ef84202375b0a494115582c00ee8c9f560e01722e8d3b9d2f1a665e636ea0f4113e50ff8ba64b54554e557e23c01c211d321af93816c06693bed1f09ec72358c9cced90aa0cdf8e344fa519db916d85feb0eb2e8b0f6fffc33fd78ee3d60a56dc1fc1f8289fa4941ce4d3f65016cf8539994dafcd31a042c74e6e473d62f024be09987d2798379ed16ebbd2522c8a8e7bade5b9875663285f9dcdd9df590a48800777be2df8de1a0d4457deaf6839c2adc24e887aa829c73b59790baf57516b1e98f5bfed69e569a91530e7fda0e41c307d3a19ac18f26b5c93277b3419900aca76eef6c5162e52e6435c66c7b32c1d067a41c8b86ff8daab34b5d7982a46db43b5b4a68fdde760b26635b6cc2f156b322a155d104208ee22e076e7763ea02c2ca190b6ea0282c0cf9381cb2c8bb28ac7fb5c611c267dd52e17a6962366baee02a8660c3658a24463290f8bbf4c2839e9592dafc4ba849ce17cd8ad2c0d742303d9a2056350d3322d0f00d5e0ffc2d36acba7b5461d16b0b9d3234b4b226cb7dbec9bd700b87c93b7290dce40ea37e91b466d9637aedb0ef6d7585b31416c2329b636cc249c11b3566149efe3385148b96cdd40686caaec702e4f975e9e6ae1f542aef86c1200e415ec234956661e1b6460027a4a0ccf7cac3f808f49a0f1284115dba82e3009fcd1b2725c4ab00ef787be47ca8484b068d181a9f1d24ffb884c963191d3c866c7b8a178f3c6ffa07b8291aafdebc50060a3a35d6f793294721d0a84e0de3b09b191c0431ceb9b6760086f8f6a9a6862a184d13352e8585a018daa5d5f6c10777d66d80fd5cce982fbc9275b724ef3918363b4ed8ad8912f5c1accf8b2bfe4b7d2f9985dda70706317fd27988d493e126f4575a69418645684a9a117f04fdf5ad6581dfd15dfb534388948e75e307d80324f10f9188c488c0a930f83166c615d5329359990e0bb9df991175ff6845379cce92a369168d02d279102ae0fc6e10a6b01cea2d9964105302742a090c21741f0bc073e023ac1299ae6ebb80feb9b6e1bc106f0ff13b6fd20e24f048cdfba1a83aa8fdd4d3f87718807bc9eacb604a74464213c6b32d86bd964b23d1e1db25c0dba01f2880f08adc0e8efa286225ddb7b8e30875bb4c78e8557fc5784f44e284cefd15df06b22e8aeaae671b56a31d6c6ebcdbe3903e015611eb3fae1d6980863f9e301a0fc0a8cd12f4817437b26a674dd5f70ea9979676bdbb4676bd84cc4210b9d1e9a5385f961fcf679bedf0fceb14e86fc59f98c8fb878ea1c3b319a09c30abbd511c23a31e39478e82944a46c032aab83502ae3fa19ac96d78b5820a4f9a54c519fdd5751ea0a93609f9f1a99cbaedb7e897ff5d4021557ec2b858b4ef24f7f02742439822068eaaa2ab233b93d5de9ea80b7f73999cb4602d097acea3447a4923e9c54e0838a80d04fbb7c023f504b30e6bd21e8a40cdfc472900235c8349cfcfbdcd86dbaf144c3336fcda7999f714d422f1c6d8e12df2d9cb76d07e5e068c4d59b37ea7879270c0408ec28d7a469ad52b785887798126fb018d6789b3cdd7018216a539201ef6a59791db84e229bdc2d8d4f4aa43586263c51b6d76fe141d845378e854c6c6adc94ebe0dd90383de4b4debfbf90ca98701f41b1ccdcc24944383c3724054adf664c30b5aeb5a831e5c9c867919113e518274270dbeb0be694ea0e867cec69ceae2fb7749f1e6f6d5faf6aa089fd4d295910f4f2fd005492769571536d4355133a81a778e890d3e026a01d164d9cb962e37c97e938bcacc2b8fc32ed4eff98a3f32a2ec9e0381859b1d9eef75435efa6aa7375c04db51549ddc17776e86f7cb789d30dd2dfde642eab63e7a11bdcb459d2d7d3a9cee37be23519a1fe307883f77b1ec20ef000ebea6891349e536c4f2c68779c9cf6ef8e7f0e86af2cfd439aaef8975f952158229102a353341b620ea1592552519c4b541631c9b7f2ef34c83b2ac239daa275f0a57f8af77179c07c0143f4f442b8b9596ab5c281a4667756f78ed4c964f5d7efc497a894d1307f3a739fab39d6f07ca33dc712f103c57a3825be1ae699dbf1406e291b3bcc5f52db5ada52927c03881f2682c1f1a5b687e9cfcde48453290a5958c3ab9de9e2247afc1e9d0aa5763133916e33f6935034473fc7c8696273298c25b80b42c3ae8a9ec812f86ba924f66dbeebe65ae0d348043700d01381ef4279eadb5dfc932d65470ba6c85cbaf65b8f04595eed9dced4250f82addf32675fb28d3ffb09462007cb59a8b4e34776c8a36c130fc09008bb9069543298ddb30c6ad0ba4403fb2b0cd6804f3ef6b1d01337de0774b48094212ae036a0471a240a1f4b508d0661b95181f262a17af975f6e8685981eaf7e2399332f5bb67c37d5ec09157f2ba146e68109bf933b4372280292be0dcf2c6aad6f16b996c6baae6ebd5fe70774658ab369d08005e0f4baa50ff5bb9b86adc3a5185abe973ee0e2d979796892834200b863bcde77be266f434c402d289a644005627cfed06b2a1b30336147eaec15e458f89c4fd9dd008d4c3bd729f9f2bc06c164db85075e3c1a65d218a88cde50a7508a07638e73f5351d250b1c416a615a862e4fa56d7e4d1257fdc377d65cabf7d9fbedfb4ddeec5af3ecfbcf1da743640f648f5fd916a6e605d2fceeb43908d3616ac107473e43b442b70f4a5da0a8a821fd2402a5ae5187f01a16e3cbec14a82c52f348698b56ec1ef9f261a2ccfbd7ed75b9128ad72181093a160aa0df510eccec7c483b1d9908790f29f15406f9d6ba34da3d67a7e0de2b278c12d89ffffafe41ba7c7b3207d65ada3c00341868558e72d39395a145d5a7c364096a173ef2aa8672088c62474bc396892290cdba9665167bebcdd971e6a05564af8a84ad77d0a51b9928798ebc7fb3914356abe8aa2b49acef8f06443ad9d5d503c0266d9ebb02e18c289aa3e991ff10e8c53c6c1b7e797f9b5e383dac898af001a1188209082c4c30486d44415cca29b8fb0384c7a8928f71dca9cc585d9317f546b537ab626b5758ca5eae67eee46d189131197bcdd82a5f98d50197ea3402c0e48983d903538839f36c49ea7651e5bac31a623948b9b831a5d27db18c884ab7366a92efb23a15ea626b233735067b81d0cd0e73ec1601ee46523a27066519a70a38d6a59a4198680c83b176fd38696b30ceb8b0d2e4667d4440f0a960570aadfaacd1c58650f0a4e743e716ba2a0f3c2e58171a1059ed5e3f18bcfc1547e3c86fd5eb71fb241b14fb97b8bd7102323f8d57ec52edef74bb2328cff10ec67c83e01c633fcf24b54c1f07ab7cbe6e31013ddf51a0316f53b131a935ee005e2d3f37e72ef45cd11c8c50cc94a118584b6d3b0246480b3cf8438a9bfd98f169b572fcc8116217e5c4b48d5713eb4288738d516b1bc137231dad232ad5159eaa01290586b5f53588cc58bda8ae6748e96f7dba5ed591bf5bcd7ab32a8b72da2b209888be1f66626198e48d93d987f4590e525133fec22ca36d116802c44bb4caa3deb203543ea134825c881438b0099517aa67fd108d00d661feb018a02ca66d94639b999220d8ee5e085a66dc161fccc9e2cecea472ec324768cdcd4a38a47ad4141a1ea3ac6ea031af42ac84eb915eb9af1fcc5f15a19b1638d9390989669ea69b336d5aa01d88492453b0d681cdeac08b722ea8cc17d4135cb6ecb13a2dbb34f584290ad5ccb2bf7349bd6c8c723c26348a3085ffd0a6abce6f9b20d39c05c1ca45201feb636127a62337bdda540ed5bf90d1543a70ae39b8cd4f2225443fd020f4f44c2dac0e0c90d1ff27bef90118ab21c19df9ce6b904cdb9c9b187d084849bc5d09489416a2d709ac83ca9f590a762089e198d48ce9b304c0f9973cfcd4ca571e02856903ac93d160ad04ebcfd96a3ee28f00042743c18939a531dddef4b5527e4a07c56cc6bf6cb3e39ac189724d817afc820a519d3450099623d3d66acd81a7d7d9d056ae18cc8a6095f0977bdf8d289235440321a1e81abc6e5e8b4534413a40addf060d5ccde470db94271a76a16f3e788614e0c05f947bac408119bd47e2f4ec6d42df44b71a00be50985315b17c34e4f049f3eceb808770a1354ac12e636996caf1ade8c94bb8bdfded7fd1db2c3cef9f77a5698900049f9b871593f148fdbbb71aa958317b211ce829ac9006bbd3e4186ddd04d9d962f17958976effda58e0318af7bab56119f97e16640e6a9a93166e72b001396bd6f0c97f3a9f6c5ef5043f72f44cf5165bf0cc93338f39ffce69bb7537a83b5892a3091b41e6ef091f1d3b936926146d9eea79c6b23bc85696f7491701c9341e57b137602b6355927dd73b22a2ed1f7bcebeaf88ffb15e51cda3552a1f85fb02deeb55573cb3280004e53a00a72de4ec7ad3eaf69a8156523d925483ca856300d8d86d8a758a04215b07e9fbf01788f08dde7251427cac1e084c05245294110ba79a35d35367fc74278f60603acf165d7963a642c96e0ba1f180afbbda5b556bb12b4cac23cbd0e5a222b7a2e32879c5378f558a001a23e2771e8448a86b661227aceb67aaf7c8c52a32e7543404514d7e12fc439cfc1d78574a129a9290ff53bde8c9521ac1bf2ba321efa6da882374797db5c9287628a90b70f3644292ea41a06e01d1dbf909afa7cb406a79ece2aedb3c20406bd0b3c613099395f29083209ae4b26842f6a0de2c98b091d0636e74f32a25c8fcacbfb796df5fdb18263c915ca208c43ce37670bdb8e23cf2b12756b57faa2504e9d5d2088cef971c9f9216f6f3232ab7642b01cfee358ba64246fff00706dabb2b04b45be1e6e5fdb75d2752f593a3a6fd975d26774c657389a81c06204b2c4ad3c13028bc9baa5bf85c8a720827e4fe66caf8e0c29ba0898f22313571fffea5c6297e0b2704c15e82a597e2395077ce2ec4fbfd129fcadf06c5bd423e9efbbaf73f2708fc46961f7c0a948ee24e2926af4151b41ae2cb418f8a3200efa21dd8e28b0c86063bcf162daff6ee4b12c556de56878b2d6a7e455c50a85587511e4b93d3e999255927f9c1443121258399ff822def75b99050fbe71d0f3b1aa54e47bb64eb5f9216fee66ad082dae42abdb97cf1dd1486ef49729fd897f067e633c4eca5554b2046b6c8bd08779daa77e913e4aa6327647c8ab8b7218847825e91b4605dc99f6d2ee0c9f93b29889038ccd0236d3de8cbfb41520cdd85abbd7d6f1361430e0e1b4b06b3ecef3501401754f18b04067d858bf9d6d36430ee61505cf5bc369fb9e4031b306891540f558e934385531a7d6038df5b026c2d8c2988ea8a3f1ed770aa0408efa0f5845b6fe68bde5f13fedd86e20aed9f76fa59ccb7b89d2bbc5322c1e009fdad4651fae4aed53f344caf8304a8b1f1760872b9cf340a22669ef7dae359d04a844e7026996c808101578a3e8d57d0dcfaedc384d418957a7e8bbb83e0f5f179be4b373b908a6a5f9113e418e1f443687fd057a318d9498d381337aaf3f1d1e859a2188892b69309a20070ede216254315a7d4938224b3a822fea4dcd4c0e1314df43f27fc1459e18a600e475cb52e214022e30fe90627a327c90ff903f697eeeb0e5753c2a63d13816d83ce57e8e118fa803337d2120ee4f1072975efaa493f94d72275ca7caea24930a6c4dbe951d3f89d9c8052141a71a83b5213d460829b3c8d1730420b51cea3d4f7d477f6f37ed50cc33cffabb70f2692a7688a27474a18373b5a8d750c8e529953725fd8fd8d40a578ed99a2691cab9f6c389a0f337b423f1ccde067705db3aa636e11807b601afc5430a2a599a58574fe9594410503b54cb73dcc2e30f5e7154d3669f99d633e1c941bafa10f7fc4776f1c9cdacb31caf1611e6ee2657452f8d3e142e5fef41d19297757f540af741c0e4016481fc2ac740d983617cab1a1dfe858e5e387ca2be60478b12a21ef0c91f67e6e3b151313caca2ba1457062b9f9f22c4a7da6b911eec273da00d8ba2d443b110b446b9ecadeee087d595ac65baa3b5bed644536ea347c451490ca71f77c8a2ff6e0a5d1635c8cbe17fa3eb844a9ea270242c580251e33b98680bcb8a0f1fbe3f3f2d6dd028fa50d9f882721634d7c9d35d06c018d40f6ea403101ce935609ccd9ba53e12bba09a323cd1bbaff0b35f07608c8cbc8aaadd7375157d31765545884b2679ea0b44e33cde486043f6557369ae3e6d0f343a9a42188497ca787facf176fdbb361b36b3905ac6d8a339598036cd4a458c2ecef4f8d2424f52b8745093b1dec5c8dc68a0f2bbcb66516735567f4aee515f2243846e46e545b6d5186c71f544d1ab8ec9d47f2b87f6a57d72ddb9309a272ed05fdb7fb43e929fdf29ced441cd7d595b2d33f11df07b88f2bf2d17cae83bce025829647ec1b0aa91862b7f41eaf1bed2c8649a91a3039ea2f4a5a4815eb946905547998695ad9a6f38a349330859a59ae79147025b102ade04dc07dce47f518e2e49caac4159decf9a1952a9a51db4107a701138b60c610a620790b20657e030060bd9679effc5df6b20735f057eb6f64b72137679ccc18cfa166a31a5e39849547b1a2a7aad876bbf99c82cd19ac0c254f1046a942519998fa836344140d5833fcdff15a99b6602143d05a35ce444755c847f183d991ca8ba3fc63d48c92c3459593e8b26a13065b83ce685b53c4352e4107f1e487dabf548249434c00e71b5a33b0a81c1e41d0f09f09f1a2624bde2863c7a3f7cc70c917f3b96da07a01d3fcbfcda7f656b80b59abdc3ca0111b58714c755253b3b43fa2b320cbb681999245f42b86f1762830f1a7f830bf3ee5a07a23dabc663aec968462bbca93d36d123d18d5ae8e79a5955a931eeafd9678cf2040e054dc0519a5ca56b16623b2895e01f2c45ec2e000e254031d193c5cff84a2461f2afcebad71635fdcaa13e42951b4b66d42e12f75aa24382be5826e589d8b47e111c762850495612efe49b47d4d62fec5ba04bb80ff55286a5018a7805e80f69cbd97450b51c1ae8acf6122f0524a8bfaf40e62aa5ee671280518b36092f9f230c1311067eb1e69a9c0f2461fdfad3c2e65796f95c4b60b4e4001180ebdbc4abcde958a055f777097c12af029b031f0c1e722d6becb4a0d025f4c4afd5ccb98ee20c9c0d5f0f2613e4fd1244289a037bb5fdab53958199b8f24c1db064c503b2ce193b7ae5e37d83538bc21c01c10708db133d677f2b62a5d0b152df13e5ab8bb64d10052efb5fc2c59eb844f50aad45d732d67ce8a0451d798670f31d7f8c63639e5e878cb066cdfeffc43dadb3c1ecb437b044cd69509edfdd855a822fcfe517bba6fc15e8790a419ede33873062e24154de84fd7db63fd521f394a5e7b706b16970281c45c2f505ea62e5635ef6c7436c3670d28e814ff4ed36772dfb52622f931b5771160828f752afbfff475b9b7049ef1262183c448dc027c5332b186e31ee4736499842b56187df18e8986f49f0a104b2179842cfcff6fb9aed74066686c2ac0f224a9ef8b30f41c5effc9177a4199771965531e15adbef7f076a3179a7cfa41e06c36579f1267fd924b410b6850373377496ea9e0d7bcb1cb3113096183be2933e7ab20b3d7fb2ab7fb679e33d413af992735489703c6946a47300acdc22687e460ef8601593d8851b972dc279142604493c649fd184b7ff24c056bd7bb0b72ec937610e892511db69f781eeb7651309bb5c380fca05852c4effc0302250c596bfac80f19805242388f0fd4c80379ae3f01e63d23c5066db108472b547fa734c3d2ecca5fb600ca760d3c0d02fdcc670d2b0b719e8c9b087fc56058ae7aa091fa3366ac666aa11dda6e10a992d07fc88deeafaca928e2078939c852c37e1a148bc3852b1f232f5d80439fa10b90a5fee52a3fea1e2e9b41180db8a6400f9ec32389d2f73f77e8640d99d6cfc5806d0f6a26bad7483528e6612ccbc6447b7829f91cd19d18e01b2cdf9bed303b95e5c11241fdc678e70b884f84d46de4f72ee1d8ff35e67f60024da7cbe13224fd9fcb9827d3a0c8b79f92335b677517cfaf7375a8a8705ba8d01495de830273387be248eb93005431a8c42308d9dc31ced755c5df150b8c570d9fa33f357088306c925734e740399b526c3b692e20288f4775892e56d25959cc58f623c4885c9dd275b557fa769c6065f426411a6a456726cd9d24090c0b617b27c601ae32a57c9ae0e6eba863b1f386692331bbe232b46d0557d49b117049dac12b044726d225263a10d72811aef3aa6bcf86333321d65467f498cffc059104499a35f6d8135f0eb2caac984576f2ce1669fed41f02d542f5aea48cc144e6f711bd1d12fe6e2334059696e4ff6c502e0b667789cf1be72e73aaa4af5cd9fa0430f1d3c321007c5304cbfe64130ff26a7feb75ef84410ebe90ee7d04c728ec8188b505e72658744149bfe4d9d2c2169301953ff06e608d73f1f93969562231b0b743d89e5c12cdbfe0389b442e3c5414dbc4f510033b82207937df805c2573a1cf0b3feed36926bf98fe37c3049f25db8781242af61715f03d7970a1530b3516b1d95236906fc82fd742ea161e3563a04996f056473e5870e87247f6011a337c8291726ec7a568531bf76e8f614e012b2675e0a0feaeffef3cbd040fe32f29117b21a9337f5ec495e2ec82d49842b2113c1ac4dc1e47c5f13f5e4de7615216333b990b231cabfcc2240733206a8199b04fac1543ed8d9b58527da435b93ce45cdea1f6fe763bb3dc0705ea8e70e788b3f7d7bde892bf8e93252a2b3f54700cc31d25e0763d509997fe89f1f5023da580ce58d86acaf58ea8632743b88c76f82e687b9244040b776284d499f209c29726614635dd37ba61add8fd264d0eaa8ee0b7d4ff1bb46826918e191c6bdab1545a56800b2355ebb3dda3219f1f642184018befb4f082330bf221c10696068087c10a8c5472aa1db1f1f1ce14d2912cc31a9987b02c116a411ea37047af78cfcebd49d2187e4d07c0f421e649abdd5d682892674924482f177bb5b5023162228ebd8a53a222d816ee5cc4a1f961569e5c06ea02d685cd01a7fc90e0d60a8d26a4d98044875b78b39189e7cedf28b560928a268cebdf440aeca841c1edbd23b34f81c4c7d6878fb78041183aacd3a6bb97283af513e5cec19f71f947b2c61f064e46fb660851946db1996320e69b0a5a9c7267dc19889e3d19ebb61265ac5f930c36e57fa0ae57ca1efaf1704b238fffa8ed6508c6b571796d1a4cd22ab01b277420e1c43bee40a7b7fa04e6c3dd10bb2b455621d6ee50c19fa6d2e7be65a17ef8cdf4fcf07f6343c354441e4466961dbdb1595a56d02b22b93a1e2b9e4cc9f44c1c7720f260f3c5979e49fca1bb6dbbc541589609081085bf5a30b9e76d5807d2b55fad48feabc4c693251109c868c4be251aab5f1ba727b6e6b2819d8161e7236096046300e3c1146bcb153733d3be0401daaaa1ec01973d6811db748b8ea36e95714b413295fb147b658873a5b71356e5ac9b152adee127fad4f989f8411631cfad92425c9a0ee49c7dae23fdf8bc2d55be9df6d2cecd3c8d2722a0cb392ac504b8ebe4531a60ac7f6f3730a6488267399db172f5a8c8179c425d0a0819fa01aef2607620787535fc9a91bcd1e0c8b1668432b918d580f9dc906f5dd5974a0b8603a811a49de3fad8d0353be5c3abaf64477ba2f02e08bae5621483e6746625a81798c60f466c852d9a17cb2b1b44ed58760782f02c180bc77502ec480f9358a95291f0e63a6a239055cac2a762ba0db06152d2238e197d7cd5b468da4f950d8f530438d22f8e052bebcaf8421b654fff2634449593edced35faa7c44def3f99d20a35f0eacbd479e4bcdf256b02c694ab5816b1088f85fb554b9f1d85b74e9416c9535ba8e28d166412c77a55838bccfd9650e46d73807a813645d75242763dfdfabd8d9fb543a1d5905812e1d2a9a75d2a3ad8743c773baab009777e1afaaa970b0b101a9c36f6c8aede02f6219d98caec7788076c438f00a87e7f809d39449f96f66475d1c21282b5c983f6a8763e4b8356ae0eec27c551d71d15d6774768d07d0c1179b74e0bdccf4feb3bae2d19fcce886c4306c2c7d3bd0fd1ef2e1b48e2453d6591e55121704b6ca220172163148fc7ffed5193df6103dc12e3932b2254f4fd28d66937c5561dd2829f7dd182be743527c6d8489a6caabe24d4cda21e93ea4afdeb3a4de1176d1437b12d870adeeee01fa67426b991d6bdf22d028359555c07fa9f449fb5ab37c5f028cc82286f140aca7858327d545c7d6111c35b218e8c53be5a922edc97b0dc023654733dfeeef8cdf57cd6c1220a5c633e58d6b28430912f9f98f41632d926987b2146580bd647141e4533c5e8cfb05cfb0c06f92ef8f2634524ffbd18da8c87a5db6eaff322e3937060b22d182e5204cd5026193d949a725e167dbd4e1836d1fc685f775e7ee13118649de45e6cecdef147e9877d9f40f1e7271646c33dbb7929b00b90125e54a05c74d96d0bffe5d1d7100f760966102b0c8b71fde1521c7f58e8c5476c334f20489afb8f66f0ea06813acf1bbbe3d43ec636eef46d64774be1148558a08a7eb5c526a2c1c872529a38ea6dba38c4a0dcc259ee566c8c45691650d72b0c19fd8e625f8d40839111fa605add53d56300b4a1facdb092593cfce6aa54ee43b4ced388ff88652e1a558c0c62b3d9eebfa20c3a67ea5db7b49dd860a7f891d2f3c32988cd537641ba7e021f48967684301e60be7751d49f8233d9d5dae543d0c50953c6876be9e1b5e7887cf4e9d8fba914cd6d5ffc3712724c4e2332e808c9c5d6c24d4d322657c284fec0ade482c934304c7a32bed65637f76bae0b940cdd8c02389f319a9048331b5d9a7cad1c91f67daae88b0f149a21905a301f6f334d2efb2719518c3baa138f4ba0e813714c39826cfc8cf8946398b6a1f771c1a75f659a121d330b861de5b1a843ac91ee7c511d4567808f7dca1efa5b9cfc4b6cef57bf0805111ab18ef89229c450ad5f9c3e82cacbc217fed18d64c51f211c64f3ceb534180155bc612accfe49999f3b9674133ca0688808f3814eb94bd227ca8d1e2bd2c8905e641a8f9a99faefe1c237018bcb8146fd414c0b7c77d3b3d2343419c58f7136fa9404a73f419042222e6806e97d17906ae80b5d54113bdc9cfabc2b0c33df58f393c06ee869f0cec4fa021fede753f09cde3ee2cb60c97d53f03944caa1efad2eab73f88394de4227445aa90b81d8a9d109c69444cc655c9371ac8f529d6cc2ea6e723c5991207c879e14b04cf5fc391211be1499e8adb7e05ee89ad43d2874b73194bca875a0b58392cca2225e90dc9e0a8244f9ae690d631d2cdaf4648ab0417869bf43675d5aa03bf1b63a81392978fd6c343bf604335ba6d854831ce84db9474cc70bc7baa770f2e6a591b8c7246b07340af04f8a7bcaf8eb0c1138102a446c563519814a8b15098f54bf0ad43867dc55ac9a5e0f61f5dd12b00cdf38b2429e59c8695927cc09f2409c701c843d4958465a04b52fb0d526d6853aaaa84ac7653a1a6300cc267c74854b881039cb7e132517777f8cfd5f19bdcc627f9521f6ef5593edfe429179aeb2ca5da375568643c77fea504fc07e471fc2c132d9809ff9c6c7a0d3331527739e72eee73046d767a4e2d9bc9a711c096abd769a58efa770d63176fee95fa45dc0c3fa88a649a573d68d4c06c986db44cf1ed6bf8bc87bc4f4eee6d343adefe0ef29295f72e60ae171a4c80c2d60c0a6daee5d85b0f32703452faed9326e62879e68cd331bba87092e28882e3c49baabc2a1826cb543e91bdffc2bcbe70d0c1566995ca865807c3c099342fecfcee3613bc84e22b9bb5ea88475d01b151989a26d1f451dbd70ca4fb774c3c20b2e6b91455716664a170149948bbd46d2477102d7235fe5b0e467221e9af3748088a53ec23e09f7556658efc8c5298989ec8b933e9682de24202446b879f687c2195af290e6b8967ad5cacacab3dd58bb5a5110bd591fd84d372752e2609444afb1db33ef3293845f7ba44782cfd4673416c6dcfa257c4d0810724fe0607b32f2061ab9b2c8bfffc4e986de31cd7623bc55740d40f84b82f421a89dd8f2ab9eb86e12bf46e882ecda12e62bb671edd1075e9658a35a8fb735e703790ac6090fcb21fc3eddd4ebd1dc563bfaeeb00f2df0b18a20accce13be378b3eab7a5b16ed570fd91e58c8f936886fa5975c9e1ebfe13e5678037d99eca18ccc69108e35276749677493d5a98788b1ddbcc4b77b2faa136fa7c6b81a85b77a4f32b4929cf632b50eda61099e26d791b226ff126c77ee7c832829509f0c7903148ec8cd51034d2141a87629e73d0f8b0e5c95d1d9771ac8213771660d5227a37f8475503beb47ca4c8cf8fd87315f7579d5e16e773166c2a7fb91af7219fddda9536a8b538a8b4adf06b3edbe23d6e01a7ce92460100e32d62d9491acf11b59258b9c1335a943147463bbae2c0b7244b17398b9ae8cab6b0c2c5fb84fca2572ad1cd9c004ccbfbc2543d8e0e42913126feff5c3bcba1765ce6dc400ad601284d5a0fb36d6309dd385188449954248c67e82570242af2959a8796e31f1e34566c9114012fdf9e0a12e429eaa2c5d085b683deee43af3458b85fe8b5f0a0697c84338c2d21646a5d41bc24147e1fa319845297e9a41e0860d7d61c20d5d240f5582f53b1fea723c0aa620b05900cf92fd3f26d5d20b41c8636359ca8e34164fea4d5cc1b647bf18569d73e12cb61764d3b042c31126546ea67243749b8f19a87042cb4a8b2ca78d2a13833067f6ce1e00e241eb16c366f36f97db0dbb0f5c33306d1fce7f91e2e0d3a2e232cda65f8186668bf32aa3dbc4987883fd0f0e7a45d06436ed559929ea8ca56041e9946f2282d2f3462fb59138c735a166a6f9b5e43fc1457d9a704dc9921d0debba2dc9e108d186340c03b4916b44078d8c8de3d718cb4add8f3f1306b69c72e770a8092d40b303232d9c51ebd27262c72a2feb0d41278ad4537777c9564f4c8d318b4a67732c23f056602ca11c5d6fa6af5325d229f651d65c4dd4b380957dc64ad71d22ebb96a4d5cca1084b34e15f5793f5517bebcd86d606eef0f03a6859da79554b9c504457412c8ae1c5b1cf1c28622221123feba0b75a7df81e8f1305b0c0f81342fd333059627174c21ab1bfeca65a6a8e7c9dd9e0af4bcd72f7a20a1f81eedf5f2ba8343e18dd50e8158664a3663a1ad9d7cb184316058e5d429fc03fd72ecb1d0407e2ffc1df3d2bbc00222ff7f288ee6998bd637dad43aff173479f5c670cbc12d5ef224c2d9e9ad6a9c0aba2a9602999c1dbf1208c878b464387db2c710b0ae51173340d8e86bbbe4bf8becef5bb89566ea316bf2a9a494edb4f7178ee3b1d95ff67e94e25ff5467b9612bdc5572d59e7d9a34be7ff5a6ae9508a02f62dbff681a7790d96fa1e3ba9afc9fbe5c616374b6279f1b1345aa51d6038b8a37f6cb56421f11981e152810600524ef3bf5e2ac6a0324f05809fac0174472309abda0aaa8171203ef9d0f90a971e0b98d23165e19d6f170519ebdb082fb129082c2fec6e0c27f4753a38097a13060e7748c5a5053444994b0870697b073e12e33e195503cd9933fc5c2785b49aba1178d6b1448ba1a9a2d9ea9c8c75e5d1bed171e5f809a1ff800e64fe4b2aa5de205b71597aea0508c6f3f527f543562cbe09e56276cfec2b22a6c5c90a73e1afd808140fcce9e5df7fcbded756c74a5b9039be4c53ecaed5f9d9118db00c97c90b3d848f3085547c55ad15561e056b8c51fdf70731f3725e32ca9b5d97ce6e69ac7424d749a78064f7bb154f83a01353f27c8e788dd6ccddf42fef5dd765a505942fd9a14cfdfc4ce81067450f0e076fdbbaa8b32c2ffa1ded323bb15e702f714abaf2e00bb3d2752a867153bbb0c2df8a21e6a29e4e8943b2e604674ea432b0593415a1935643108271e47f5d846832b57c647bd5560836c48eeac29c876295914941f67889441c7a6372247c49d4fd6671f7b66b25b300ceb21207d45359e672cf161aa3a4995724d0fa080d8a482e0c5735905e19c19b010da6b8df07ec11b0f2df2ab60debaf8bc8b6cb02bda1ac8aab154486f34f8b532c0041b2a97a60ef8e11112d0771525cb5c406afe48757a522eac89ec01bdc3122991fb1e5b22bf77f6f7938844f6c080b38d5475a9d97214ad9e267e1571acf0fc535ca8b6d5b9eb4ad5fb1a652129c957be7efc982c458dce5122dd75d846c52e2e454a11692621f9dbca76b4e6c715ae5d24db355477dee9e16a6d218afa96a002646d337c8187e9bcabb4de45b5fe26acadc925e00a3491f93c2cb5a52848e9114941edb7cf51fbbec6dd8174f12248bf38f0dcbe2964b6e0b8c7afaa06dd0bf7641202292deee043540ed573a9386e232bb6793e5268cfa127c0f1610c94614afd8d7b885776bcb1f8666b070e6b8a6b8cd8502521f8aae6adcb34c0cdb1cea86fe0dddc71610a4263063423e42d88b2388b88bd896479eb7ab267e2ef51df51229caa0a45b9cee64d5c632e1d6c078d52b756c54fa55504dac88e460671e68d541f295d7d162c4430b18ed24228b9373eb930251659269a805bf2824b50f655951ecfd84c30015634b048e8e1c57fef7333db9c2c2e82ee0dba9e23b0e9425836387b1ebba20adcd529185bbf9b81537d82bd0b073597944d5967b4e5cd6f0134775289964dc613493b038c044dbf49528722620647922a4bb3e49f3ae7a9cc23c5c37e271c91ecfdb94cbcb5ff22a242649ef39883d64b3cd8021d1606a5d08a3e1525a06595022882be0d1714e7dcc6e7035c8a98aa4b068a10f24463716d6281b7dd9fc5ffe158aa576f418ac6ae397d0663a1a5d7cfcb43d456089507d6ead177ab87d025370beb6f2944dfb01d98842e550db095eca97284e869fad34258011d3650d8f016b3e721b078f9d66ac4fb5b6b3bf765c13a58ff0ae9dbcf9dcd7b3e07ea3f9eea719c3e6efede95773687a33e1b4570735e6019117bf9793581a7855bfd13621fd900563539249bec806c8800b7491e2d2ada6061eb7f16bc6dd3f3492f84f33ca9b034a7623b738b64e673f6a13b26447f6b20b0fbe7b187083e6892b5251ed6f045d9cc0d74f09268fe7cc3e60016369000ad0952b27ac2068ae6a5d68046fbd9c61a9e22d5201fc51244837dd860b3a7433080d231d1c03653f23c33ef3e11eeb3fa2a86a5921a50f153ec296cb1991a1c33c487dcb82cbe50f1994d8ccbb031e85fa4929ad32c983ab43c8b808aa5dd8748e71530676b43f2f462edcf53e4e12e65a8de6467c59ef079fdc48d0cb26cbf8fc66c31c44b1dad04cd8d606ac435aae91de1ac53bc30201367c2fc048ebc02b147ca543c264eb0b675731c086b45a502ba310c996720e2709556f8b4192473447cddb62bf05bbe52f929016f5587aa073da6bc94b4aedcb0145483fbdff4fdf746b0fafa5a276deb2ba62c98fbee57b3ba15039e9f9a396c63c51f8401811246dbc7f3a4893f3e247526b3d154c1f2ce5fa13fa932b26f11f17b4740ff3de5fdc83ef44d42c153867cac049e153e82bed837c2c42da00dd0a16628878c5f0269656f1837607576c5d4f7d7f869aebf00258c5e13e41c0120ac3fc39ee0513380938ae60b2f0a1caacd788734d32bc4a55bbeb0603152b2603b491643596a277099815188b044021b1d26373e4e3d07ee06577c33b5752e988111f25c8101383c8ed70d3e0f7261ffb5587fda971779e643718ea55e3a0802556a3955a58fe8f065ec9540d0beab7997f3380dc440b6d95878a85b992101f47eca1b7a0b9e40c2e9b5fbd25345d663ff0dd6c1c1f99b96c995720dc7c121bf35d385f92accb033f9646d68dfb56c16b32fd53965a800e20c4cd52b0173fb2470de266228db2be0b2b30cfc41f68201201df442e0e732b8c0826e5ef5fcf43cde1c9adcc2e98915d968743f0cfc8a4f858769ea5bcefddf82a42306af5ea4ab5beaface6a845164566dd8051eb16ba75aa1fc5232cd6f1356c7b2bc94be4f6c66d7b9aa8aa19d991160b07f4d38284253375927884214f9fc4d2c99fddf5e7f08992c3a9119b88b095ffc9d5b19dc3409be83857b5e896ff273eb1bcc72d4bec03a9c27c2326f5698110c4d23977175eb6a8e81331627623276e2481affe5d9078e4c6dda5b6c115352ba235ef69816db9f074c18329baf19e48a7848f696d3aaf6d16d65cd90e28e75ccf0342e8b9cf1c9b0f502726cdec37b1ca8747ca3bb986a1b3547f7665a54a70d7c863bfe2294a7574f991f52e2c1a72ab86e557c683e7a9c2090d20d34f383bf2fbef8118d262a471898aed25c5f1006e7a780e0573e32184781d4e65285a020db68d849e5e693411dab810ac3629800466d117602c22d812aecda9195201cbf622eb14ef6d561b0d1d72305567a3c91bf5ec0da22b047d0abc97f255e45ae4d5650280a33dc9dc10bc37536b399f7de57c0fd316dd0e8a6e65b5bf151159fbf77adce8e89982d129af041f493afe6d0c114d5341b60f8134fc91a09f41a0781180f26b0d6a1c890f51919b0787fb677d6f325c8182b3f618bf9148c9fda19e0a3424a519beeab13b56d5d45a502abff177ae44d44f0a591e85fd2d6d1476ee8954b52d5d76b4c79d364fd0e148ac4aa10ebef90f738e0f17b1b92ee42ca00f1ef746291b69a31f8097679c183722be3725a6e2f5adc0e4e790380300c5122e6cfaadba289103a1a3044ce256156b4f12f80ebc31629c16b4cd6f6b232ee69b6e5d5f8c06fe912a17417d7166cf4bb7d802e2154ac86d20c40c29418c67813b4239d42897f0e03a70b402c7091d045fe9b90dd81204615c55fc65943f5d98594a8e688b537cf35049bb3fd3384e10ad37bcd9c0cac9321c7bd40dd0577a13c3ac86b3d74e5449f04af3c30fb961e60a2180889df60d44044e0f589304de994643c6e060990624e7e4001a27193bfb3a2e387791a541db29d3b608619d559650ebe81fb06c3511b6efc6d6ed7423686cb06037fc6cbd31bace6df742cc75c400cd69aa3c356d9e275ae733f763cfe58e1f48ed2eb3e6f6cba1b834234436e0fa804010be77916d84a1efde19bb8a368f97b056c360263098ef84905179e5b181eb47d43167f9d82848cf9e87ff9c2dba8bb3b6437eaa1f9314e8600b90328f41c8bda49d993121b784f12c3ca6198f1b0e8db59911e3ca474da10304e27de62b580c2160e7eaa002837816761e0f185e21e930484e7c6a2bb77152765884cd8f5b1b2e20732053101cba11f4a38c1c69e177951018be289c477e66898612783f873e27efb90658cb1ec1b21aa095075d3015fcafc10beac1492e23c1a532519ce786cf1fa04f7ec31c4b3e74166cb0517ff4d6049278c9900fd7afb367d78643eb02f57e0e3b8d3184ab13f1aa3664d508dc8f531620c737c1480fd5e88f3b68fbb7c0944583f59ce1dff8b016b93a0b6927330f78dde54846f154cc1c0c75f277fc4d6d050ab5db19f5465cc0ad8b94db138e456459111b404641d1e21968dec144e27ff78a3d46169dc908ee6dfd6ca5312d0b7d566c08418778e9ff107fb002e7d14d69e08da923f0924a1e22c23f7450471d09875ecff34dd8b0dd505c9ece73bedf101cd7f78dfc7f1313c02173fa266667051d54a87c57de3406fc9830bc0709062785baa8438bc494e7ee3da4e274798690319b3b11d3467f44757d9398a5315fe6d060bcf1c2318d6ffa95cbbc2fb725af2ba8bb3c6bcfc2e1c707a83139ecc7b33cf8979d43bb73139999570b413cfeedc99301f1bc7c2c64ccd18a376d2085f369cb04a189361312d1a93d40d3d8064bf56e2574e4c4553c831f8dc4c87dbcb647612a4c31d0bf7904c10494afa58cf9d94b3727510995b5de34f6c352f1cef9b7327cc5c77e19c51dba8692d53072809663180248d04011b7f516e10c6c389a871a37806565af079026c2c37996ead013df93fded96804b52f3e482dd9344247ab855d3c5f749af0454aff800813d70e115c1aea85ea8cb8547c1f7e65f8772b8de3169e96d2b210eb184e41ae3a41ec8129f6b01a58373e825045ff7e402d472a29f306aafe50eb18406ed1d8a45af9bb2bdf5985fe27ccedf5d08b260632f582ab8570d97fb94ce182819a7a7c515411f9017cd9eb69b7ab4da0149e9d2218bf19f8b1425f472384f6e4e14fa530fb9fa1f33da19a38e2d43eab245b05b83d6c1ae725fd4e3ec364081b152b67fdb77494908e8a9b4c5296b48c6598a2ad70fb13d64446b7710170f4ef0396a53251ea3b4025fc5edc9395af00ee903390019fb35138eaeaea9c0a1a8487d42018f7702d0fc285de0e6a8b35bae98e6f22bffb5d3603a1afad51cc5dd2f0cd82a1b2c80520901d620af1896f4d0e105ea4f6e32e842f01a5310cc6a89d91fb6cc9775dc3d403da101d02201ce43c6cf658ea9480c1995d6a56e13f339c83372e2c3b260bace76812888afb2c55f1f9d286447ab769215ee3a208edd11addc16ae56361673f81f5231d447591e65537e56b8d3c0135219435db439e9db2bad2d83d9da7fce431b478cb40c860c7e098417d7a38c3ff5a9087aeeb4c873013730b7463f4ceb15a1fab9896f10fbb7fc8ac1bed851a8f8cdea4d6f779c18d88605b925945dfb63887af8597262994828b23082e49f8578c07abf175d4f8ce4cab6c18defb1af91cac11eed41fee13efe2664fd561feda1dd26839167e91fa3b7e20be5c7ae07a27b35f86010814bdd95e42c40e1f0a56ea4896d6444d497c85e621cb79927d1a6e943245856eb0923706c1649b640006655d4c947f728ed8748b8ce903df8cff4b7a19dfd29d124785811e77715b10262112c52f2b000d51bdc62fefb12b390b4b2ad48f7a43306fb40f1a8e46b54268e255a1d7635837432c5639f895ea004f78207c5a5f24186530705fd4431d5134465d0482b070589aa1547a9809cff8ccaf2e2662c4bdf9558547cbb81d1bd01a27942b4a495505aae14f1d751c320dd79265185997824a436d3f17818050b6042c5671074db91463ded90536d661f4582163383aeeea66e3408806c39d98e61b68190f3eb3fb757abf515d860dfa14034e692fd4897ede16cf695b069fef462b0d311ca4a5e2f985a9c4a69d5dd556317c09ab051963d9d3351aea408afbd6d66d9b48ae621b131b4635ddd6cb692105f207f4cd88cff110797486fec1de7b11baca04b90984ebd47c13f8190c61de1c249571189828cb907fede328f62cebf2e5a12cbc56be81a405d3672135f3f36abf1582cdd72a4fad252bbc097d71e3d487b66163570529cb211b5885b457634445a67b8f49af9643d62f2ce81c1c452b2f88c44e716d80242f872e659d025dce89bead3a2a65930418a5672f5530f60fe291833522a6879932507e76fea139d272d54b9747c19f7daae92f489ad7ca4365c0681fe02d4bd2d59d96c1fe22297e11f3482bf5f2ea6849f4b65fdad1158469a43096633474f515d36b62146701f0317e53c1052cdeb9401dc469cb8f80d1f8fc47107b25e65b5208c9833d1000bd0bbae87bd06915f086bfb31e387df5c212946550d2c1b6873fa5cce6941c15ce452bfa24a837c14e4444d9f057bd418ba6a49f26fb3824583779e018aa80345baa95e26015ee089d2b250ac3f439596a3c2258447a632bdc6f5f16d9cd3bb9886400c5ce3f0c277f0e1489ed8d92776a3b0e96b79388ac145b52b82eca9daebe2d84bf5a4a91ce7beccebde18dc229c468c69499495c018b32c7dd5c9363d9e7cabfaa0145318399e0597b3b48cd4a2740630e1535d58e94861589830528be681570457a4047922228ab427b9dd4c1b33511c61edb65cbe0c27480c102e6145f965b5f720d3200012e691a86678aecbdc7eab39768937e4c7305cd378800736a96e134a4b2711e3c5ade640521c83b598317cbc1d58914d4da80b8b6a12506f43d2fb914fc096af5a8db60c5f7a229f9977bee3c332bbbd9973a73628d7bf3772248f8a252e66226eb39b513872cf6cd6259c2bab22e2bed17223c0c1fb920a710b58d9bdc52294d7f5895604320cc49d7c5caa8df979b2befb9faec1493f2b023f6c74d82d8ed95f80c66ae6dd3748effaf197a46033d1236ff82c3c0425134bfabe62d157fa1b3164c5be6afd18e67f9dfa153fb40cb2553c76a64bce76d906ea40e0953dd46c30d7bca3a89387fe17c3b3b7fc2563e0a1228896472bd3a5d86cfc5361d2cb0e1b61170fbe4ccd2c88c387fc1f7002383cc3ac9d37dfb7296670965211139b5fccd3dcb2e9394806fd5a931cf64a3a6007579d890dd8bacd498b0e4fe29ad484baa0aaf373b132b2c20764a89c976296a4aa0624b624fccb971b07887750a77507831ce0260c20830d8195a4d155b8551c5d6aa03276d4945ab552dc47733a3b5fc96cf2cfbffb824d3b73bfb842ce0c990c88efdb22eb52c8ecc4fbc0fe8f607316440ac5ce72d8117ac246745e7406c12fd4826578d6f7b43b067af4d69441dddfa05b3c92e61fcfa2e89501811fb081be1ebf27d0e983ce9f45982b39f4e93cec2e7820c778f38810422bcaa976a0c5f896f627765db84b1d5fe83a30484fbaffd8337194d65b166020fd5f807a65f26c979c2dcb07aa30ee9250f16b65fb404e6889be3523d5a209ac98a4f3e7379fa30da10c4582a2959d4010b5f02daae213197d7b034f2e8bc1a99913717e3581700367a0a95472cd1f3baedef4fece20eb3e4a75739a6423dbc0715c8383f09d34e16881d38251908b9abb63ed1674476f431c2883564251f4d5fe02c0f1a24beb81710113944a14975ecdcb4ba53ccfc8baf9061c1516f876ee991f911f37b0532d7835ae780ff1499984ebb96fd533ae210019181e40c75570f1b43851a894206622b2b26f84bf06d7112242f234e396942c60eaa79295032077940eeda896cc66cbb9ce054441a5ee2c4da122e21edafbee0c9d35dfb6f92a61a45a2fa196c57978dc6644b62fd0b5f5722f6b777a586644c8c8e253830c314272705a498e138bdd090ee3d4bb328d71c08ef72b5fe341d029cff3a2257717163d9ffd37d4643ba22e9192cc2cf9986bf26c8037f175b11511daacdf913fbd9150b1e7b9cd2ed3d6680a9a2bdee2bf91f080f201f5e984b68056c5804d7261663bb8030c2883e7ff3b90b88159f61690883a0605fc41b724db8779c4cf657b8d7bcb784a35dcb0d551ab12fa9fbebe7e32f65cd0885a4f42c477e7b308951d0c11af81aec91cd5ab08bbb8c4d0175ca82efeb0b09d686d8af03ff7a34207cdaf7cfb1b3779fab2fd3e4c81c879ff50a2452228b850a29c6f8e7fd899cd680f16239d38621844320347bf4bde73e8e3c77430e198ad5cf6b0886294cff0c8ea7e977c5cc5052428a207301ed31c83c6227179be293e15b2454cf32531965729b4b5d4277b73e6f7a7978905f4993ecaf0bf680d0b21fe38bb6c8c3f560fae4d1bc992c29da8afc88f1bff16d07723ef044a7b0870f967c9f02208ab01f6de6c168e6b43610c21dc1b511c99611873559bd74a4fb4be4d78c9f02d1a941586cf210b601c56aefe52c1843deb82d85e23930b773a8ee944c00d9229bff074801aa5fa996daa8cfef1895228c7c1513f91852cbd608130ac3c2543e1759f654b49edca8e8de05b38de4a670c5c5a66876509f9af79eb9db43b75099e92de0c1358f73ebfedc65b8273377cf816d328d15e61771a281b5c75e1c22a5c1bd253d14cdf648049e17ffea5f58e3811ca610da19d22065fdfe011ff8cee945b97110a025fbb8b5103c450648e8ca9350e619ff9b5c4eb0e582594dea6cfa6861379842ae98d113fdba5be12b21ac8272c5e4bbbbd4bc304b3d2fec1540a5112aa39650dd7b61e3e1b0aa322f48af9c03ba4720ac208eb3f3fb688a2a564b00cced782178f9d5a1b05bf9ef664741cd89c74ccad8902c2225fecba185ad58ce1391900be415a823047b74594385493c2d9cd81fe117c2fbff7a8cbeaa4e86d2c2e5936f6f931bacf794c150f7034e7332ce6ea6d661c4cf4147c35e465578cfc88906dc487e0fb3506ed5e32e11855a8e6cc0aaeb85f8fb466c584e0e37a7e364b79e25261a73097a16c128a2fd3407095b1d8c5ab1109137f387048d8aed1da3a4e9c0c140524d517fce4e00c7d60cca0d268228c991eb5632210d8c0c1d6cda17b99e48e3c35eaa67d70bb70643bbbbc301d93f2bc0ad4a210c301f000a8cf811e7dd05e649d95e9bc05fbf316794dddfcf57e0d7414b4c29b371b746d57dc963eafa670c3c63b3236db1970a44fe4f382ca74a71bc1e239ab77d1ab23af88522e3900fe7e213dfc6f4b64bfb3a0bdef3bd4d343ca78ceb960ec9201bba6527382ed6c0b6b9f9505aaf07a3e3aca2a8dc24063a5569f16942f06c043c76c6b3648195100e3988cbeb2118d2ac2bb59e4138ffd0551716d52bdde88470722f4a754c7a278fabf788a9a7bf9669a9af5bd88e9806875a79e25b0802556fde314041f608d62ec839b8ebaeb912b45f44705b6184aff6589a96e19ef61483af2dcd04872747f87db2f91f804bd592340e13b3366f8a774db9de13917e45504af37628baa0040445435bcd40286ad35dd90e313984254828791e9df2619f60abc1fb6a4ede2ff004c27f8a024de2dec8460c513a18ab074846c56d828eb649cea80b448fea3c21e726a4e2b72d055875c5de23fd21bd7b21ecd0bcb0ae46b1d147bb105c07055148617f9aa778ea777ac17d1d6ffe6c33c3af407bccc10df89cd5c0cfede7bff6c1b0002e816ac2148b0147f49d12702eb531405198fe20c516ac3b733bdcee186863ee8e19d445398da8c1a9d0902b83c5832800a40f38ea96555c7cbea5b10eaf1cdf72838e422476712f392864e6251b1e085c0e37db29e997bcf1f31a61efbd30eba20a18cc80235fc5e2add935b3ef4cf9c1af3d2966357c032e5082e0077a908147338f058fe1b2b63a027c113eb2e9f5f21542f65c50132df614e73a02356950c216d7c9efb2726d4c077acba1576c1bbfb1222404a5eaaa722a57d864f40ad791e229c20c192455cdd13f0d49cbe3bbccf705176c62cb4df2b1a59bdeb987453075cbeab1fa769ae43787a6b704358b926f7ba15cef2bd73e6e6165ff6e249e78aee102b06742c6a636554b929d7331d696250ed78af1be428db542d78524549a34c95a4137c06a42d5ecca3f86e49a0e4915d82c21de718217a5b6c0169d7a32b485fa4f94ac5b737be0e4af6501beafa47f94b9905d66e44bf85dac638e430f05431569a96960c44736ffadfcd39f46a1dc9115786985d7f5a10d961492e4e36e601f52e5a5473c0b69a7d25b5e428e4cefe57aa181705ef30a5bc2279647cc9fa9b73cbcc7f979b3c6846f4f2b45ca0f4da305c5415ba87716ea25ea88c30b7cb16b5701b07c9c529b322ae258690cb8fe49d22a50d42368848858fe52697cc0d84597b5aa43941c94645e9333813b47818e0dd2b860d319e2502d6611ec5f30a24d175774058f710a8638cc0ab32d2ed75871270448ac6944b4c80247520c37854f447c62138efc4011b8147d4ce3cc81402cb00f6fc7edcf988dd19c2d6c5ff89eab21217e55fa685b501d5029cfc3f507d7ed9b1c04542a654899f0ca42210a7d0bc91673bf07f4a8d6b2e5a69fa7e43bb2503b60ef3ca3bb5062186935329adab26dab340812c8c0406af3e95573b43d72b6ab5cc0fae8081b2501080dec033e80beeffd068bce5c869de07974fe8ce4bf8154d48cb46281e7baccbd0a9bcad4e6549e2903d155060a70fea2bb73347c043cf14ff409fd31f541ef1eac95307184d5f8d39e020d5c58b216998b81ac950ebfdf177bc94b0885621f48283de3433400754101106d60c7ebcdab3b8af492bdd65d7183bbfe0e5f8fb965edb9b4b19ddfc99476b7bbbe1cecf52422cd749b5301417cac3e790089a1271e8c31fc434b92c505959900738651fa784bd9f9966f1f3dd9828c805c366021c54b3aace7142d3ce4e4fb6e73c0eb27d5cc4664c3f11b59675e9ed176afd906b4fd8d446a414cacd52cd20ee9c0a1d101ca9cd8dbd98060cf1d334ab447222e0187119da45a60ee6bfe2bb075635b2072b757f912a2ce98a84487e54723f8b7b9166257623648b0ce61949af4c9f95a9059a25d9e6775e68194e1c0f80f1a4bc0c99700eaebbf9e0be34d087cc2cdef2b6ca79b95bbe14e6ac4872a6d157d2c336c86e1fc3eca016a8d9d88fcabdb65679deab64a10d3a9443830a773e505b07463b21134d5ba99f6da930818fb0d2ba53972c494835f2074f6fd4766c1fd25c90221bad0af44d52bb52e2de2879130cb80451407578296f25db287b706a90913e87c0548b4f621c71359057c6dd4b9394009145e1c065886a80619783b5f609e903e0c53d358917a08d909c48320ec5edfa28601e7a9ef33d70627214fb30b4585bfbdb7b18c644d4c3928a39248048cf62088e70ed3703594871fc1449539867ee4f1bb84fd8c8ecf6dd4c771c9604a50be81aceb511262f8b824223791a91ec5bea83fcf0acfacc425853236f950a1b18f3544cd18579dc03e931d5de4cce2608e0816533087157c9e24253fab5aae80717596467c50c864785a6a8682b95304796f59f0e4051f9b4826dad9ee3df5df42c8d4b50c3ce68aebb4b3f0f5e748f8bf89c2a6f04227e8d73980e726ce6884e62e90fa1eb17b56bc3bb63e441cae769b4d9395433b7669453b187cb3fb26d04c52689d3b3f930c6fd2cb1d3f0cd8dc20d5540d6017cb9735692551a0531c704222da4ee79c062798bb222c98497bfe101512b19472017a9dd11851818591069ae8492dca315bed15f4356b6dbf396d7f05e97fab43051e7483d309a17c9692bfda5df2b0c8c88caea9559590c762b5711069869cef8a0666c004fb137d3394ecd0db216a9cc5f00477b71dfd20410f3c548bfb63fa8c2dd3c60775803b1d484a1cb775972e928c445796ffe4cd00128841f337200ca1ce214a24281dbbc757cc90ff09d5163a80d48dcb044c39df768c88e9fe5e1e2d8da32e4dcd76cb857a1ab483aaf4a866a54bb6563be8259bdede0b9bf9a152177430e6a5950c602aad7a895022f423b0e3002b3fa8aca8334a5221cd549a343bab4071b9b20933f6e385a7a206a377d39106be58cf98525ce4bf52a1a95914dd6b1e55268051794f98ceebb4399ad7d7e8df5ac78839efe40758afd245d4becd4f827c1488cfa44102424aa6511b1fff842447e873cb2eb7077708512d33a3e2c54df3f71b9d9a2441472dff2c8f07bee5f5042d4b6e88aa74c972f103592101cb7694289176dff5b69d467cc191c97892da8ba594038098368e73ea5a3b836eb867092001a9994452e71b93f9bfbad7fd4a523cf84f3d7f5d2ec7bf1558320cbda8bba270ce49941970e36283055c3c26d50aa8bfc1c622132d7238fdef3aaa0535e2aa2102a50f66cc8f1c15237ffb5876d0009f1135c24402672b553eb958e204a6e29d4f734e98fa85509ad3e036191524f5caff15bbd5af48b8a10cb18e2567e5ebdff7c52996b824acf25365e9534c01e83fc703e80260ded40ef150f4cdbefda4bffc145c73202e0c61437bf87dd2049beb8ae329bedb1833924ace4fcecf1d4bd3b24cc693cd3e5f11b4a75bc19f25c80d6fbcd5b974c5572a973ea0595f46a81970d8d53953836890d239f041461d3dd921fca5287c8ed6926043bfadecfd486f0f4253af1020cdffe660a8c326ef745c3e3a37ed1701392f8c9713f0e025ab122b4f365e31e0b006f8d560cf438fd42148a7057dcdafeda29e0e9ca5e4905c18e7c603280b08b9f74227f67dfe220231956b367a7e22d3cf966d185381e5dea340ad0d42f4634a8d2e2eb7cd9edadd8ba7b9d25712cc9e4bd4479d5fe332c0df17a4b0656df1ce016f0f57a94a8a1881586d05b1f1784fd0bb07eb21022c9a27c7394f16b81b48308df47a80ba8e0900f83904c2a2f37fb67cccc6711dcdaf1a983cfa6ea088863309167b083148f030350b7882e70e99cc417af7c738c7692e44d8166f963ff72b02be31e6caed2a592bdbe7b62de0b9519d083a479a9456ed5b1793da8e5527fb93eafb28fa77cceaabdab30f1f8aeddaf86b7ea70f69339d5b275426c9e5e61f260b6323feddac66054e61b895f8abbc8cbedf3b7aa77938d26200ccf81b4312a4cf52ef518ca47c387f25cbd6864260cb9deb9ac75d082a0ca8b7e88f6a05e610ec5bdc9b82c21a84f9cce23371c4aa0c0fe4fa13a3f786159971b4baf7234d86f06503ef57a2ba89152722f24f9d597ed339b5f33c0dd14e32afe348653554a90b3c7a6903e027273f1f7018a15bbf664c44d0ffe979a5e3b777b02287db0b2956d3a701f5cdee3c43cf8a696605a106c2d08fa79fc23d9ba70cca3547b8ceeca1dbb07c813308a9eac54ddf3a727bb38f6ed90086c18de8a3b21bcec2735003b14a749a5c80726930321b83cd37a674fced8a228f0f021ece8867df04978ec2bcf3ba04431eaf3949fa3eee445c01e3a5d7f313e9adf35071a572587873ba1bba20997804d7a71ac3505cf126652fc7b3ffb79dae0a1710fdc3278f227984b07d48bc8da0b7d12923049d1978bb0132d1d0f718f74f158f6a6911d3669ad88fa9b529b12c3c05b87dca33ed305bbc53d50b69d5fb0f838e193c056a3bd0b6975263945f46f53275a2eb2eb15a236b9151f3ea56b474a6bc563f1657ed6622e7afc0a893fe334d947a2ac050544722610558e8e86591ae57f262bdf99d986a2db8199226b4d9350c5010a0e5c94145e6fa14254112b95424363e07226ee35aa42517f7145cc18620c186d601de92b251f28caa8f03ac04e6b6ba75046a0f82b132619d948af5c3fe4196d38fe35bb6e97b9baba95fe20f3df5326e2ac39d86fb149c3830fbc18b9daa568955f0f8e54b9685b3eb42982cb7709dd4e4977492ac2d0d94ca0d88bc7810a3b9d93671c552e0b842f88a6f30f634709c3e88020c6c7c4cb5e9c0c79ad01af2592ba6b07f76e0970c2e1318316ad7bed3a674d762339a4cfd9f04746ff2d3f8d13ad2f31df4ecb0550ab206a92b09dfe04674074311beda15de4fe2cf918df8bad91732254c7aa25b38154d70a646e58f2280dc80ef1c1676c8793a5f4bf30935f19bdfe609a06be8ed71702c70dbb1eaad7af310908b889da0095f24be79b88d37bb63267ebbfdc9f982f704f8300a4d961669d692d5067e6061a84f8f26c809dfcc896d1e48b56a4425b4f125186de8ee094f8721cc00d8f9fdfe8015df79256bedf12619b9d3909c0e7f6608596226857cf4f4250d01c7c65c22db663f85ace22f12fbd75f9f94bf13a3f5f428538c634032a5222a5fa470ab8937ddfe9e624d59cfa57e801390db19a7682d756fa9476a45356e8d09612c9cba6fd830f7b9b335c69e0ea0f4ce5584468f5d9488aa28d845228786730c5952cbfbf8833579a1a12a291ea885293722809358ef2bb7b72f481254c8db58b2064fb59d4c90caf45d8e1c0dde3a9a18f195692540c34c31eae275fb844223da2786bc722f624423a5cd551ea22f02d87f0d4c677086569d1df27d8a04d1252db36d60392258a46f166b78f984b8fbf9e2e9db92e6339f734116997f53213917aab17fc175a768b19a814938ff8304b2c0732eb6f3250941c457fcde8bdba1cca2d177af63ebd401a7df3ccb935077fecb0970252e56abb15a557bf165e8b848d4ae1c420e1ec942bcec92190fc8395adeb56063a63a475ca6dd775d5c2cc1264b501f826e55f168637941965252c6ebed86cddd865597b133359cf1733c1e8c19fdcb6637ed1a1c330c0e1493859036ee9aabca664167423875889744503e4dd1dce8ea3b95ba6ac780d4054d893e310b7551f3165466ea02bc5add9a2501c96c8d4716a1983ea42720ee28380eec3ecec57f727a01b035a5669c29be20bb878a3fac6536667361d566fde3dd3da805932768f1cd2afd89f644dbfd34f547d145a7efa898fea931a44e432b04d849f4cd26017f2e062f4f7a9dc8c0379b8fa70072db99500a63dc1782a96cfc106de38563421cd188dee16467d046136351c2cfc9e24ec91f74214bc77f196070a0168f43a565a880d3df82b339fb64ca61870810742aa00e497a0f09db847bb03436e193d560445af6d5e525f43e4aa4ab122cfc943eedf0ede0490445cff9410d3b8076b4ad55ca7000d27f8104c4859d203e3dcd652c81404d8c100b7e4ef026b9fc84e279deba4e42ddec6c4e4ddbad1b2427643c8b0a56614b6cf62be2e1c35ee233807b1ccf70928f65b8cbec1ca50e4a0193f48e1486c79c46d30b9e48c9e393cbd63daa454e6af88bfc55a2ff11be937310b127f4de6df7220da02997a07057052c61f1b0be3599e5ae636d1a3b0b1b9d441c59bc1f31b517c554315f3ef69e93f0903098a4cf449c29d79ba7769e1b50933b980f1b33f1da44a74c46807f7c538cecb85acfa1a69626110d67edab05c739093cca92f92cf92e898c91f45b54e17e3a921eecdf6dd5a263f0e0733955511552bfeb5c7b587d03f57849646a019b0f33c6ffe5b147fe0f9b3772b57efae95f27ff0d39696d9474f022e207473fefaf2a2010d39e501530b8014544dc49142d70842fd016dafed1de70350149e093d077358674dcf90d1948c9ef69d0677d5fdf881a58a6ec9c337d949741e5a0de3a7779c267c8ee82159ca07ea7b071b2c98b72b004ccaacadb21ccb5f2f615cb2bdd6141c3d79befd9f2fd63e1bceef3430c5cfd41d7babceb92a060f6e0d566b9eab964bda2777dbd8d0717611cf43a029b3a3aa000368eb1a5321b56ea1acb6a00ae423f4231aa4ac7b1d9cbf13924bb0fea9a25b824f15c72f4311530657fa45622b3fdaa1e467ae834b8ecd04dd145ef7d617ce26991ed6a9d4bc6cad4a9e61040dc9c5ffb4c160a7f56bb3591ddb8776ad5a3963df185198f88119e4e8fa7831db2838bdeadeb66d2038f386c0bf35a047ca43eef5392f77a700b26d1bf6c7d4df53339ac1bcd069014bf879c653d2060acbc55e509c23fe9ed84b79c39c419a4cf52cc8db94e78ac48cb6b568749fe0a45d79788fa285e090c46bc7309a293c1b55d2ef82ccbde95c88d20f3f0284b5c9972bdca6870c8de755888a1118963d1b82814ffaaea1fa1b7dd02f352325dcbc89220091bac7942be0029b46a8c4cc6149b94a58d1e3de450b8f07f30f701d01c58f8cef22bdb34d3004ca42262598160e15cc1d7f63a75af85e8e88378a2bb1363cb5fbfb3f1e28b7b1882e20503659fe57a5b5dcdcb85873d29a3f0f16c3e0d0b23c5e6858a0191027b5ac33901da450aa6974a283202249a710d6a850d1d47eb6ddff25be568beca8438bf107e571dcd89c68d93ed9a839163cc1ea9eb6e5d4e41d8a5761474b12f5979251dfac11ffd9d189e30eb2a9b723aa30187fa138194057a422439dbf475c018c2e4bcb9b146a3ce86c55b207e9b72858d3e832f872c2b053f3af892b0b92d0f244edf4f70725679feeb990e176e535abe17729b5eb24be3e758fe7099fcfb6c42ffac3432317d38aa4322a7dc748f50c599933cbd20176f4879fc4f72480c35887f67c2a94189989f86f5b275dbe5ecc1aa292b9982c002c062a780a820084c73b1b00516909ff7da2f37b01754e38e9f3e1269a1a827ac1c0395287f7f3b4fe57beaafa78cb07d0674587b6bfd3a5a7371368dc2d12810d8796ef16f7c35b5398b4a02196605cda8547deef48bbb8ad14fe52db7f73a750dc0ce0853862cf44618e60f1b83b2aa63f6db6905e5a3691b86b1fd6d5e8c5d80ed27b70879cacc6f505b6bc1e9e96a383febfa351d7f2ce676cba62dbecf00a4c3c10cbc222b6e7bafaf3d400efbf030f1dd98f8e71bd1ecc08c1505325506d20868b37c5c711794e91c04f80f5c091f244944159e6f8b0d087263b5d4f8893263d1283e04689f9c476c45f1f16f0fca7055775ce24857c03b6eed64f1784c89dab8261eaf9165ea6761e3e8de3725a705b2c6637ca40ed03fce12f466eb6f65a4d093679a4c39d45cca5fe7d81cb26bfa7e02e717347e87b1bec8072eb4efcc6f3e9e444821fb5f379242d5a7c5c70af52c76104fa6317fe3f3aaf35dd0796495f348ad89df31865ed4e165b84328d522cfc449cfad46f56755b826d5cd18550d21671d1cd9dcfbe1a8dcdd711fefe6e454175d8458091867bf15d3ddd72e166e84353e414ebdd21f40d7ab1eaea4565af67d7bb08ec4eb6eb3b6aea9d3d60f08d3cd62eff57bea17651d101617ab3a9c46bf88aca7f83af3c9884d6d598508a3a2c921aea4d780f915b9ebb4c60c464cd550b6d65c79f0d326381ab5f4b4bc2f6e09de4ca53f69c7fbd948564acbcba10a0fda579fe1d532a2854d46299423bd6bbd4d7fb6464c4e0c5087fe6b888a3c6ad5826ea417c750466a04c93c8cf2a3456a8696c5dcd7feccd0a779a47722d49db21bfbd1f5f2476027b54ad1de696435a9f33a75d351ee4b646f3e6df6351b580ae3018fe8bf5429b347029a2af77e230a0f75314b7112b88cd85ee5708a0dfd0da15291aff12dc525a86fe51030b6915c0791276b5b617a6e8e8742829c6cf12ba1a749cf4af0cc275a0877dd3f20ef77edb076987e43f11a960add539fc8fa6b03782dd8f96e99e1f6d8cb06951402aa9279085b6ff55b62de6af59000152d571f5006d8e3e200824b9bbad2f4e0ea71d86fdfb34e803df8ce14bc475cb06721afc33538d204f435aaa65675f5030dc72687d5d0a71b1f865dd462b44abd167ff319463e5e0d1fc2a9a721b299c686237aec29191f4bf9b87c0ea73b657104251d35bbb732312edf2a7dd042a67372dea754fefbecbb436fd92184b4551bfc6beadec1c3b54b969d23e617eb93906c9bed3d8478da35973c018c2d812b7740c8307ab29088bbd01755a10c20b0fb0248e7fbf4e267293efef7f5048ef0deac0d8b45c8d11d45707eee7acd3c41c165c6a1109af2356626e7178b00e93b051bdc9871edfdcd3f0ceae38eb31a4ffdf23ae07db9351fb497f25b70c948f31a5fe7625a633a62f8110b5e29fbb42482041b60cf1ae1ea3e3feb5d44670446f5123ee9c1cd74f41db7a7bf0fdcdaef4e30cd39b6b965a84b66ac331b033c590384c66434e8e803906acf9bd856126c01cb8c186c270861948327ac001243903fb4e8fa832fce5d33853196632b6ba6bfb0b3fa55ef5494c38451a946c8e7768de33bd4da9939e546343dcc00d2a75c298c452ced1fa681a7a1e011c4157e5735b3d93c50c85c1698d2cecf107756c9560cd0a33fadbb9c7dc113805889d793943fae3d63b2d2ce54663f34bfce7596e7460565f6a72f4e5b268cd5450ef3a41ee6c0dc027526e2e0973a5e0f8b868fe1e693de20ea40e78b8dca6c2654938d961edf111e8370a27c0d1548b954eb59ddc1c255a34c814846bf72299108ad442d3ca8889f4a1cf792202b432a62c295f5f492f21f7a1bfdae98a7f250bb042f7a83e23c32f1d5275284c495d5d6d50a04e5dbbd9b941ffd73aaac9655878d7f5865c25448abe3865e8e0f00d3e8c70ffb0b58caad6f577dbcf7b9bf52d52a2dfc5ae54d3683796108397b3db26244a44df4ebaf03a49f09dccc4e2bad001634bae9dae366a1b8c76c2293b8115e8837774f2a2ba7ef6b5d90413662ca1d1e8dab2456482702d20f245a72bbd014cff42202894ba24c441f097ecf8e10c8889ee886af4247a02597847b57a5006a1ce6e9ecea12dacbcdb1bcf30f0ab216a4be83103792343b8c2c4979d761957a88b1e36e54d7db9122feb0f37458856f35a0eb13e46b7fad6e96e11fd170f47a71d9174bded6a11606c95beac033fbe7b4ec8bef87ccb4062b279f71b3fbb6b9507b871c7338d21ecc5d0cc0822a4c968b33a29337222f4e0306130391271e6b01ca0ba9fc4764478aae5071ee5e14e9ea2fa123c73e836cd43d79947ca362dad22956b690d54d04de5e8aafbc91de517ba8266ec8143843f6c50d7716749bbbcc00ccdc5c3335f5c7b61431f1c0febfbb74d9e15e4287db889c2392778af63b92742391a7931bec3102182acd98aec498212e2130c5b010557861ea08f829b7c57eddfa6fffe8efcc06b32a2a19512f8cbb43b00ef9f1e06fcfe57e68cf06164aa6cd0b58ab997aa24b26c665f21f241787d70d853fb94bad18b8d13e8470165de7ec657eed60178db249d1da49897a1476bb2809a1709c4491a591979d2e9941ce7fb084e7cda424aba07e10af5e7e638c9394ac9877c2831eebc13a46feb3faffef9c169ee7072668688fc223cf4c9f9ade0ddba70fedbfbb233d3465cd76d34ebe45a3d39a9fedd2c58ae0b11fb2a3dfecdb77f473ffd38b9433c8dde9a1d6de2d02273a215734d794b39a28feab396c65dda93ede9b22003b81c43beefb4a8057f6611ae6d8df9efa100ca4c135cf0ebaca15704e814b477d225f5d6fe37ee1091eeddd991a4f0c1294d8424ba598bf6387d47bbfa1eff9e4661befc3f151e3a8037ef0eefab0494543702a6b430f48c1eff38594d1065030ad318cf0ed0e0bb42c249715526c061eeca3541535f87e2b758fa5856537c781df83f10909ccfd37a847b853383c3965076bd1f7b9565d6c1d1d7df41c0bd545e165825665c6f2996566cf7f21e55c492c03a73789465af702971b91f1d09df25340f7361bd39fcdc787848890d5bf15a1d413a83fb5a925a80e1c562b543027aca4bd1d799d2d9f0b203f8a6244e3f00222c9b4784f574010806567532665831b5ad21f9efbef6213d1e3c44199c709d34b919ec0bde8db5e5b3424df1ddef3ce2975eac3f46f6b4291c16820adfd41ba35bbeb73e18fb50e372c2f1e1615ab6f63717e97c60570e7a0388f9b7111610d0c29a601a22896f8b43d39089c26bd3ab6ce18bc2e7c6406c7be20d0884f303d3af9edcd5ba2b79422148efa592a870dfb8c4fb45adabf558588015c3d3848589ee7132382cedfaceefc752fce7484d6bee1f4fc90f11a58cd97d65c3b91e47b6f492dde766638fb65bec17105843544725ebc0f82b6ae33e55f248ae701d423f958b9a759e6a328ba974f6bf7a94422c112667e58826ab6126cd0694ba3bc067f926314ebf0c1d05927357cfa7e759800ff8f244da2ad277fb9cceb1bbe2eda62dd2b841e012b8cd08f8a05e6fe8d8191c51b6bb8b0f9e294191d42d08842880a6377fd4628f3f1f506c5ce4a5f5a45c98cd3c492659d92e6111fef98f7515a7a355ca6a1bff4b5ad172ccc0b15f289fcfe4781ba431109c8e8198f911090786685ebd2e6c703375fdccb49197bc1b28ebecc53e9f48fc26d945bb7a3c5bb4c5b6596c672392ad52ccc7f0143acc3d523daa652fd2c239de35d0a50491abc2e53d1be7650cd099e71554cd02fa13de0e4bef7023318c08ef274c04fdab7308ad86dddbbdfde9d3d06157f0330e0ebc1b79b01028f15acfc4ae926236b25293ddf4e42a10433b713d7702cf8ed8c0e1bb6289cedbd8ded1206707b68de90e70c0bc57c4278c1f6d6c93231c7ae9e419b4dd057fc290ab15f06db315e0a8b4dbf74f86c78ae2324099400c2bcec8f3ac9615a199abaa83b3a323b20cd13e82eef32cb0cee8ad653a2485702b3cfe386893dc70d46174b645712912334a8fe7ae04dd8e8e25ea8b2abdab9fec2ae5bbad9d5aa54a6c64121d80c6a52854173e7bf0ee3ce28db5b0692b99387dba0872c143ca4f8a67c5d0771d6b1641bbfb57124563b1e7d61f95389f2e0562b0a3c11420a70e428e3ccb2c9a3a3e5ba4df8d170306c572c6ef92eb5c1922cdb45d44ebcea2430aa0ded472ed2bbd4882fbecec23014377e7982e303da3c2497db0a88b6320e586ad9a7255c87f583eb5f538926866ad6fb8859b68ca273186a00ef2cb562cad12862db6bf1816dbecab61705b47bdf2ef93b6ea52aeb3672a754fcaea06e2c589aa0717fd626da070f88322c3904bd60fff34813f920ff2b6a60435683282e832dc6dafdefd9459e5e60fc377f35301f8d0259b5ef22d621d0c6a31208c1b58f2c85bdc2d1c3248808f166e7b62cf4d6585e6f7f3066230c233b725346b95584bb78efaa21f02ce20789afd15d4a99f577d2e4f021333af43bb3fa0dd1e3475f8abddae60377341623fed0985d14670787148ee6b36cfa938b838b30027162780e1ee8f51ebd6f45b3de1a8e2b2e7b676c5ecc2e597ab2939bcd9282a9505f13a301c1f6bf377ac211e0c1b6808a940da991456273920fcef460db9a2cd03d7616afb50109f57d5047c7a3744477516d4dec8b6da60af40a30310cf0df1ae2588f49ba18ea2ddd5a7e7eb1baa669b880cd05996f86d1d105b856457121dc4021431c5dd1a82e5079f64acaaeac5d0c5c571da53eccc1558d7f5e7ace8cc626c106328a793248ff5b20db759559eaba7939bdccc1bdd4c4debfb2bdb573cd7b1809bd53b28bc420df5cb8420f878eb3142e407c5d63f3ef195baf82ac7dc9be03d20e0a92ea73160939a59b0f51fd020a237703fb7f79a9d6e587ab83332ff2a0404493f1f6706b9188435fd2795f462902f11c71f86a22d57536d20ca91852e5c6a1d3ad8f8c6beab071339ae0890dad14ad5a56480877c73cf9955218117117e2c0a1f6b5c37904b618ea182df28caf3a86f33986bc7299c6750cf66aa145b55183d339cc4d363b242c9574e99d8159cb61f9bfc7f66a3d53952f97b646b0fe074b2d7959ed86051927558b6ced7b5c6bfc4e9ec2925ec3f512f02e9b29d35ac3494b2c919bea99c9e7b63a5b958e5a836b3d8bcef72bfa86761ef6353473aad7b3a5713e5837a546ff62c6ad56b97be99e9b4abba9b6d1936862a1261763c60bd92d87bb5e63ccebec493ac98bb5f12d058ff3e10d86e6152273e96321f94748de62c26296b2f082ae3fd7d95dafd7258992f0b9113a5be6e227c9414200652a66c45051f1f64e09b7b24801910618851f676aa1f99671b42b3217ac7182e8ce4a4c0a086b0d0ea7c468a995f37bceef867ef0e8833f9d563592985f5943a89e4fdd325d50b70fb68ff6ccccf28ccaf818a57892f36360b647bc7716560a64c3a1851cf753c4427073c51a5dd33dba2d1ebff9903caea294a967b4789b5ca798a609d3f96ffd0443260a2a232dfc216191683df0904af6211a2b33ecae2203526aa08d8c2c560ec01d1ddd6fb7d4e48ea08d2fbde3932f1e19a94fd9d5f2db544121396ca0518f442064d40b221ce4eca4399d0716bb5cce388b10e91942a8a9215102fe7d53be71adc64a99f72179dab3f3e6005f9a4d08e2f0c68d6a79e1a16c880c33443881f1d44efdb1fb09118b25fcf2ccea177f9a8f7a623f2bca61aafd3b50608ebcc3cd8e2b8cf2a0094dcca609be79d1693d3010b045e980dc31e786200882200882a88d4025f98cf578ff95b53900006ea66d5b47de8d7a3d0c5baff62dfe37b2650ab7412d42d33e63ba8c9d3e9913a00d19412cbd6cc949f59df8547b41534be6a10655ce83966d8bca05168106643738a52447a0d2e40f8c606a3275ad0e07825346a547ca03106ac212f35b874820d6a5dab25cf8c644cff82af50ea33eb04c3a3fbac1fb9caa0645cf3d60c34ab9f2eaa240456059bdc0f4117f5c84712f009d0c252150441439b925650754234bf24d03ecbed8e9929ca0ba2b57c2dbd531a81c8eeb969efe51a554bed344461fd171a9e130353822a023b6c69f1a7fd1ce9c57853e161c20de814daeef78e0c63e67a4ddb9e6e645ea783c4b1db19e4506c417d8529481b7f43ace2a22d02f8b1636aeaaa505c05ad0044e29f8026ba088ad10240fa80b86d0a176e1c8b20ca8b91624bd328a156fce40b9e0912c54df658e9bef41067fe09086ce8cb4340a3b15b9c99c9c9bdac3ea6d9e24e94529c4e43c19f21c428ece5f9ecadcd2a7b79c6a6e472b20cb9017d9cad074504c831a7051b31d1f21b0148c47dd074a561e13e76a69884b4158aa5035164261d9475f799054491ea756b19b77c44b5af4a45ffc45791106c2ae5652d055ccfe5e72a21da848ac678082e4b4e834ba265667b29aaba31e92842fcf82cb757801901ca7cd68226d4bf2150cbdd89f0583742af9c90dc06eb1621e241f2a8f1c8b6ac1908e030013300f24b65c345406686507864b330c69334a1e4f124a53b7ce126df7176c98cf9afe0b94a35b69c8d268496ab67057e1189ca07a1c04346fb284c599a4d9f12a2a09594f97ba1ef321546b7a84ab19695af34b76612de744ca5a4f3cc9000cb499c90b4e73010406f88a0d94a624c28fadf466435790ab63ed1818d52ca06d5342d1e96461ce583b44352fc397e921ac5c9b5629122c2c2425facb8ff4677e861ab9064cbe231f6d5500a537b9e1ea1a6e203ded9280ff98e1aafdc8ec78b5d0a04eb2f1c8551ec5403d2a8b7ad023456ca716d05c2010db7bf0f47252870356410047d94505b11ce9c0b1d91cf860ff94916a3e5fac64595f7ef4294b90de8456f1d53e142ea8c2940e42e7a375d9d9f146a0a8ec9892cc29d199d0d15d765a8e43ad1c9b69b9a53f5cae8748d80bf60e7c4d415c4f91220cd6d8a525a3309496afe4fdc8714b48c10955686528661484b2fea0c888e4caf4a308267e0010943d62aa4f03fa4ac3eec11a7554cfb5b7384ef95f5bf0ad78f4315563e445601c7c8809e16355696b3480a4e428131234213c66daedefc67151915e45181fed881380cc37a84e16a242b2496b5f6e7fe005a7949c99d78a3996008331f9c11658f876c8c0a6d19811e4316b50bfa9f4d77fe2de3c10082dac151166dee8119dbe01234fabc84371b1250c3a09125a6d25d2e42e90a8c87e5a54ea444ec0b068444e5dac34ed142a2274dc9ba881662a1c8f14860e924955562be20a48090b9c9e331c04ec73ea4f9f4b5e1edb2122a311bf04bdb88ed8175bf8b29555761ad8244686a0e4aff3b83861e56c31f01b46a84da01688db001a1f47dae68e5e2460e5ecdce9208b375d80cdd0a7f4ed68134f743cd3729a252529d2420a0fd84713ccb4da0c09f92f68a821c949d15316ece844566c01d998e1943910ef08551031cb0514edb14b0218c937ec503b0b2040c75302a52f1449f5a7011cf21631629a4a6045cef242d8993abdc85fc8588d40adda67794afccf0f5e0fc2ca4cb7722632f25183179904ca72a5a027912a47b692044bbfc85558426e701aeed0abe0548a94cdc08605dba5ac8ed57bf2c9754c21c9d1078b803e4e99623b503908f7ccba1e4117bbee958c741c26c4fe2f5f6d3485a7bd4c98f3036dc234194ecfbbe94486c57bd5e3740a55332142c5f7b104a3991c78b618361899911ead3e73ea56339b6438d95829b62ea636ae371b9a842205d940aebb6c01d15d5f2a2b0bb69140d5bd72b80882f842d7bace6cca53d6c8bb508a6b2ab3941c569c1eeca83cd026138a416e25a4c563b58a95e1049ad549a8a9327448933f6a3bd2720bdaaee59590ad9a5cfc0d99195f93323b86328985720bd906ac74f89526affacb020bd4820ad3f5bca3580730d0f21b283d729f5187ce2b03f56b3a20429e9080573bea003f5622bfb6e394d561f6d0691d6a42352317b9d853687a9cce0cbb9bb2126c41a3808d16a7f8a3090e5828a83cfc0a095999881305dd6893dce110f075824495d85b748fda2d2d5297d0f5cc7ad85a7993845b8da989287616815ed948eac0d72311455a891128c05b6d50f6c1963579ccd7a5c3953265a614255d66eeda3d0eb5751bb251ee70a1c817a48091853841f3e0036b06d042c6737812c336ddf0b50638b91cafa2d44f120460d5a46c97317094f3081872283d869fa081018b0252976614402c47996101b8a2d4801ca6abfa65c6654c4ec0162318c14ca3be83e80a6413733619b035c7bbf1e55ce214fbc11c6a37b1d3a58144396b3f2140e5a89d33fd76e89aad3048f15c6c9c1c881cbe5e854145fe086ae765c2f11c62697a88eba0fb228c695b7ea0ed9444215bcfc011fea9d17449270a0081c89162e56441611f0920d09a02ac050b94ed3dcb980de10b1f740de5450356aecc8b0e94e55457623ff51b1a1f76d2973a2fb18405db5e42758f14429a46124b6f56525f79c1a13558b19417701ad34f3cd5b89009b89a98a0cbd3646ad1032ce9c88c6cfdc84858e46a34630f3e89d22aa08b4f52da0b942f405811ea7429b038c82908856e936154f0939f2eef9b75872da3834acb6034ead60c7bcec2365854647cdd02449bcbba72f6365c3cf4df0d541d17a941f32ae4e984ec5c39195274ba0188b3de82c0542392b5f7314b061a149e356c8643a45a940240405496203408378b821204506a145bb200299d80901391ea2005217f5d62920616ea6c7329811e7d2a47a79e4274c44639daf5118c690d6d6a9e620f24b6d60607d73467519f1ad4e82af0f6586980133f934620e76875a7975f19ba4ab0ce8deef37a4410b5d9904eac8022ba5823943ab04b2258ea253ae0840cae901ee84a1260012d3afe835583a0a04b0a7a579e146c273bc5060384422eab13702b6dac6caf47354e0c31ab9940f873106a40d851492e759f8428ec14d0b0dfd0b2c3762923ed2720c29c099260dbd9328a6521657a0f26c9c6b265c1ad3a767528007e58183eacc102cb3a5dc65411a090bdd45d9a4f804682c2bcc50a16743e68e6bc0b96821256f53dc08768df82f5e37dc806b166a28a9e084eb2696a1d1b8a940e19439f1afde2c39edc69c7a2a63386c1c5ead83bb8531e86ab3807f1068de5a0a4ec5f0de63a4189b1c69445cb15c1a9c8a9280162615958d82fa0a7af2b681d7e02411c90c753460f52f40008a746abc6f2874730529c349f82edba1c873a6dcb92957ce34d828e5589cd730d629bcf02f44ef270fa05323d6cd2cca06f8174e16788b0fea7c030bb08d3d19388dce8208f1c1f62f7e1345eace85c649ada38c14fbf49cb21d4c0ca436fd8c1890dd468ef4e0059832f98322c7b6acf57163c5ed8137228351478d48d0526b02df2446926483075a7bb0c7da9ca29fbf060eb624d52b1617e1bfec44e33df39f5e223e24061df232a5a4ed355470f60e8b348c75620cac3afa800c3164132a783687ac01e99b3cc31d892f31a3eca2f195e99d1334d57f2ab95d7e4e070507f025825ab90b0c24614da459946ed2ac18d7fb9c27b0bbcbce301d1fd6100946d82004e5f6944202b51a9fa1f4a3bd8a796023d408c9ac36295a94d6099f1e23b7658a6ccdc6f82897ec242c20652c1d736ccf068205d6c3525404fd8e7823710bb3cad97f797c1b2a000de3d51661153c200850837b4aa028c58258f42ddd12c36d954b3c7a144be7a06a5074da212ab36a083d7810820f14dee96a798d028c7056d5d6ec9a7ac044fadf6d341070bab069b9e65d684e5751cd14f02e561f9a43ad49162292c956a99afb2b2049896eb9919041574202a19b2ad18a73e610a8c9c235205a09f511afa1188304216091a1d86fb868d2343ec190ea860b381e3c52068c1c63bf6b4d54cad4c560343632a11216fa93a6a4102dc7a4e9e097d21edd2695d1fb5941e365ad6df591b997cc80020b46a535ea62c16bf0bac9aba24bb6765432b1f8d102209d2d7795c7161617548e615ef5f10c6091f7d07eed9911f901e12c24deb7a41e346d81c79a72c0d3a8ca04bac213e457a162757ac8197cc85286be42eb0489cc8025bf9549116cd614296e359d07eba23094b7563702fa31065366d75deab25c48e85315b15205d99899a3387e9d42a47da50a8b5349900b43222541ed56808dbe666e9166b48d84979656e879495cc442d470bffc0b14ddc9879164089b29a0fad3a801a232dd4c2f63e104dda472d281d09c5df8998887427896464e1291bad82172e3655291b4731bd638fd16927c774b013227962810c103b1328aada100e30414fa2d232a865982f3b51c864254252196ccfed2170208216943cafe24c7f8d9ae61f2541d3693804693c8b966d3cabebbc32626c19b5365ff231f2af0f2a9a45725226752aad55a988c49e7986fa5e1b4af94ef4c3ffeca47a8f58d25e5b91a60594dde53658f8d8496244d800073405a57494b071308e08cd863c7b573410b054a4af4c9d114148a5d0ab03c29bc52eca1371b3224fb60a981a97f1c641637a84d6593e55ff238f8126a1a1cb835b1c3c412f46f9541211efe2004f5759b13d970af1b416217a898b5e0da7e7a78d7b8d0ec24d84c6758357eb806f640505d4801d247d7807b654395710326792878cd0112a25599096385f561c4569212034642c51fa1c50093e6d3725d5bfa438700a2204bda5a426dbc214d773e014b1602da95f90adc955ea34349e10ac0e0189aae3123cea2776678dc468890d1213baaa4960722caa984bec49031c20264ac781e0715068693eea9848b80112a96c6d604ad8be8ae6407848806f5edc6911101e3d91ae4fcd760a2dab4230a8efa8a2e61137921d8729819d104847b35994a943c87ad4290a41fb932c2a9deca1844dc4b7a28dac5ebec557e27570fcda442926af45e3114b6a04ad4e03f3083e82dbe39c127e954df97418591480ad2ab5e93e62d67a75d20e35204e88aad4ac5be954e41b27da5ec5b2ed494c8e67aa212b8ba9a2765c9b5efe02a748c3d9b95ff202460ffa7bbf4a56a55665ab0e1b0054a2d665c3535b95aaee038897f9dcb2d5d21e66a785a14aab5805cd3404d962ef2641ca777092b2a33a4380365c793aa9936195c553f901af1d6c5752d4aef4cc68567b0ffac1991bb9cedba3f6334440be8251f41c651434949080bb05b963ab58f1d2757997320f4b044e6604905dcd5d3bcc4c049b280aa81e0468ac8f00ba9e8415b1a652a0735b2756413b7f40b2923515c1311492b4173843fe1427f82e7d6ab151404d6828708226a340c38110e1b0bd67d9a9a8c461512d63b492095caea2c2d919da9ae53426d37b7418c0967db1d37d98a2b089d4dc68eb3403eba30f8307adb0bd8a66e3940435c967d0a6fccc5d6b1a5be05a122d3bc128aac4e40b7246056b8901c26222953c9f4372fe6a8986202006692c84c705435d71f3204a36f4a6f6549729b3aaa904d9d2b306c9baa01730fe3689d735fc096b4b65fa640191c67ce9454927da80fcda5f93dc8705dc634b9cfe2480a42ee507d0e74830d231a828c979d74c4fe541482e8b408a9da20650702d57a5f7398a6071f4ba70064d54042ff96936991679ac06bce8b9b4089253f0c2d05628ec6111e5c8742e2aba5c40d88f4634467d21a188667b555b4f39e9082e81385f17284d0d50e5a26215ff2803b41730f5fc560b556c24bab61e00a8654f59c9950925ef00310ca8fb21016580552c20ca62ad19b6d525b47c02c3f24b8455476730b4851526c39a0fdfa12bc812a69f6424bc12263887f5161f1083121700e342b3a1a5aa030df1faedf5bb02306f990f95614b7b1dc85cfc90f5978325ec5d24b69e62034ebe0b016d151f3e7c4d544006d0f69747cc49c93c687900de21f2214baa024b7866829ab6a54940508b9b9c35b11a02694581d536320216098aad530479931fc19c5ea4cc9ceea2a7a5ed7c88930744a8f53c0cec5e480090d78225ed65a8340fb2ca0ffb810387bedeadc9a31051388d448d2e690f5cfb9521af85d92803f19c003be4a2cb59c874004d2d3992abe880129ce661d2758b6a0873bcd073453b405d521b24b744ebd345bc20d2ad7ec465219286fcae8b9ba9a4e896e3a4eb3036bc1ccf842b4d6095a247b052e947825cea12b894d911113d77c0345e8794b5ef22a526a7f1d2f44e88a2ff729426ab72defa6a2067d9f187376088006e5a92926a4b2184280f9f7f4106324465211f7a048f47aef4933ccd466365d6a1bc0d73900aa2be2a050f36158ab356f1e814cb690a9c26218a4e53b56c7829291b2bad01e96b6a56e44b659a9ccd053c8f6b6068135f14b213336b2705264623285ac9862895095d3922d44f401582523aa5faceadb72f2425ace44e76f08bc6b0f754d5085a5f47b77e45ea534d54740e1098fa06a2ba5b41f461910cc88de747af5bbd127b8d2086de81cb8be6a005513f1153e90d5c11c97066b39a4f8f222c892800da4388108781e81dd38ab4fcb6a0508e8ef900d4f1a6112be40227e320ceb11fee88c2218d9c7422209b3a7782cb777966b29869db56eb2b160c271ced8948a43b29526c004524b1652ee0b58c4cb49a8e9d1a8ff322571ed2c247f7a121a48ff470755e704a35970d0f3a432b960d602bce4d0cb2d1029ed126432184f5a400af9f51e6e4504398ede3c9cb76827ac985d810d93e6367b22b0a44722f2c7672a30919ae6b1aaa957078c232fa33a6c934c0c42e0a61e5179ef0e52c766e75893412b9d7031e2d049ed41f76446952358e5d6290894375ac385bcaef7e58ce0e40caa66fb47cb9b563a3595ed39c039189f621a6150bc5c85a1674614d50d671408fa94040c8b66a55ffd99481fd9021ce58523161b3f8313baeb798cb0a04dbcd101bff1a318de54a2456959a8ebe42e64fd17bda098a2419eeadd3f9304d75972cd01610694a2f9b373aef048f56b00453a7d27b95ff2e00ea1c2fb6f419040c9de443ade0570c65d332be100ec9442abb9121111432d7e310247d01afd4d9d08dc0dcb0791ac4eda44e23b6cd1e9b1331cb939d550a2724c6d23d218272b95d128e0188a273b2153d84160fbac51db76cca7ad66d62291b4b05265732c7820d5bf63a2d3b6107e5244313723086d5d10b9829e841153426f8f03db521acd5a591c9c40cbb3aa493b720a9412c6ca464a594b7ac776ac2b70438d06478069ac073c0a76868d232e2fce5eb8dee590b5064265dad8655ab434e0125513b7dc8f910bab6b651c9481380c3d307da12b59b93551da0caf7466d6efd56c7416882907749b290d1607ab20d8471c014515c6520914541e64bcc58568502c2790835152671ec233a9d5a801b5c5f4321ed2d03ae6c00517afaaccb19168c2bb0b624eb47c6c1f6e53384d59faa11e7628bf2ba16186a935206e9e696331d069e5e2e87a2a6e3c88e950b04a5fd34d9c5e2e0b4895dba7f99048e59dd7efa04447e4193a525707de971032757a8652908a760515e46d091b72828f45b989e645e664cb4154f2cfa8689139d8bd63463589a7a98b731760096538da10eaa8c04296ca7991b1dc433e1a49080fd862d4c0fc157e332708d7a083f212c2ebf5aada905a5ac05d18786348c9033f502eb0c024609cb46b10da14a86a0261e255a92f4c5175c003deca42c00847a0068359cb05a2f4069d73ba0ead45626943a24114a0e3721ebb60d740f338571166e3ad7da03e14126e1faa12f715ac7a8b3832d9a74076b8f2e4a87a2fe5305152beb47ab3e913ce6397df81eab21000fc92993f14c40fa12e3e319059b1d994f57822b5a50254b1129ea1b464d19032700efa27490e7c876dc5211635e4393a0739ded7a7f91fb7b2cc00ea7dd959c60627b8d7af43cac98dc44063dc7f1a547fedda4df51d2e8abcc10781637abf2e318cde8105d874880253b80d44b284409e64afebeb02fcc3475aa36958414ee6cca729788f948c94b4e904c146cfb88b433135cae70bc7b0b6989cc29155fc3e202804d4424d5a814542f2946a8b60124500e13436a4562b26eabcf85cf9ac588bdaa237dc6477f15115633f10c68b31682fa0c1e98c3520127fb417526b461c648fe05224038e7595e4ba108b88b095c67a9938015a0c748039a0023f74095a5d16a0d6941502af5f114dc73ed02f4539d24bc851b89fee36453068487d56125f1c3b2c0a2e6768d26b04cb406fd45c9592eae41f211637b4e8a828ff395faeb31846e9c0c8b0eef82eacff704a5654b1064e45a88e2808258396a0a70553206476e58162f5ae0dc8952fdf767d06b31805b14852fb915140c5da9171c9b814890b65e09d3065edd359818a6dce242f34029425a4f9d2e5d5d61c636cd241f4da184b514b6e936a050be401299132f8cb5a85b927a441e153912a0541d08828f7e9ae16395804ad24657756c051c6fe39262244fc588c98b58b2172a94426802adc1eb26595f6bccf2c4114f580b85e47c850511ddc5c0b02f5ddac05ef534fa0759307a2cc2890e7588c30ff948d132ac7068029ffe8e868cfba753609e4a2e0d0b6107abf76a4acdc380902eb4e8ac6fe88963e96c8961c3648ac28601b4aba334982d41d735cb90442747e2e0841ddac9d0d5459c5a512051cdc0538b4eb331aa5d9d485ec92943ef83c4260f09bcfa9d1e62d7d164aba5167065138a5efccd8b682ea205da2592bcb106e260eae50a1d2c872624bac8147e92156876d1a819acab60200b6b8d61eb3644685c6818d8207c586529b8440899c499d35462948cab878376f26249ebb986b8ac52731a2ec5a0ade868c342da64f755994ce4062ec2bc4c34caa2f1f0ea85f2de32754c843f4230e5b9be54e811d0e39338bf5a4e0c4777801bd430a428d811c0094db6e711f0c48bb9d673bf0461498973950a50768ef1158b1677164c526490d9041861a971d46e1768105b05d0e213e686f419556e4d26baa7499432f452431259d71d4f2f844046f6ab1abb95838a4d43bbeb55ae2c34a830215e440f8c6e0fade915422af94c98ba4652e8c94358ef58b3be3ca0b453355e221242b4e6b08bcc01d48a4eb0650b479a59070654b9c4872d422e7526f51a255e7e69578ea6a0c94d3f7f10f882bab1fe3288fa279088ec8cafb09642c84fa71915774e693875f403a5270923aa4dac6af13f6782b01aec449dcd9a681e40a5d5074dfb4e06c35887416195db7010c8a3acec68221f00e5b6566f424d6110f217dd00e0d14aa56e2297ea693cf0696710036c2abab89d3aaad94dacd7ebd0d0713d3a809acbf0096ba907914ef422168ba7935e0f00b1832573a9ef019c5d6e6b8d84875ae32833a9f2a391c870d518d4607d1804f018d4347dab4829b6a9c9cae2897505afa14ab4274d89720e22d31ec3e91030c925483d65c003784c1bd45f4e916151c1f168047a7bac59bba871241ac5be4012d6af5ad0c92edaaa7cae0a59160137a8cb68c8c24a49bef724c74f7e02e1d1739049e9269eee7c09a01559ee11a4a31080a3ab463235ae0c626ca40d393280239580ae9880ca8bbad0ca9a501d62c10a14c82f6c9cf582124be085d75407601394cfe432b59a487e2c04150e0e26aa45d7aa7a681c4c17ec5b2dbc2e7313824d852bd3cd8c9af50077757da591df256c62f5eb153d19f94a793349e95be8f6fa2ecb942c4c9ee520cb08c13d3469da0f2309c1b6429eac6a4f0abb2488a0f3a1b2fad27017f71cdf9d608aa0f4365a551f2b43f60e5a82796e8c8b1362f4b25af692b4a9ba4cfd56979a8d9ca9dc82128dd692e345db3163a785137afd080cea5750ba72b18d4df7aa152baf7d4183cdc995719592c5221f5cca96c020730e2f507280241d320805a732d915b76ce0c42bd693045df9c5ad0b415144a2afa28ac0ce1820e5acdaa8605138890dea8dcb82dc7cb8863ca9320550276e421301b6ecf9e3bb8245f21302477ee9902676890a4c2d038094fcea31d1a91acc655022069c56266aaf2d1a94cbc0a20434d166c88518159b16af016da40914e0aab857bda7ae0d9beb4b5b2e90ea2a4c22a949739a11e658005c9bad258ba550c94b67e0d0abbb28c95a8e92d0e5d47abb16514fd84b4c92270426c6de19f3d4682908b16ad8c0e827752b3a0b933f7735a5094b6a53927ed19cc5aed9f3aadb261316881223d9cf9a105f46dc22f9801442d6950b51ef21c1862de5667ca116a882994ac9f5a3b303ec5c9c25cd0a17ae96b37e7a9c3be49bc33fd10d2bede60f8cdbea91a3232c09723d32885de6ba9a8625b947535568475e8ce4346d14da0671118be38edce5c25cbfc9079f5cf772ca89e0f4692784e2af16ca6946ba6900bc100799ffbee0006bdc31f51445f2321ca53dcf5584d3531a3c792d1b5d1a45a16827ed2ef45a9cf10b7a895d9015090743a74a433862e81c16a8ac0a25a1fb2841e6a0622e17865090373069f22b0e47d723026d2c207af50002717258824c5997899cd7def8103a09f4b256acd4094a6013a831c891c5120a16d9170998c1499d2cf48d240a72a511a65ac392bfb840eda4ba3d2122f1835e39c9660a6234a73257d85bae76dc8e102119128ee07f287e514cad3e338b4e7f72e4a03d91d1d1c3bd0bbd600f505e20e45637c042882dc3f279880717fad9c8d684a110500d5d340f789b03bc43604d66ce29616925d9f01950845d4667a97d34399ca921ee2cec1556518824f733566543dc22d48cf8acb51b2b8fc683048bbd31c1ed2fbfe111dcf1d4ac227cc80330cdba8d55a5d8185380f45b02c26fc5f1d0083ad861d1fcf8943fe891054bbc39d0c6eb877cc3428accaad98a6df5a23d678c1bd0da2751b71a43d4573217bda4c4f60360629fc246d35bbcb9d440c25a640735067d0b25032dc5075ac770ab9cce2a2a4da74749763447caaf828c6401d4b65bd1a1e77732013907205dc6e325a68f3fe8b0b75c5c39409b675664654b36854354b0112ba0430910046c3322d771449bac142c553fe901e80bfaa03a1cb4b0dcac84e37daa7cc8a4aab8b88d4a74f2f5135da31133f50c54de6e839d94bb2880f6955b30721067b583a8421cc2a8294d844bae6c6d33a4abf0a0d3a690014e0b2c06d0d19434f9c20b21ac8d2935df421305168083a137b511591a3cd0349c9c1bf080a45497c4ea45ae43d7e4c7509995e354e8a8205061cfc8b16b45af14e5493af2895c62c5024220e1acc8e83a40aab9eb9a70a3117d297b14143c9aca46391c9dd37d44e4ea5404c2d844cf634bc13b90db1aacc8c04272c0a8a047b98a078715ee5a944915c2c2c2e9d0a89574aa918b6a6efc40e84bbc09ea3f3a14fc61ba59038e7ea651907e36e094a708e8d0671ea9c89388f45d2e081d2b298aab0ec2a7fab7959a0e71a1419efbd2e9b3047516ea617b9a2a62ae55f6e6781507f653ab1a1d4d8380151025d3ad9c32b2104fa49ad2de9ea025b1986590778385cba0b4ae096c0dc60245f75991c132c061d7a9b6e060e7b2d06a242d2234d495f15d56aa5cea08acb9ec71d09b7285b82a0e16dac92b30578562045b6bd35e93294564e7f65469340074fe04891916090919c1ba38913a139bb0e00e0d1a7a821b26ec2143cf51c24eb120045d6a417a6c054d48adaf40ab097b0c25fc2539b4ae3126eba5a2a63a011ae2cf3c51915d1c4d1cce10079609d5560f33e1617508f0d00f2a99c9650e28f8280895d8f80a9f768333cb6092061c8aa4b4bee104cb00724985ada01b2d83035db0975551b715f194875b3665351808001b81e1e92a579cec8fbf25ed2a975b834903f64ec727ec8f1f8c9eadb4b16ee738ad2072d9f8cad9ca1c87ae63c69b73726be51c68069d411e532c136880c62587cd87ab2034fc27c32ba9292f61472856068844df25c78610c7195559811d96f512e7caaee810176465604893b8ebc58aba0483a5db1321c8a240976f001489dd25cac38d6c78715c7da66f362a0ea76df82a74d657a181a3c39018c2ce08137c9458476ea726434785d8caa4980d0ec30a66770783d6c4e800eb9ca4e06bc46664e901b03c26e9c49be855e8eaa90becf2f99751a101024c4b4b63135540037cb0262c0732d3d68a3c2d3b4e82b4ae53ecd11954159f064b402f379dfa24011cba140f42ec1f286abc1269dac608097fd44b2d2b3a03e068d60cca6646346a1696e8b04854c43a09226858247fbc5c930c352c5b424b9378650a98265225d6855bc69a8d29c95afe54121ee845a40111ba9131fd35ca3754d06163238c3ea24988e5512cf32c98bef205267c3311987c8d895d97eaf3b2b6f4107c8418b70cc5469e8ef3b4a4938828d480dcb87595d2b061c8d163a1937edc2d1092274293a74d85dac55e1914ca677b003a0f2832dde15900e8165bca90d2b879a01eb33a49090eaba47a8a4d622705b04095c1d738b8c29ac1fad229daa8c97009b2be3076e0739eb7be84c982ada4024f9b25cacb08485479a225abae62ad6b465490fc9126079d8b5322b61d71a11fa188b2445a9de84ab0ce58226e12b26bec032c614682e52336164c2089afcf5c718513e458656ba8bbaef1aa2e8f859d08b6e0c2aa31fcc14d2348ab7df0b19da50e8da603cae51d79258e26b9a36df55f431852b814beb1b66612d5696e98e90d6ed696fad5b519d54bce0184d4430419b2a410204f678b5553c23480751da901b2a0517c212056b225024cb2821271f224396f2e698309e09e0c7b1a919cba2cc108aba37891864d5a00704c365eb5dd120cbfe53a7b45ad0c1d42c55f3f4051a98b9cf863db5975de64974dc707ad1e9497e95447685e06a6a36f2168d368c6747ad8989fef6a7385fd6046426322b3833dd5a703fb664a2c4c4be0cc0aa69860691111c0ba783247b8e091817c474501d0462e230d61cb9255b2c9cdf5b4616d012ed28d2450d2440e007f8bec8fa5c31b7521ac0cb9ea49ca9180e295e9faf49c81524faf4243a5d7043ac232b9537453313337396bf0a29c47ad420819562a8590691930c05a50b503a806d02616001719c23728a6643b76c43a115a25d6c55e320b2945a90350a2d365ada34e2123166b458d48cb6882805523f5d1ad021579d183268765eb0adb290af31eb4a460f564356a465836b1a06eb46ffe2561f138297428c737d6cf84fc584d393fd30043b6c00348b3eaf0cc47a3a1dbd96121f7b8850a58964457a350b02ad8c2d5d3045cf1b92859cb0fca63a2d95c5439944e3fc680177dcbbdd01b2ef9695435c0b280b5bebb1ab1e58838518fc9c2287652905aedaaac7931a2d0748a378e3215087b2f9385455ec4cbd0efc49ce42dd5326d41c7942f300180f4ac37228072c1426cdd0835403d3752f652e6652b79b294cdc87a2064c1273e8958312e288c4c6b0255830d3204d8af7815684a3ef81e092d4ae76a1bd5acac136c340198e6f041c3c554ac6838ead7053915f45104b059c0a49a8820c255bdb3ee81d65ae624875786a0c249cb4172d54afac8c96b204eb5dfaa5bc14aa6a264641f33568e488d1c0a920c56c0a2b1f3fa8af23db8c00e804d7eae5b479acaa40cb722ab51a74174f705ee5827711fb17988c9437822896db6d8fb0e2ca27ea78988ee7067afa938e2d13dac43583d2c8a8dc986851f395db98d57693d0843a5cc609158500c8be675290ac12aeac2a7c71020913bfd58fb0a108a329f82030d87eae981cee4359e180e9712a555236f717a9226934b81403a090cb5fa0099bd3e05d6a515d425c912cc7af49544a846b007794405c4b06440507dc8c4c54e79c4dfc01a590e8910e5621c1a4000a92cf5728919a11b0c083ac9972ca4e34b9081eca120ad3e53996a978ae5f4c14d03dae164e11e34ba19400f7e89929316b045d33d3c75b4831e4af23b86d7e7fe083d9583bbcf81f5c69a45d5b4252eabf280d671587a4e0edc72e66dbb201c47271ded68479d2be9636c596968b17a02e2dc679a23d3019392c1dccd01e7b2bc781d0a26c206158834d88b251ff5e1ed059cb5fe6694914389f280651a38d4677088c8852c756a219b4b0f8126d36dad89c1027180760775602c063e144d5724461ee3a5474f0114c70239193a9a45bb9a80185ecb4569b278ca5275953483806f839e19d49716f6519b40edca8615c81626419666244102905cf4972e34a02d257d7a9775c85e0d81351a4e297ea00b8db71f129de351df5fc1cae7d559e8446f3bfaab14a7ced537a499331e5d815fdf43e8da6bb62a1d3641866737c15124ef9853a7b970491e0827431fcb20aa37b5ad6817064c7d3db39446000403109327b4dc68d61e5bc7498a5cd432fccaa06540e312f36371146af31f155ee52397289cd42b4a6c241459ee64a0a497f02af538628532dba8fe1f6829d68d995d9bf5b9e5566ee21ec2a34946f5c7497fba2b72b92589fa9425339f11a7af73d428b6d18e984c16484d98a5949963faa48055618a543f3fbcc99ac83eb51c5e8e803626b0b89f4b398ea44f91362ed0943dc56f0bf0f286955105c3519522c472e880a24998cad1672064bcd60a593dc6604853b872695a31881e23a59047e160f5641b900b307e69267851d8047bca644f1fe034a841ccbe922a05acd2014a16ceed0143c009c88c28f010f2cab1270f98e12728a97ae2452b73ba0a32ed165a5c6c3640ec0f16d2f2d70c3a9d0b2c4e43996f5a50d89e4c542a5b0b9b4bacacdebb803a551fa362fd915e4eda0f8fa896736be4bc0dc4067b52d7d7110a329d2431f98e72b51b979a7b109797b44705100b2f474d6a04302806f626dfa05403d84590933e31f7e012e6f0b58b3a06990b9442df932af39b30b10ee03aa5a5cc907ed510afd3a6808ee051a93e2279752c5888f41d1bb7fd388960a5c8287565ac3cecb40cd94f153cd855706a3aae1329d6281dd0cf5820842cfe44617df838c3de594287f5a161ac89514099cccc9d775a92e904502fdd0a4c52bb92a1a4558149d5425038731f11553a50955e2ca01c28fa099b4decab3409fde542ca1b8c815e9554c5814228fc899c0c59d3d8986ed36a43467ad8f5041884b49090b05b2cc908a70968e515016c00e1deb75cc5ef036ba28f46e641674ff092926ba78221a4691991dc4a5d0236d8a1d19fc009eea7cc99969576d703089d785b02664f7271348b227fba51a5dc3314f9e84ea964bd6a873d9212b1c3f08038735684be25c4087b6288fcf10d3b8ee499b62b230958178a411e5101446e24474ef710438a2d340b8c85c225e8365a4f3202386af2a31674580de432931b685a102148f96f489de602d7aa6514f8f0e69cb4c6a2a9ca85520899cf18097f306245ce83b834185884fc4483a226d165efef1be67b49926de78bf12e42fcc9647176d8012fd880730566b4050854322d2b75f98f03845c04c6a23611a780add2c2c22a50c405b84704861bf2802b573a55f6304868f9cf80081d415921c73003e3714684752050ba9a0a00198d6188f1857a08db221ae02714e96a39c51547318cf210758dbe65978d96b128ed105ed86003bc085a972a35c1359be8d8641b0538c14c89ec41d002e0ab356d99c8052670492a5399d5a634421bac8b135022d716d0565d79e153b6a268d94c3b51ada4a1a3f5c82c620728d8715691a03c030ed57a0860b158cc78faac12ed8928f674a65c6cd830a4583d025d828e9273bc133162a1cd28df808517eceba2d6a7bcf8f8526b349f514628ec378cdee55307164aa87904cc2fec0b3cbb1a829cb7bf0802a6dfbca0c84748f0e837490bb7b6c96a36344e74125b607d8581c6726a44a42dc978d442dee08ebf3cf3045db9ba3bd5603d41c17ad10a20cff224cd6915e558be50432e7db4299325e9b0cc324d5829b6d69a442c242c0e5e31321f1e230fc4074d43f5dc0471e5b0d58a66c8e94beea886f2a2456342d1e275144cd91863543c979a1c36d41e14eda74d07eb8c9b7b2552640ec414e953ea18f9014003ae46d28e96c02041bb58e4260340d4e2615ee462711deb749c2d5e8243ec7ee45c4d387c1150f661ebe2bc405e6f903d26870f36c3a468fed2a74836e0827a1a0971b9092c05cd8b071916c02e582d7747d59b7665fa0d9445ddea849ce721b3a3a7fca871556e981ccc901d7d1707a2f1c408dd2d8cd94f2195fa442ccdcbaed20e2cb57558981859af9de4307a65b90d174deca1247c58233844b1b4fc7235921439722e4484b21b2a65ec8b34af1e48d9d70a9054e93e0774fc0c222eed5413bcaa0b758de350b4bfe409754648a62f8a59f0169496643b53a87f6a19d098088879a91362da459a8b3f2a15839d02204687d990702f545efda4c2b187e42cf58c3dcd3605c1566624950484e367aea3fce9f932f06745a791a26199c022eb3857b6583bc03d87454db2b6c8fc3ad718a30c6b6c4ad74d59d64aac4bc7900aed7c7ba15e74f3f6d6455903b0f5a0174459f4a839318ba0919b8e54ead4a144a175b00c0cf2264f7f58333bb880687440c864b0ec014a11c5c88a686401d249c26f25a512d4fba420fbc8e403d8e191de876f24b29a19b4ce0948aacc67c383b6c20ac3cb0080d38d789df819695ce339b27adb94e6a3406f6435b2e01acf21b586d71cf59d1bdb5159587526af896e13e1a8810c927d4664d142128508c6b892c94130bd41186c0d1a542d5bc02b4022652ac56bd0b8a0da8f8e0d805bf0e81c9a61046b36d7e57984919b3de0d177b4b0349f2c100f52c174293a0634186bcc52a0a1e22b8a28692d4d0f27924b4397c005e07b8f12e5b94af0475178c162f1e3bb85385358568fb26413cb434007438e99ada0cc6a0075e762442a88ec32a911248091af8c96c7310995f0d9a651b77005cbc9939983b9ed399c34065d2301b343b0207a0c81a63ca1c572477330b21258749ac303302c530bdd19c869d51edab6342c044ffac51e5d6b71f1e4d236766e042ec71f8081ca5e30fd0953f18d61edbc5861bb2091d45526b1019e89a1d75ebe9c60fdd82992356478220442f9790d28d1788ba46f3637d8356ed44e74480e5be8c35e4b87629e444dafef1855241f9ade398933316f25674b57baebc58a4a71e1434cb978951cb6ba41805e2db7aac2534438d0822608f91410858010e25860c91c8fb48f347472860b7b80274249c8299669808d2c20610f50cac02488c05a43a36a066ae2fe29eef13909cc0ea1062d360f24101f4309c7b3248af13712758de25291bb82512353b8c3a32d59d863f7ae19fa0aa9cd4a2eec681d127a75acea800bb0239751acd99511f5358148986ed745b7a95324e1928d5abe0017ad20d3309a985c0dc38b756044664c448a3cc0a339998ea3009fc576468301d61d343535212dae9a0da1286c11466b0eeb4d932c96e4d44294963218324bcde845ab2cc300adbba2d4d6372e8d39acfe63ffda127c978605d98ad4d2683a4a008be811ca06ea16b1b4aa0af2ab691cab63d0335700a208482baa2beb621480adb3a9d86fda74c8b2d2e4c6e0a77a526cce7ed58548b30549f45f2b09ecd442a4cf6168cb687238eab93b28f7f047ef094ae462bfa8da7316791572a7af1f3f6467402e00e2c2e3fc988d85af40e37282a65b36b7329f4e591a0faab3aea5a60d70d2daa19c3462629164d893432181138c346c7bc319713d3bbd5a48438e8563c2ef29fe82b98d19dc3746dd65315278ba4ba9273d024cf3a80ed784e8b6b41dbd60c6a48944e3a1248ab5200841a36282839da065c5fdbc71d45f48d4e85217b204eb143c69465392643b2e7af4a2505484b4d8e0583c22468016e6f4f4ab59436004102b2ee0089a7c24c9a31bf4f1c19a4a42e40dac0cef2811f44f1b68598c08ec2f1eb0f2914f851fc7a462ad3d34353209a1d5e6fce83b5c0eb05978857a170967b7c14a41a7a1a0e54aa664ea45687065202c9ee40f8eacb0449294dd99420570131aaa3f4ac08715425dd4444055e1123f0b9e55456a52712afccfa65eec2c09ec732928d36a08bebe440799ab1dead2458e663296609b160204c1670828d055bb509dca059fd613e392e37c71713e552f9f95e7cb434d3263bfd258c93faad001c220e322e741ca0056c8b1a5278039c4ce0830cc2d7a3809be3263a7a1af7e002d38c8d38b9060c81712ddf8255337da5316e4d3fc005babd79547ea33258325e07537899ad59061c78b8c2ad5327c50ff36e844df9d6a7243b0ce6e424ea93b38c08535a5c8cd595472d9010dbee43b13223a4c2c4b2c9e0312583c91ea722f1678be38ccf2d3794f526cab3869f28a374b0611cbc63b9082b38e0e4cae678a0e8b0206826e7063433f08d6399a235dd830951e5c901c037615901f3dd6822c938061dd566c889dcbe2e550805b1ac80a1e5f524850bb5aa2a5df4c711f27c58b4c2984dbb3aea4e8496dad0ee9cbb5f30460c26e39fbc4ca10758525427688a51301652de451d01702ddf958de94a75d08d162c672358d22589acd019ace923a6c974766ed8617b35f72ab8e2096aa27dac3d456256cd84a44a8ad0ac5a1eb11c2a0d16089f02901b0b427b900ec97d64b06b2e6da2a164c79a6430bb29a5ed1ac494917a112965bc36092019af9805f578645700f009a83f0242b930a65fd1f30717207516f3e648911b66ed8e1a89e6d5f55ddd34b1f6432914c8fda1429572c29052d0ec583b3c91099d2ba84473ad6a3fe533badf654725425d4f22d06cf951e3dfa3aea6995738052c052d020a0e952bc62d188c995d57c080a116d91d14cb800014692e2a98361ace43c44563e828b960b50227735b9b4dc4fd34b9f7a03a1954c15757092aefce789d9977c28cb766851681f6953be85cd5bc3f02088ed2304d139422d6a32a1f68e07012f56061b9ccc098f58539164a9bd2cba02e40b052537354df96200e04def714185c5f506a8d3d892d1740279ba5d1466e97c8075352807b2f11ae707ac8460c9d845683f64672d8488922eb4c2ca0ea931f73d7025d85c7d1b1a48840b6d24ecef7fd07c61fb7acb63596d1db687fa08192abc7dbaea450ede078930ba510264ee4405464ea1039370ac411a1b878617fbc38a9963c873f74084ca7ad08f2cec9c46adfe16eb4713180bf3b8066258134336bd4b8d372d01efc34fa543275ab3a4716c52f3124238742250c4c34112ea4da348af000bf4ab1f5af91220036d6755a94695a1d96e4c7912724072a701081505534170b2463b3e1951dc952ef4a623c880c98ecc6582a7200048b75ad96807cbab3aabb18c9de5440542348d1961d9149a9f06f449caf6625bf45b190af9cd81466da782acdceb8e897e03c7d953de810be192bc242f3cde225f7d55130a872403cdd1428461f5b810592b5c88bca3c615605cb24ef7d883003cf2c112db242d552321c239cf1b20a0f2cb4f8f99e1cb9a0a7d7a251f1cd858bd74753551f1b1a26f5a578d102c99b7ec41b92eb9ef0baf9f285393792481bc0ddb809eb5a54cc7301736d49a513f4375750146540733a30f8b2190880c87c502e10c56169a0a42c946503392c5107502a6f914d61bcedcb59893af139ab00a08278a94cee3a62fc771e1d37dce38c8225051fa16317b7ee202b5fde800d065a6796d4692a22e2341d13f815993cf70e274eb98ab6c48d1a8ae55e04046f58afa5c032acfd186c597932ab55c260f9dcb0001966e46a46672a74f5861c92516cf95be206b46176b61d90308f75cd47080a8b1a49064ea19430edd11a028fd055690c3592b3b5fdcd787aee0b46bc9164b47aec71b5c0091b14c71f02a4b35eca345d58e34a54d1391bb7b1e4a4a6e1402a8997674b0b8b0f0388cbeb63cd737ab93a4fac0a66dd87153ec19218bb3ea6f452b0831f8657aa15e74be304cd193d3cc9d052bb885382bbd03f9c68f4c7f52d4c55a51f835166a9ba35f372e96e3c3636829d06ce6fe322bbc742c145858157bca2ee69633d76589be90a9251f8be3d16e5a533ec0e64d334fc469308ba0f7b160cc57821986fa2d56937c0b07800c6bae5690cca14359d2972b40b612b4ce470508201dea9d9e1462cb5ff89d39d3565b86b394e8515cf9603fad79d0578815d84aa89c8f5b9423f315399ec828323f64e4c2332571c3ee55897111ac8aec1658774764e5d881c46a3c0f1816b97a6840d3e9d2f64b294e64389fc97c16317bd4043b412b46bdbcc797c68e99ea915f446a13ac901975015169728c0bc7d3dc2a9399d638cd2493cb1aa062d6d94607f68c9444efe1e87250ab263d938a5ec6d2d916c2b46a1d5010b4a14479aca80088fad589240ba75183363bd5d7ad548c3882bf2139129411ed46571ab60dd5404e4b25aa9dd8216b2460cabee8d67eb496176fc01578737a0d4081f64387fa3d4672be74c554d3a1c4e917f01469546408651766541e80879015f2847b8628903550a4d5931c5034a924401e448584cd72abec925cb53c202ed55ce2d46ec54fadbea474c346093e683a9044b181eec0741c0d6d6c9e0d60d9559838d8414ec7f5e860123e990e38a6e8a1ec66818083c2f2e977998a5c5126199dc808a44f3af3d5bd887bee4454920d71c3afffca447643bbd4b798e8ca00fa8c3d8f902f960d8e0b1782e33d074c9c9a2d4680f652a399cbee1644a176cd5004545854bbdc5b0381ca4538bc7b0992c30adfb46a597263807a1c7e0b4012b08b6ce8f88bbebf96d1ca38bc0bed6fe278f819a0914c65428fee5067aac7c818ea2851152c6b60d1e774c8d5b7b058b80a320f583504656f4db38e0a386d344cf2bf142574ae2a3ada7d43270333a46011f162b6fb080ceb57c80beb1673dbf11b6b500824b17c9598349c1c240bca90db4f9998bb8c24bb3ecb4a97bed0cb4b0fa0d329bbd94de93b4f38ef1295e8220454c96f6e506ab027245809f18fe613268e0d726071a204043d046477259fdcd84ab8a619c315454058eda5eb10cd802a587979231e4d205e2358aca9426341599ef6349dade0fd9410f6156b91537971e4acde8a641f650cdee4b9f75ca736f505e9dbdb2f544d84cd9fd6042ada4858a060afb8692e6b98a7fd32d079a23a0219c78663afa585c8676fc474045650dec72752d6229645882399cc958e82f21a8c49ed858a2b16cb334253b9f084fd528142e7c0808505f0e6eca4aa93ce884da4db31c2c25a1093d347d27ee4b33c280d4535960d8c50d15f902ceee50d8796442b6e133c06fd42270f0d6415e5ab2ed4ba17368c7e47cb9b7671a954d3cab227275070e4848c76c2186758daaa4aadc7a0f0cb792ad2b0546820ca4164e80026ad05bec5c61c2018e3db182654789ff54f379160ed3336909dedccea487538ba962837d993dca71329e59fe995e9212ebd1dd2a7b726a209ca7c1d865cd418d97171fad1666a886279f0bad289ae85a57387a51fcc38043c90014027f2312777a8d3213309332babd8416c234f88405e1fb4e45667767c517860a21da8ba905b2654ba46a92c2cd0c8a5e5ca54391a173dfa0e54028bc68da49682d0d380f0603a9d5345ce4548af35fc49b28e8cbc5ec10aa1ef8e28af468c120b8993a05ef440149bfc2b91cb68b860e1d4f4b0654ee87cd5296bb17cce0ce44d28a0a76441fa49637664374b445e4bcccf0d0c2d34265da15ea04c279657944adb684a39832e3b4d2480a3cc0441e0c1462b0eead9e6b8dab464346e6e4d03d000b6c9195ce3519064359d02d473ac738e4249a84311e3e879cea8790a0f204135510232a4b0095fd40086f21946732c022faffac38f22390d81826e03a303bb2781886331d4aa7f30a07243add464e400450dbdf1d69912c03826560e3a115a6e00924e9cd59c6abf66de5a5a45cd1d34f092251828f24ab340bd0e70ef51435596af44866b3851e3b25c3133910b6a84617378b081f2f002f68268669b25833d23874b5e85679727cd3d60f1b4d4120a91b5e21c24d8ca4436906815ca11ec091c72fa996f643d6170f49bab9b7609f1fa9f407a1d63d488aba11022f70d9bbc1272eec155a75e8ca496955ffd3e060274071f2abac88806dbc4642513fa4a63e7a4a8d4d3251f219e38527200b942ec9837b91a569f3e2c9f4e9cba960f4deca7414cfa4124072dd6c2518712d184ad8368f925b8627d5417bd6ec3dbd217fccc7507378fd8381793ce614d5817aa91e59a42b9f88734ec01b138329821437a82180b2c52cd5b1761832c134f60b21a175060a3e2b1a17346582bb197cc47a6c7428ad7742734b75ce0429f4ce90fc8e6c1d2d64226b5ec0206045e0bc2a46ed124edd60760d9898357a71a238125c4823a2939435685db5d8b9210770d65cefccaac48799840d5d30a2c6916102e3c0614bc66b520d18ff40dd946a28c8d8349a8831285968178789545cc29ea122d3404357ca1cb1d144c60ef2085e526050f8d88ccd403f41a94e19a8c9d871b8e4cdc90a28d6460d2a672496a336c9b58ebde9e13d0e3205f15a83a933759b94a06d3935aacdc29ebd3916c8175b81fb26ee2ba807d824ad1023634cabd78a1b11d9c42201c0d156c8e46728451785d9bc614123b6b048d1322f36415e5bd69354d76b06a96903c111c5d57b0ebd2613d2e741e3a27cde51d725271a2236ba17999cb6a478ff45e21048357a0b2ec0368b8ee428af43264cef481a3933c0b889b238892c716a153abd7781dd92419909c4a5519f096dd84cc27d08f46144b52a3d2f2a36dc5a9c9526480017f0c68d1d7210decb348c9f76050b90805bb8ee75196abb5555a8f852c5f74c9ef728640691f11c01a85284f39d4a8b1f6220aaeed943e3ea894861380d33b1e3e79d8582a84cfa08109dbaa81886b619125db19c5a73be032c0f645b1d1c03f2cc19843df4fb970591f72c8d8e49e4a3d9ce1a53374d2d24e260db90a32b05cfd32a34d5801d09a20b0b59c1e8b0fc292a33f5918954bb550d25d2e09381846adba8b134b06216572bb2f232ee0129323ba539325c4067ac791338f7312f60f40b42e84614676da7895d340109317eccde9283cccb065afd8d81babac19d685489d2411180b872f54db7aa2a1655ce8c12631ad789215902c45a3a707343534832a185e2531a56d90edfadb21355908414517d9042b0b5ad1e4a704e968a716ebfbbcb9d14bdc2e3dcf4a74b618523e41049383a1a4e58ac6b4020650fbca757654203441a59c07aa4570cd138e3d51472e48050f4cc7f1042bf8a8c6a4af203286f5f4c25477b94164f1d46035030b393e4b0bac372abe3aaeb2372d89d721764b73d990d474c95548d89d02ad40194d1cdb5748f7f4da8203b77638921fcd3afecd1eba4ee3fbf32c8cd0b0801604612f40e9056ce447af938cb106d7f050331d530c58bc1f6b9905181d105123579d2689322f65453a29421b585a9b62b5910d6e2d6da5e77878b9b82a0f487a8cc41ecb0626e36746c13aa514783a158018b77475fb54a3be57f0d1fcf00e91aca379a249f860e643a7ae9943163779c5a647d7b39c026c037b3cc8701258b2933a0d1940769554652395c73925d5a18515a128024189945493bcacdaf483e5db64ddf001694a1d08c1e88b0e3848feb23d78f004a301ec0402fe6b50602d19ce49d082d1981f2d038d0a639ea8f27469c8cdde44fd593519f8d1e61763806ac21a1911b7253f1a271ea6781d69541b6e06a7961e38568ecf2fe6e441840679ca953f8dcf439513983b6cff595b5634aac4ad21178b2549880253959f409aa94ca460adf8cfe4a8f1e179b2e163334ce9a082e1274008ca4cb1e80f202703c9a631ff98a12a0551435e20cfd602b93fc09f43c8293f99db97531bae9676a5cc2f60845eaa9f6ccc1ddbd520b9bf004d47fe32b2493ff2c8900fd8f1972df9d7361ccdecffffffff4fe626c01343cf37828833ae26e48e6c21230046df845a2234bd72248742984613a8911a04171664d180b42a889720922261127e9059300bea6d62ab2385add494070920000230e224bc14a5412838550556a0f0d2c7a2c00f152398d0b113a09719e5b2cd96dc314874a0c4080b7b75970889fc43e6d05a000e6ab1dc54b1a40a971e3469f21acd312040c97d66da5b628a8a468a98f04f211e63c9504b61114f14464aaa3b80735725677eb460f882929ce543958c43483ceca522f000c89184d48f9cb45270f0a4b9028394d85f140670ac5414d24827a82c298027ce5985373a5c74908d81d4a1f0ac42500a2d4d4910b55e7b049078071966966f3c8512bb12a792204b00a08c71a09708d98a9867c6a793925751a5b761dca5bd346a8c89d052b17060824f2240253630c2326545a43d626f707c24bd284097a20964570f382271661884343d3840e0815406f36393200343d04e84eae082d6d3ac93910f7d005ea90ab536460059aa5822d2c6120428236b734646d921007994b2449096c803c511ab3669f40cc0432644944bcf131ccbb70c562c2b9648523196ecf540f3c68030441963770648190abdb3126fd4b86b6e85a826489026d200364b5dc0c8a0a4a12c6e930068dba422480743221fe0208ab2e991d446543f9421e3846645a3565eaa223ea4120a138dac02a60038b34b4d21195b0691118a0a03488c5a4c9602c3581d574162311ab125115448d67422b54208bbf4c60002e9ee073081801c07c51a1e4b8c9c4450dba0c746b0ad80211aa2986646f871852bc194536f89f8c6fe0644d890088c1af7d1eb244543016300be301456e09a5289922a332a71004a0128100011220573d4af244a1a788454f242692c8a052f14383813d8e13560ec2d45467926ab5191e09821a8a85d61ce1cf531f943208ae140921872246860087ac960e90d84b104d6392bb644c1d194080191af0f26bc9c45d70af9d19ac109859e372d89b31c44d218a9b1a3c76d421819055aaa5e6ae43920166838a9451035e7213d41def4003a98cc5c390132d5ce1c9a3b954346e7005522ad812b44434e81d9f290034f2b4279b07c12a371c58b544a033e2508bcd9b992a9b5317baf854ed49985ea0b195e6cb862a0394373e207172a941c32461a0161331721458db557b73468d1e4858929406a6545f6da5865b668a00a64254427e1165c84b72660e806cc9ad3c619c3c3481f3b81f46070f5c1c8da841b3dd2464031c546471c17ac89e54c19102e8f564067c451d5f7ecd5a0c5a164008e6b582e89d3e98c8b25196655efa442c61b546068d3890a8ae68e1472061c3850365cf396a4c9a22e93da0e3cdadeb318021a55c290aaad07a123552b607c8d3ab95103940452b28e138f535a704dcab48b4a0cb22cf1061c4a25a82604f9239524478f4c022e8cad88ff3845e5cac1c70b835b684ed48c6767a8b03a42c38eaed9b268897945b6c055124f71707a0e25bb29d479b01560ce9a54547af572f1f420c20189f0c5ac02372c1dc823a49786039c0c50b18a582561ac0f6bab4e582f25544577724d68350b478006476821adf472baa850a642ae54126a11a8936682b5ae978735c3e2a21ea4ece88ca950f3095629314d480485804e405aca39334305535e9f92a7a710239e630ea81381a35e894496d88c2013d049c9a2bb6221868ad1ab3f1f484032a64875e5d3afb83833f067471e8c1b1b521c28d3c76c031f66da0e0848840fecac68dd98687259818a9058111b71083acd21c3600c337057690a27b332b27ab5a27423c023149554e951341786e6288e9054413b0584b422ce28e0935b88ada10893c54a9125e9cba3445aac9089cd18b0b970f561faca83915e7e18f8985042734bd54c85658aaa4bcb5eb74e647a03620dc8012307fad84a402288065aa28e326294c599a362eb0e273a38af50f93dbff26419c0a5d0ade981b023347690b9a58478c6027250dc9db05556dc175189b8c123b01265756c2b98ccfab2e67a4ab1848b90252906081b2cdfbc1e18686871e081a0503b92c8af3a403439299315b6a00d9e57d3873fb24392a2a4c9d34a8f0ddf033aaed54901017a2cbc62a9e68a8b4c733804e9682ba5e6e2480a534028559b8e0ae1995057e106d90b383a29229561354a080d3a188c20e8144095061c658a9848c329949bda9352ad2615302365ce7800d494293174258855201570cc5a81c8025b8ed26ad0e1d012806c8c0d70a76894d40e0651b10cd5d2e5290b160f0470f1e1f44814188d08b5665d20fb617765acc7a5066cc7a18c3f68025c2750ce8039e3c404ae415811ea660d4786573cc00e1150c12a481d3248c433547251da80b1901b1ba335618289410ae0cc19c2e9c1881684ce62bc7fb9907326a0152013167762f2487b63bcf2b1deb00ba564e4a2c558a31073a3e487d984eb194761705c2a2b3166925926a70d340b7e60bc90d82231408d0430edc8d209d5e4d6581006b0971c09bc801013514405843c9c1abddaa4864c5aa65147331a4952b7ce4cc1d0260a2526ed132e29d05e3f5eeea0b883e20c0e092b3eb004955973d4a60899d6808e1a4218e0adc993d3a42c54092a67faeef4d549e11be3c0038a3c0334a9d10128235516c24e8c4571570c24583b9a3242654ba72d037444e8b1a1ea005ad60450c9aed712316117083c6a9ae0636a4007b2248c04ed4864c9784050911c6b70205b6869f06302083e259058bdc95b2d62325a61aa28c038c97b21ca8e02309c6831113027890032c057211e0c08052bb8429aa7f6450d1826211c68ba2346c0aa44679e24212392aa10d551a705934a4567fd59a0e9928003495a651926cd4152a0849c3c37288b0aacca25671380237d6994386c80d045c78e8cec130a54c432222a5e69348c805e210aa0e50a191d8e643c6a778d819502ce0ebd0218aa8cf6f06a13d4030811c2811988cac6d23c916561fab4bc89a2a08fad3d1f1102887d29d892c40a202d74e438a88389062e025786af78ada97bb32b4c5aadb52673a863179060b97380e0892c326993953c71489c3949f39787d653c402044800ec3943c353daab297c967344cc80f506af051e24690b314c695350af2268c1e24810ad211bf0e2ce8aad5826367a00f9c82027968e154206a59db8c238be50f0a613001badf89662e688490960627047cb93e188ebd451865291cc9e1c68c4830ca0b76883c2a44000be144293030108806284468f66050fb9aae2619618980f090d12d9e0354880072f663e76ec998971bad6c1358b9754cd8c08a72e20ced0cec4f8d6f0c00a744c40a3cc8a67aab156e345160666f2e0486993164db55156d996f8d3a644084e547fe009230d51b337204895b314b8deac69f124644911364a2f9061e8ba94c1a38501904a773bcab009b2293b408a0d0d9d207c71ccf82883868d99260258755923490d54804e276a385161767caa5989b8c8a7607c99128a8784039cc29490cd11b3344a120c229d9ca4f98349111b3c026e9c0d3b7978408a8584294f63852b07d4e2d24c8859b2d5e5540f392500beae0fc834c48aae3a521d22e9a9e352f350ae3e0124b56a8246819651ac882ab1c82d3b36058ca0829322ba8bd3073419264ad961e462ac92f4118b600e358da46492125d81e3520011c75e6e883c2c00c53d8a23618a4a82c0a6491a326c515a230c11b4a1008e2a5ec69845182859cbf07a9ab4b764ac8f0f32678492749aa800182b1dc2b2238083342604defce030c446298a226a9f435eecb410c47a9da571a955e47a81cd953ea35614f07396a153bb8187193d4e2c5d5571398360555a9429d729518e68a28bfc96078b604a8c4b9a97bf198a50b918bc641832bc106626a8ede0f7ea3a2a03940c80ad68a20c0d56052b7a1eeca111c5ad039535c74f9ac4e68ce86ad1c30e053f500ebc10874535661d4ce562a2e908e23abbd38e4a80ad407311c66e43a83d5fb127033943812967d6d620594449401a628b31551d2099b903a98c910b3045e2284264c9c41ba34149587c809f35474ad5a8ea19a1c6eb829b9a531b90abaaa429f03640419a02208e2e500235a5ac85173594c2c811e026232a46c842481bc0a0039544c21a7dbaa025422832626b14f2ad8c8e821259572c2cf9b2a9baa445830cd49b2675e7962e3473c92f13a1060c10904546b69194f313a9481f26c84ca0d4d47cb42a34cb8ed0aa42089c10f09e604143069d5e352df9c0f18961c00f9b804f4a040c27f9a1c0804a08d34d1b07a9d42ea92ae02848a7034a7bc721cb953b490525108cf12ac541d78c1b9e96f02a100b908c14310204f942e6231a93e5613813a1151c03ac0a203d08482b312640080656c760d30ebcb12aaa5e49a325e2281735f0300055872167b8ceec3832f94443571f3817a10c94b111a0423922e5cd8b393e52054a9549958c54630ef8604e8eeebc42c1638f45121699649d4d33499992a18c035190302940653d01276dd2a3b83845893b34bac4db860ab549ac9090310de6bc9aa3674c24246020b04265466b6d09dca707374e31c90c807ad651691350edd07025ae8564d5610ea22b63b888e1885586f987d1002b2e88717cfc30a5c38cfa74d46e72e5d8c3e0515ada0d353eb42a8a76150af04787f726886d0cad0499d6705577c17dd963a8572009a9184d78652cfb7b934103195a3d8464fac40208a05a8a065c65a090d4444a63edd115349fda785cd728c031e0017a7c9982f8487ac11769afacc823b9460918201aa3e992812b8e881407288570d005e74d0f118c91832baf1605f2c71d998f4d411238626121e603721d5306e6270f0a3345a65e64881a384af1072880d283105242079e04d2a407f33a90175443146f3acaca9bf00aca8b30406e8289c8bc38bf860148edb235e005934c4d4a7d11d0c1c3884605bc00827338df9c68fb62448dec529a2e3385de44ba0514705781354306e1d0944f561a6f9a46a1285e8c940911200f1e3c6777b104f49c38b80ecb269b0a2c95076ae86955a050010dbc2aed8213652c10205b7c756851b9b250d3cb03a827d50012a070a65073f4f6d28cf532cb34410b813e43494fb47cb97226254b904e463f3fc65e8020a20714a13e28d540612344c09070c6260629059960177336b55949a3f1a04aa0ec4d17392700a40203a00e972858540162ad4c88822385098b6483525d5a616133e18c418e4d9b68e5a1e963a70011357eca7aa4f1a09312e616428a83bb5265c6d86ca22196a4109a334804b4aaa757204c1419d499ad81c518faa4676d530b5599c03ca22344ab2445c41d4c095045629e390142c327116208b83ca114a42036ea89e4b811b1ea84590421b182218e6042dbe067230b9aa9506814c4b4126e5950bca34207fb189343c01a1225a88619263cc1453d1296b6840aa6cc4e1ccb5df0694f1a1b9889140b892203c6b48810448a6581011792203ceae3a51472119e9e30c73e2f9b35856e0952d5c15319860d708514d542d28858ed4441162346038e53344cabb2d45c70b26905a257172c20c3eea85831233a668c136989cbe9a80419a34974aa50f8d0480d8a0dbdd2caac0110f0e3c3a75b88ce76b160a3926154893495d46cc4388bf56cfc40d3c413850b981549c8689c416a32bab6a16a7954d9728b170160d9d85048d75eb5ad004953335b551570032746cb0f16197c967040d1811d63666c55af125b60608861c9568349243a7cc00be00a52cbb0e3c99b25b3a6401b55300d21d04b82a928c1099adc4c084304ef3509352604488a4d8630581bed727a6236c9f3e82cd48d32516a81e900f2e7500b0ed0a5100232e21cc1d111db0306822d3c4a8b0085190b302447af386da6490b551d1ed4f48c9cdd60d313d40586985d23197b5c98ba9420cd4e832bef90552f48fc7095a6098a18414ccef506a239556238afcc967413447fcc3ce1b283ab809e314e4e2e6ddad51e8434325b636a95329549c5a19b15695eb2256682d3112e00dea0ac4e438e9488204933cb21e994a016454551166c62c151561d687a46424d5800a54ae254d3ecabc69ddfdaa50499fe62b969fa92a3101ad10dd7ad32535a843732219310f25341059a405dfe067cfaa3a896314d03f8c19719291238ec593b83709505404a9d2877983c81af951694412dfca0638326898b1b265c8afa10cc1830a2898d0f2e74781002c448e563298204648d37b3ce9a752fee6ea470d14004ae0a647e6f04a06811c8f9e13287d635c2df11017934a9b1f179f6aa216a6eb8f82a28503bf3c4541395a2ecb2250032174344942cc9260084a6f4b542e40244e200a3151a010da290f1b055560dd505885072c38c5460161d2d00e019336210529609118003213201717839bff628d0d58102910749155a00305d02b124600459456799121152de1500d1d02304da84f8b0c7d3093e1e7f288cb9613305532991b78a1693304722ceca043853a526650e8cf0d49c3c178106783212f0a25cd2628123c41448d2008e983148631c906b02469182a3e46189d3d3481edb191537cda74e3666fc49314292a2332052a5d912248665c52c09ac5a4f2c1f283292629cb4ecc0d1420b0e176a0d48b9f08b11e62c6cd7875a6a30c8a931354d4b75eb0922cd85ae2c6f948e44287d9232ca8cdc570cb118c8a75007d45042f0a0ea4af82359cca880811183150b4479b60634450c41e3072d0fb0cd9b340be812cdca5184860e490fc4c60aad72834a13a90b327c8cedf511f16116200e4d8c51903a3982d2c084c912194e6804064579d385038b2023e6983031a2898ab5e49c3a32065142f3a9535f0e64da9b526560e2bc7a9b8c7cb559c3a84fa5496f000c4092667c11c50c232c2d7439507244d318328d8e280eea9cf0f3a7c64f9c2abc63a63206ba66740a7b6dfd5883e93c422d120a9095eb15b3626296274bc50d5164f4f9527bd5a3c4a53d485855143d9a44c7d51a2664788ed143b4ca60f8f9232abf724c620848aa68a237611384076f06b0c676e346cdae14b4cc4fa4395b32902ca44201b0907d889420020c1da3f8c4800be9e09202192053c3a1b0834f1a833761b09f2032680a382932000908a60e3a5aca54e481d504c098a637152e4ce8f812e5f51cab04230f89002776996980ac33d424d0578010ac107822200a8371968244b5a0d0259e36a1695569ecb980c19e8c2f330e2da94c98cc3bec99a2097460b41989da02528515210e648004ea728169c367aa449d3e5c69e85421821863c17468e2244a1c398dd21649c805c2d1d9872356da24c0356cfc21d3c3ae8f800c6958d0e8f1eb0120d62c378c36316a82011303a3a44aae13246268719383019051c906668754edf559b3f1064396393a3868f4a983760b01a6356ae0b49cc86548edce1c0777621532b94c9a42c24c51a3316bca2b344f66234cf06255484c12b90473de3907635aa0d5c8c12b8d1d114c426d7d4e723c64aecdb91568850e340e74aa2ee0181a2b0e298bd4856ed685304600585d3ac165559a29030a6d3814c751999f166918dd0d7257c821674db2d509c8a6360c56f8d03327c8190e65320a71591345cd8e195550b488a462019b016d763690d561f1e1551e516a88908ae50259814d4c408e53591e2cb7504ada78d3242b75a708c08b0a209d82842af2a96a8290802f648e553a20c0c102a945794553183a6a4618a9c853e98d1c1a3c0c177a8025c14106e3a054c9839b31a8eeac201e8d98fe0900602b44884b599818a0104a93861a53ae052ad5d1b289c91f084e249a2085bdcd512063e5844115e3cc8b015a6944390842c84380235e929081e0a55749a76c8903c5c34a0105859a24f48814728f77026dc804e637a6850b91b65563684218c4ba950ac9223f67bb848caa2eb1dc8192e54aae8c1abb55047e84e2a409501f3d99e234893862413c3326c1986a41a62b75ac6dcd628d2ccdd759350a29b727154289d9c3010d9e26154bd234cdeaf247ce08194ad2b40030080a821d6289708dc1e30a0add1d45b448d4cda03b60e6876749a966ac23ed35168124510f6ed2e848b12ae021029a1e7ccf034de2a52c6249149564b1c985eae9c4031622c204a0cc7a8c1211e70c8d4b17b6a69b831b5126e4ce7f17c36f92c38a0ccd693b85eebdcbe7fb2aeff85ae89d140a3761577a07a546eff85aa8bb7ca3eef3ae0ac75357196def96ee1d5f0b65469dca6875dadad951d0b3b39b42f3946d0765b43ae5b0031370155cac4636202ca539841f1fb32c9d61d0e128d3a71e5e30a0b2b561d09530a72e4479031753090283a8cdf0102c21288e9d6005a295a1c5744a1f0678cb135c4ab64111a48c8c8098f3900a4e123486206b048d0f5f88a683823c22d099630b8d821a868ed405519a2f3234449b14e97542479e1fc9595382ca35757fcab8a0b931066c4786b5bb2e46e8ae842ead696a4dc9a0f46745410400523331b724a7fa24b142d86c19d2fb00ea140615bda26c72e06882820b04ae90e9c161c1a34f2d58371200b9197940328e21fb8d9cacd7914f610977b01b3befa02f8c77a774a5d3094a57afacc89625093b3b0f70c878edc9167899fd67c370818dcd4e0733ea8c3adb78da4ad9fedc094af55095be1d96f02b036d09bf32389d98525f2ad3998472f8cfe1cf1af2a663569149289dd117ea46a15946a19ed4922a64bab4e2ffddc97435e1da2ac4499d49e59d8d182c4efc21f1c783060b744b2d6f7f1e28305040c01f3200c53fc23f59172e5aad529902d5e952a446890afde183870e9c3567c070b932c5499222407ae8bd3b943768b43831a2839f0b7e2accb9f981c0004181017ec6fc80f9f9f2c3f6ca756b56ab54a43c69aa04699121407beec85953c6cb95294e8e00d1933a9577335aa420d1e102dd843e0ffa2ce8b39666d667ecd847d8a775dd8aa5cad4274d65502b9724394a44e8cf9e3a6ed088d962054a92203d62289e8e1a3158940091a18e8f5b043e0a0cf041f241e373c647acc807c8ebafd6ab55a63e71ca44e911a2417ff4cc6993a68c972c549e283112a447fce62973cc607142c4870d16e62438d8a3608fda9e00637b8c7b847b585e7fb45ca91ae529d3243d8b1005e25307ce9a3260ba64a9f2648911215518da4ceac1a127460b14243e64b09b7b8bd020015b81007ac4f48011f504f55cd72d59ae50950ae50913a5488b0c01da43c74d9a315eb45481b2c488901ed3a77b27238f8a121e34d4c1b5313890670d2dcf591e007988ecaeebac56d6509d2c455224888f1d3868c678c942a50992213ef4a4cfa632079e1a30569808b1c102dd0407780ce04933c33366c413c4735db866b1b28ef2a4a9d2234584fee8a9e3260d992e58a6384142e4075ffce63175dcb171c7458a3b223a64b0a3737bb0e080da1d34b3b03bc43bc05e7fb662b54a450ad4264b9114150ad407cf9c376ace84e142645516294c8e0cf1418d3695773460ac3021c2c3053b15ecc0b5d96170c04edad9d8d1ced00ebbeb5a0b962a529f385d82b4a810a03d75ce84c942c5c991213d2404d283772871c8a8e302451d11753e68a81027c181813a6864c73ac23afd674b562b55a74071aae4481121407aecc45963468c972b519c241902a44a470f7e32933866b44851e2c3063a15e2da1a20584ba3731666a413a4d3bbfc68bd52756ad4a7399ae65c8ab4a8d01f3c72d694f972e54992207c3cf2963966b44841c203060a73263898a360ceda9c0173cae680cd11cef95fad57a84675ba144911a13e78e4b84943260c172b529c2421e2430f8636973a6cc468914204073917e4d4c9919333410e03363432394639413999d775562b53a13a618ac408511c427deec05943a64b9c2a509208e183f3963866b03051760348080d7671e6243430906616072cce57afbf5aaeaca2fa2e3d4214a84f9e3a6fd69809d325cb94274a88f8d883f35c1d3662ac380182c30538757070263c8083008e1a9c0170c4e0148370be0b97ac55a6406da21449d1a03f79e8b44943a6cb15284cd22019d2434fbac3cd41e3850a13203860a01027a1c19b036fd4dea4057843f606c09baf37adcb562c55a53c619ae46691a13f7ae8b84113860b162a4e921811e2237e9f269ccb1c3462b4301182839d5cdb023637696e029489b931c29d20730374d3ba6ec162654a94274c9216190ac4070f1d386bc894510fb509d3058b942544aa541e50667b23c68ab23c282644b4d96027e7d606020235016dc28c6d826d5a971f2d5656519d2a39525428109f3a6fd088f992858a93236c84b0f941e591e7e6984186058a111cd860603337818d83046cd4d0ccc28a6c826cfaafd6ab55a64275aa14499121407ceabc4953a60b16296b9cac51b24648750a6d2671c85861e243060b736f795fda1cac51b0b6266d8d803562c535c05e7fb55ca52a158a13a6488d120df2a3a78e1b3566c070b122850912223ff8e2378fa983468b14243c60a0530357a63726c1c101350504a8315323a686a806d82b972d59ad4e8d02d5c95224468704f1a103870d1a315db05489c2e4c8901f7a705e738983868b1527caa8ab4070c09066eead8d411a036a6906a4294b239686184cd3bcaeb560a542630acd284f98263d5234a84f1e396cd0984123460b95264680ecf1d8e1e24033a3c589101aeae0d04440a3604d009a312295139ace552b962a527d9926312a04288f9c3769ca74c952450a932241aad3d799c421a3458a33264274c050e1cc5b840507906967cec6ce18cf00cf5cd72d59abaca54279d2440952a34483fce03103678d19305cac4871b284c80f3e18ea4cde98f142c5093324cc78c850416ec2830506cca4051833033634c3eac235cb55aa52a13c6192c4a810203e78e2b4495306cc162b51941401c2074777e0ddd170a1a204880c14de222c28b346a04c803262658e65866582bcfc66b13a450a94264a9014110294874e59ac07ce9a3260b65891c2c408992064f478ec60266fc45051828c070d15e2243c584086adc08033313247324432c05ed75ab0565945f565aa0469d121427df4d881b3e6cc982e57a63c51420448751a6526ef6ac04031a2438631766328b835303006cd6c0cd898af31b9975f2d57a94e85e28469d223458502e9b103474c1a3166c26ca1e2e448103ef8d954e690b16204070b7212c42c10c326064d8c99180062886282bb70c55a550ad4264b65502d8f141512d4074f983861d290f192654a932341f8a64f3c1d345ca410b1210c86ba307061da16b0a19585390ac35c172e58a844710243e99122427ff8d879b3e60c982c55a2343922c4871e8fa7abe3860c1760509000018603980c76746f6d0d1020d1ccc08881218201f2bada82c50a15a9be4c93187d41f4a5509f3b72d694f972050a92203da679ca3b1a5f5898f88081ce2dc282035fd4027c21fb62fc12ece537abd529509d2e454a34a80f9e386bca84d142c50992205eaad3d799cc11238517111c2ac4b53140e085ad8017332f62452fc05eb96ccd72a5aa549fa6498c0a75e9a367ce1b345dca80d132a5099220d56974c7f2ae4617172a4a7cd0603737e14117055d0c140830d6c5d845d8e57fb55ca522054a9325498d120dfab3670e1b325db854717204889e346fa983868b14223c68a83027c10197045c905c04907101c085c805d8ebef962c57a8487dd254e991a24281fad891d3060d992e579e28290244ef752a6fc8686122c4860b746f11b63440a0b625c096b12dc62d2cafebac56a74271b2f4689116425afe68c9a3658e963566be70b102458911213ef4a479cc36878c155a4878c05007d7c640cb012d6b04b49c8d8111b5047bfdd97ab5ea54284f9626394224888f1d386cd08ce16225ca122341f6e0275e961c345ea420e121435d9c040709b2ac6519709685ec98459825ebba158b152a52a03a65aa1489d1a1417ff2c86983668c972c529a2019d283ca63075e12cbdd8c16264068b0203701c2022c6b6865580060196201f2c225cb552a527d9a2c414a3408901e3a6ed48ce172250b95274786fca04a9dae0e1b31548ce890a14e4ec2150707ae10b8520676e5eb0aabeb56ac55a7406dc21469d120407beabc4123c64b162a51981c19d2c38a1e8f1deedd0c17565690e86061ce8a042b0dacb011b0722666c56845d8eb3aab957554274b90aa2caa7208d01e3b70d69819e3258b9426488654f5a651e7b2bd116345090f18e8de222c58ab8256254095b12ac62ac05ed7ffecc9a8dc8aa56ad4a74c931811f293670e19953565fe0b4f46e58b16322a55a844514265c8ffbe3d7ab470d2edd18ca96f9519a5be55ea5bdd7b9f4fb70ab76e7509b7d578ea84f7dc673c75e1bd3ca50bef495b78af19c3ef5e1586dfbde5137ef7f2e0776f09bf7bcff7dd5bbeefbb176da1ef1edef3dddb6b4e9d7beed4b9077d9753e79e0b3bf7f6f6f2a2ce3d66d4b9e7469d7b7bef32eadc8b3af79e4fe7def2e9dc73f19c7bcd78e9dc7397ce3dd8954e9dcd3da9b3b917759b7b4fd76deec56d73cf7db6cdbdd8dd8ba7ee9e0b7d954f776f093bddbd65d4cd73dd3d772fe9eeb97b4f77daba7b70ebee455f69ebeeed55db3d57afdd73e369bb279db67bcd69bb07bf4e186ef7a02d1edcee51a3cf76eff96cf7f29eed5eec6cf7a2ef9ecb6cabaf1e94fa7cf5de68ab7cbe7a4be7abe72a9dafdeb355be7a4ba79e53efd9c64ebd674b9d3af562e7d4a9d74e9d7a532aecd48b3a9faf53cf65b671d4a9e7eaedb94ebd2afcc64aa7d2a917475b38e9d4d38bb6b01b4f5dbd2aecea45df6a1276f55caa5eecea3d9d4d572f7e525bbd78daeac1afb4d56b4e9dd1562fdaeab97acf564fcfa5325b3d2895aa6cf5e268ab6cf5a653f7e94ea954774a75993035da26616aec4ea554d8954aa9d1574a5d3edfa694fa74ba7a51eab3a54ea9512ad58d526367940a855f3c67941aa52e9f2f5e940a3b5bbc28f55d46a954f7a98c52a3d4f6f974e124945a7d32a9aeb41abf7b713c75eec551e75eec2a9d7b71358eba7bb1abf774f762f719c3d4aa9b0753e3a71b8d52e3364a8da9b19ba4c655386ec654388ea754388e5d29940ac7d527158e5d2a1ce729a970ec3e93d4178eab4de90bc752271c439d709ca774c2b19b74c2f1b375c2f132eac2d5168e9fae5e0bc7cd690bc7d2168e992d1c2b9d78782d1c57df38baf7c6b0bbf7c64bb8dd7be378ea84a96ffc6ce3379e4edfd89d3adf187ee377d946e137769f52e91b4bdfb819bf6ffc7cdff88d5d2a33fac66ef48da16ffc46ddbc378edda7f38ddd3786dde772f9c6d576f9c6af52d926df389eb66fec3e63671c2f5dd8194b9d7135ea8c5da83386dd24d419c74f670cbb4c67ec2e9d71dc8c93506a33de7be166ecea39e166ecba70338e9b4ceadb8ca7ce66ec366317769b71157e5da8db8ce3b619e745db661cf3b9713c756328ecc6d1a81bc7d1367e3edd18daba7153d9bab193af8ddf378edbd88da76d9c376d6357dac679d136ce7bb6711c759f6dfc74b631b38ddd679eb28da554b83a85beb1145a9d3edf24b43a6d2a63e7de941975ee4da1cebd698ce7dc9bc6aebb37855b776f0a85dbbda9b4dd9b4aa1eddede943a8d9555570fa64e612a4c9dba4c983aa54e5d983a75a12e2ca54eabb02ba54ea7d568943a8d52a7b00b8552a7b1933a855b26750abbee72499d3695d469bb97a44ea953f7598da7f1140f8f1a4fe3a8bba4c653f7c96446f39c703c8d5d178ea755a6abf7c6d3e7fbc65337fac6d3a6b285bef1344ff9c6d33ca5339ebe52379e42dd789ad7baf1b4dac653d8d9c65397d9c6533c3ce7b28da7d3a70bc34e2a8fa9dbc375a93ca62edc52794cabd22933ea84a5d269b585a5d3f8954e63f8954ea3ef2b9dc2efd3f94aa7b1533a75f39cd229ec2e9dd2a9ab7436a5d3e7d3954e97ae2b9de6b5ae741add6ba5d367eb8c5be9d4855be9b41953dfa994fa4e9751ea3b7d52df690cbfd32a0cbfd3e8fb4e99d177ea465fbde73b7dbed3a5f3f94e61d7b97cbe53b87dbed318ef9db6cb779aa77ca7cfd6a97ca7b0ab54be5369fb4ef7dee8140fcf1975f3dae8344fd946a7cdf87da15337ef854edda7133a8de1163a6d4ea9cfe9defb8cb6ccf739853a9fd3a9bb743ea7cfa9d4759f5368fb9c26dbe7f4f9a4e64d63d899378d3af3a6cfa7336feacc9b4eddb8cd9bf239a76edc5263e7147661e79419754ea74fe7b4fa7c3aa7794fe7348eba4fe734ca744e5da6eb9cba4be7344fe99c569b53f78987476d4e9954b83995429d7073eaeac5f0db9c4adfe6347edfe6d47d9bd33ce5db9cc2ae9eb339953a9b5337ea6c4e974fd7d99ce2e13961b7397d3edde674e936a72e1ce3b5cde9debb6c9b53293576a7ee338edda9ab9c46dd29fc3aa3ee146ea34ea83b7d3edd699eeb4e97ae3badc6ce377661a53b9dc6ad3b6ddd69920a2ba751d7859553690b2ba731f5554ea3af530abfcae9147ea753a7d2a99c3edfb7a99cba50b7a99cba515739554e5db8554ea3ad725a6da7ae5e3b7599ed94da4e5d25b59d3a9bd3376ea755bca9336ea7cb683b9dc6d4673b8d9fed34fa429fed74d93edbe9d3d94e5dbc763a9dbad1653b9d2edbe9b385a970b5dd8b52e12815769f54984985dd251586dd64920a3fdbea324a7de12693fac2ae3b7d61e8fbc2ae33ef7d61bcf7855d65b485be70ece6295f389e52932f1cbfed0b2fdb17865d3d27fc6ca9b113ce9b3a61f729953ae1f875c2cca8135e3a994e386e329db0fbc473c22e73e984dda5136e2a9db0ab74c2d1379974c249270c6dc6d526bce73af5f4a254178e5b1776c62e1c4f9d5317769f492855397561386e5da90bc35157faba701c75e165d485975127d4855d18ae3e5d18cf753e5dd885f9daa70bc72ecc7461774ac5736168ac749f782e0c2f5dd8859f6ed285a3efd485e1b7470befb570d4d56b61d86db6b00bb7f0d2296d6129b485abcf670b479d2dfc74b6f0eb5cb6709eb2855d650b47df57a96ce1d8994cb6b01bbf541e3d9c541eb193ca234fe9a4f288bece78ea5279c06e0cb7541e506a1c6da93ca83c964e680b57a5eeb3470faa34a64a9b53aad49dc254a93b7da530555a8d9d30555a954aa9d2a7534a95369551aa34eacc9342a952e9932a7d3ea952aa74498d5f580abbd2e90b4bab31defb425f581a2bdd1d2f2c8d9d782f2c75a1ee33f9c252b787139646dfa9139656e116ea84a5eed2094ba5b08be7c2d258e95cbab0d49d3a5b58ea2a9db1b285a5b0bbdc7ba5d51876f55ea9f48ddbf895c6d357ea3e93ce2955fa4a9fef2bad465fe932fa4a5da5f3f94adde72b85dbe72b8ddd570a755fa9cb8c529f5226f529cd53529f5297b9749f5269b57d4addb87d4a9fd2d88db64f69dba38753da54c64ee9f38da74ea92b754a61d78dbe4e695ed4298ddfa753fa7c3aa5d1d7653aa54ca7d455ba4ea90b5363a553aa7446954ee91b6d9dd267ab744eab4da93b6de1a6f48ddfa6d48dbe4d69ec4aa3ef1476a571ac74f160570abb52a92b759fd2d7953e9dcaa82badc6cf36ea4aa3ae34da2a9d4f571a47db69eb4ae1d695c6d468eb4a5d65eb4a5dbd56ea4ea9ad342a7dbe70ec8c5b69336ea531dc4ae317865b691edc4ae3a8dbc2ad1476a5ad14769b71b4953edd682b6d97d1560a7db65257d93e5b29ec94bacfe5b295c6ca56ea46df1eef1b759955be1776a37b6f74ef85f5de67abf7ba4be734a6beb01b535ff87d9731f56dbed1b62985df38ea3ae137da32e17709bfcb377626e1f775a7af54ea94be5157fa569fd1f78dbecee8eb465f57197d6327f4553aa5d0eaf375f59ccf77f97c9fced7553af1f05ed8c5c37bf1f05ef85dbeb09b7c63e51b6d5de50b5395ca37f9ba3055394dbe2ed3f94cbeb1d2dd917cab6ef2cd6bdf981a75a73055498deebdd1a7abf746a36efc46dfa83b7da3cd187ea3ee73f9be51d775a7ce37ea3e936fd4553aa3319f1b9deeb951d8d573a3cf168ea96e34bae736a56e341a2bdd27d38dba7aceb865bad1a5eb46f394d168359eb6d168d499276da3ae9eb38d36e32ab40abbf1b40ac57bbad32a349e52a14ae7940a85be2fdc9c52a150aa3ba542dda7724a85569fd227151a75e2e151a14d2515ea3af55ee894fa42a92f14fac64e2a8fe90b65469d51f885baf00b7db6f00b8dbe2fb41947dfe80b85c26fec8cbe5028f485569feef285ba4f67f285ba3035e67342972e9f13cae784ba796d3c7542a75327b40abb5327147e61d809759fca167642dd981a7542a154a813aa74429dd0e89bf77442e326d3195d3aa1ee73b97442976f1bbbd0e6d4855661d885c64ed8855695b00ba5465d28141a3f5d6875f93e5de8b35dba5065dcba5017a64e5b171a6d5de8debb6c5da8dba385c62d94cf09c72db419b7d067eb84ddb8853695f1b4854ea72d346fea84e1161a479d700b8d3a9949b885ba4f690b8dbece69b485becc680b75a1d116fa6ca1d016eae6b5d0a8b3853e9d2d74e96ca1b11b65b650a593d942972dd4854e954eb8faacc2543eeab3a98c529f701ba53e9f2eb36532a9cf6a9e92fa7cb6d5f7e9eabd4f9719a5becf25f57d5661378edfa7f47d9fcce8fbac3edde8fbcc53be4fd88da7c9f7196ddfe7b27d9f4bb8ede17c3ee3d8f98ca7ce67f4753ef3a2ce6712ea7cba79ce678ce77cba78cea73b8dba4ae7b31ac36ed2f9845be7f3e9eab94fd88d63f7197de156fabacf69f4197da153a7fb5c32dd678ce73e5d98aad7ba4fbcd67dc2eeb2759fd51eed1376abed337652a77bed33bad73ef7da67abd73eabf0324a6d9f4d26b57d469d71fb94beb1b47dbacc96ca8cb64f29b47dba79edb3ba6c9fb0bb5cb64ff84db6cfa79b6c9fc9f6b9f746f3f2946f34cfc57346f35c379ae7dc73e645dd3876e6b9b1332fea3695b1332f76e64da7ce3ce8bb64469d79cba833afea4697cebca5332f4fe9cc93529bcad8cd93ba6e5ed475f3de176e99d1362f9e5277e445a93be0d719a5ee709fcc287587144adde12a9dd41d2e7349dd517d7754dd284c7d77b879ef8ea8f3dd917c773cdd3d7747f5f974774ca3adbb634a7d9fadbba3478f7607dcee805b2ab3dd11bb79ca7647157ef59c503776ba71fb7ca7ce65b47d4e9dd11776469d6eecd48b3adf1876e368d4f97cb6f134ea7461a7741a75469d6eb49d469d55d849e51175c654a9f28d469d6edc42a3ce2a4cdd1175c64a77473476bacfb61975c64ae7132fea8ca32d5ed45985e329751975c2efbb8c3a5de8ab8c3a9d4d65d4e94e9f5429d40975ba512a14ea74a751170a75bad3a91b7d3e9dcf96e93e9fcee7d3e93e97adbb74465fa5d3553a974e38596d2ea36d724a6dc630b55985616ad37d2e616a338e529b559819a536972ebc8c529bf00ba5369bd1164a6dba4c6a33496dc2d4b7f974e3b7194fdfe6f46dba53f86dc22f0cbfcd28fc365de9db5cbe6fb3ea46dfa60b7d9bcd6abb743edfa6fb7c9bcbe7dbacba79cab7594d26dfa60b3b9b5267338e3a9bcfa7b3d9643a9b2fec36a36e53fa749bb10b7dbacdf7f9749b4de6db74994bb759d56b36e367eb3697addb5cc26d8fb699b7d7365dbdb6e9eac16db33985dbe6126e9bf09b84db66552a6d9bcb68db84dd780a6d9bb00b85b64dbc67db849d6d337626db26b4ca8c5299701b3ba9ccf8e9a432dda593ca9c4e5d2a7309b754e6de0b33a5d41766566157f9c2ccd80933f15c27cc5c3a6166d47561663576eab530733a6d61a62b6d61661c6d61a6ab6c6166b28599d1bd9729754ea72ff3e94aa72fb37d3e9df0cb74a7efcb5cbe2f73f97c99d3a9bb5cbeccb87d992ef5c9dc7b9f4c57ef7d32abf0fb6442dff7c97493ef93e94ea550e793e92e9d4fe674da3e992edc3e99cc65fb64ba7a4ee61376325d26d37d32994e668ce764c6d1d6c98ca76f73ea32dda8931a7599ee338eba4c178ebacc29d51975994fb747cb7499d496c974e396299db6cc69cb8ca32e146e994ae7136e99517799845ba6cb6ce196296d992eb4655661f7f96c9955bef6d932a3ce9619bbcc65cbacc2543c57d946a354bca74bc58b9d54182fbaf7c278f1f485f1982f8ce732a34e186f19759d305e73eac2787114efe964b6301edf8b07bbf1f4c57b3aa7d3170f4a855f3c374a8de1172f0f7ef1629709bf78f1d4855fbcf87df196ef8bb7f73e9f2fdef2f9e23d9d2f1ef3c59b52952f1eb47df1aa4fbce694fac45b3aa94f3cd8dd7b9f78f0fb3ef1f294ef132f6edf279ed4f9c4c373dd271efceab54fbcb87de235a7ed13cf85b64f3c66fbc4e373e239f162bc67ab9c3af1a0b013cf859d78b0cb8c3af1dca8136f1975e2419f4e3cf7e9c473e2c16ed289b7e7e2c1ae9e8bf76cf59cf1d4c573955117cf7db65017efe9ba784bd7c5735dbc670bbb4b17af0ab75438e9e2b9b1137ef55a3c2ad319b778b0db8c5bbc671bb778718bd7e241e116af3a6de3688b5755465bbc688b37a5bed016affa74425b3c17dae23d9f2d5e3376b67871d4d9e2b9cb16af1a3fa9ca16cf55b6784a279c6cf1f65e38e9565db7eab670ccc7e74e5f299f1b75b64f299fbb8cb6523e3776f2b94f279fcbe7ba53a71276f95c774a55f2b9eed28d9d4a3ed79d4e5d259febba7acf57cf75f35e3df7e9d4735d65d45d3a9bb1ebbaaeb2da4ea7ee0bbbeef2855d77dac26e0bbb52279587fb6ca93c5c77f94e9d52d78dba51b7a974c25137ea56df1d51d75546db66d475a34e66d475992d33ea5699b01b75ab5037eaba53f85d46dda8bb8cbab0bbf74aa16ef57d4aa16ef475c62fd4755d650b27a16e75f926a16e1edc26a1ae3b7dba6eec7cbaf01bb77a4da9fb74e3a8d209c3eef2e9eebdc9a7d36532dd187ef1f05c376e95d4a51b3b97aecb5cba55a5fb36956e75dabaf1b475ab4fe7b4759b31dcba51b875ab4fb875dd27b3756197d9ba7870bbb7acc6d46553195397f194ba74a3d465124a5de629a9cb2abc8452e1a5f485975227bccc8b3ae1e5f3e98497319e135e425b175eeeb5f052e98ca72dbc6ca12dbc8cbecc165ec6782dbc74f5dea53b7d97f0fb2ee137fa2eab6e5ef47ddf65d4d9943edfe5f3f92e99ef52f92ee13756becb6a8f1e4e3de7d28d6157cfb9749dcb6afb944e9dcba973399d3aa153e7b239754e9dcbe5d4b98c61e712a6b6d337ea5cc26e34ea5cb650a8fb742eabb133ca742e9f6dec643a97b1eb5c3edda9eb5cba4ae7b20ab7cee532fa425be7f2d93a97d01666b6cea51bbf7aeed2553af5dc65ac746377597de178ea2e61672b9dbacbbca9bb8c6177eabad5982a7597cd58ea2e63b895bacb6ad45dc6d367eb8cba4b77ea84bacba5ebc24c7759c573dd69d4c57397b0eb2ee157e93a95ee1276935057e92edd65ab749753279c74976edc26dde5b37597d57619dd6b97b0bb6c4edb2533ea84dbe5d25546dba514da2e5d660b6d97cb76e9c65465f47536a754a5ab0753a94ae594fa3a61aa524a554aa94a17a6c64faad24d5295cf369e566165155636a75458d95446a9b0d27dba532715563e5d2aac7cb64e2515562ef75e18564ea72fac94beb052e98c9db0523a75c24a17ea8495b19ba774c2ca3d1756e6b92eac54425b1756567bb4b0f2b9d7c24ab8396d61a51b6d6125b48595315e0b2bdde7b2859579ca1656baf15b7d9551e794fa2add294c7d95ee534a7d9555b8a9a4becae81bc7af329ebe4af855baf0ab7cb6f0abac4a5f25ec4aa5aff2f9becabcf755bacaa8532ffa2add68f455465f25148e95d465f455c6b0ab8cbeca6ab47da1d057a974425f650cbf78d057e9425fa51b7526a1af320aa53e5f25fc3e5f65d4f92a9fce57e9ba78786f4c8dbeca57194fdb57d954c6d4a71276e329f5a99442a94f6592fa5446f7dea772497d9fca66fc3e95ccf7a9cc53be4fa5d4f9542a9d51e75399843a9f4a3c3ce75399a7743e95ca3cd87d2aa3adfb5456dba772af7d2aab784eb87d2aa5ed53f974ea3995d5d8a98cba3d9cb153e93e95b053197da16d34ea54c22dd3a98cc2d4a6322f4a6d2add679e92da542a9dd1176e2adde9fb3695aed2f9369555370a75369578aeb3a98cba794a6753194fdda6b20ac36e5389e73695eed4759b4a69db54bad3b6a974996e2c6d9bcae9db3695af530a6d9bcabc67db54c2ceb609bbca67ebc2aeb20a53e3a8ab545661f7e92a9951e7d3e92a5dbd96da2ae32693da2a9555f865ba71ab6c2ae356c97c9f6ddc2ae356f96ca9d356e93ee369ab7ca3ce69ab6c4e5ba53b6d9531dc2aab30dc2a63570ab7ca25dc2aa36f126e9570ab8c3adb58da2a9f6dec94b6cae85b8db6ca69ab176d95ee1b6d95d1ea32da2a9f6db455465ba514da2a5bd885b64ae8b355e629a7d458d92add67b255469dc956e932db64b255f6e8414dc26e954a4d56a7d4e474ea469d536a724a4dc653264c4d46dd254c4dc26e5e949a749fcb283519bbcf273599f7a426934f6af27db67878d46475b9a426dd6732494d36a7543819dd7be1249c84be2f9c747b445f3819753e5fa5f285937953279c4ce2b94e3899a774c2c9e4b275e1a4abd7c2c9e9b48593cb680b27a3ce164ec6c9ea9be47b93c9e474fa26995127fc269379ef9b9c52a36fd28dbec924f44d3e9d6fb2ba7c9371fb269770fb269b53ea3309539f4997fa4cba53eafb4c42dfe9f47d2697effb4c2ea3ce6732af759fc978da3e93b19b176d9f4917da3e93eeb27d26f394edd3996cc6b033b9849d496772197526dd3c6772e94cba4f6555e94c2ea36fd54d56e3a99b8c93f1d34dba7aaef3e9265d3cd74dbad598dabac968eb269bcad64dbacf386e93b19b376d934ae7b44dbad336f97461b84d469d2d136e93d5a70bb749b84dc26e34da26db681b6d93ef9b84b6c9e7b34d56a3aff2d926996dd2652edb64f455b649f87dba4d659bac26dbe4d4c9d7eeb54f2ade5eeb42dfa9bbd756dbbdd6cd6bf7da77d92a5fbdb68d42a931b57d636a4b6de32995496d63e792da52e3d65d5263671cb7cba81bc76d9eb28d5b37769fcdb86da1cd78dac653e774dac64f6a3b9db6b1d2954edbbc699b07bf7bce186e63658b07b77b2f0cb7ae9e1386e1d665465b186e9751ea0bb75528dcc22f146e972e146ea170eb529970db465db86d5db8553a6325dc369570fb74c2d2d6759f52690bb7ae54da5699eff395b631dc4adb6ab4adc6ae932fdaba541ece38dab6d1a8bb378db6cf961a8db6aefbcc8bb6cf16ea8cb65598196ddd2733dac22e33dab6f0dbac3edb68bb74a754681b47a92fb465469d3d5a680b53ddb885b6b01b3fdb77fa6cdba6f4d9bad1f7d956dde7b37d3edba8eb3edbe7db7c95cf164a4d3e5b28dc3e5bf73975c2ce1676b65067ebba30b38da32eb37599edd2d9c278ed127ef1f05ad8d9e2e1b54a67f285ddd6553af5966dfca42edbd8095db6b1d2b95cb6cb651b4bdb653b9dba70ac6ce13756b6b1b255b64a673ca5beca76e954b6ae9eeb6c2a95ad52d9ba536ab25dc2d469b28d956d127ea7c9d6d57b93adfb4cb6d177196d936de79cd96794c3d475e1bacc367661d624c7ff1d278b228a4c4a8917767b3ca7b133c986e76c4ea7b00425dc9b043a09716d4af7d92aa76d645106fd12966e4b16454a1685cd7db2b51cc8a0a4fa2f614a4d924159b3248392e43f8c9341799bb66c1148b04ddd0e530e4b17eac624a06e4c62da8c9d32c89ec8f95fc020f25480889fd4a95319e5f016c36df1a448e6e48fcb6452a711a66d73cf4db1e7a620f19278beef0a6787e7fb9258017563122baaf19244deb47d91376d5f3cdf7785fb5c92b873a1ce1887db1b9b853aaa8dbc690bb5b3cb9bb6285cf8f5d0ebfff0218ebad029b37d86504e9d285c3784d49db641dfe51232262e9313677b633636e9bba4be344eb624d6cf6c9f786c2fd85cea4be307dcbccb41b6c4ed8793e026e14d0f4407152d5eb8d0d1ef353d8525b8c9178ea7716d51e71376a16043863d64089121438a439ed9216732c48993bf78e2ff26434e1c3284c890214e86fc4386341952448d2cc9a6ff7112dc24b8b541df25932cc9140306a653370a7d97d116766167a77497aff2c58b9208e14edbe68a0c77b02be56ba1cf367ef95aa83b6d93d369fbcc001747bdc0d742a77d87ec08dff19c1674dcc12f2c611a3ba11aa0cf76da3e3dc0ee8a258916b113ea3c91a76c6317cba7eba4e1017e611884cb6ca75317c574ea2a5f7845dc42e137c4b2edb074f56077eab6705d388472ea84dad94da36ede1bdbd94d63670bc7767631fc4aa5d4e99d0b6d61bc29356e6317ae32da3edb0e7193e974f7ee9e2d3556b62f5e94445319b7d2a9f3433c7d57b82ededdb4da4aa7d3f6a98c9b4ca7bbf7e245493495712b9d3a3f54e1376e97d116c5b38dbeceb8855da98a670b85df14d369fbf4d054c6ad74eafc30adb62bec9eae9efbbc8bbece58d94ea32d1cdbfd7ff0b791ff3c4e5604c37fbabfe1fef33859110cffedfe7efbcfe3644530fcc7f03780ff3c4e5604c3ffb3bf83ff799cac08867f0a4b80ddd879076d5d389e4e70992dfcc6d3094a783ac14d829b04bab8b6187ea552eab4cd85b630de04bc3737b7b9b7369984835b5278c3a36b7b9d12a66edcc24a097cee32ea9ca084a713aa703c75325d78c2cb845be504d85d3e9dd4e40417fa3627bc29a62e8dffc4931559f0a8fb64cbc0b6744297ee33d946412cdd67b29d3a3f445fe734eac67cee5d73da36efdab85db6b10bbd73a12d8ce72e5f14787378722d05bc38b8b937b79593c373e79214e8e05cb816ae0ecfadab8bc38bab53ba72ed5c52d9becdc5cde1c925e5e8dce8e45cb9389742dcd2cde573bb86401f1a4dcdaf21981ad39352c1f5192d7cd8386475f7e1d7284be9a3de949c585ed401ef2134fde8261f1e7d0d905a1d2204af2f4ae180ede547c7a168a1c0d66065740237951a919c84bb90820485e411ca667d2ad8527aacedaacd88fc263dd57ff03c8179025ee557a4a66436545dffb2844a932902d517bc18ca0ea2b87d1422086d28c5f141b4315dea4c9f263b8661b160f2f0365b7a4f63c8ac8d680d7202554ffa8ec5cb7bc4fe3498036f7e2155a1136ae528bbca10d76575b2e40731a0be2001150bc4cfa56c9769ad4b1582d24af8d858180a4e6437abb208c5e6f0694383d464494722579a32d5573d3fee688e1d3b424eb0dfa6253ad4aef65a9403d85d0f1a75a1205358578e1c7c8e81ac16f521504fb932236798f18350a081e5f254f0d78fc3db28b5bc8e23016d0bc459837806ca24780062e5a6b4840bfe38ca12d89ae45663c1d7f9ebd599c194767118691b2ba4bc0eaae39f7e9e1a5506042f54614f2b00d43c1125011acec31f565321b1dba9412b6ba0e3e9ada8706d6042acb613b183b56141549bc1d5eb6d0a0b0dc02fc3ad7e0279c80ffcf49c9f3fb0d03370c18fbc7bce497e20a93751edbf5e781b633cfb2d7813d1fe6bdd9be8e4bfb679069ef891ef335be847c63d5f47134bfc57e7fb087cf6bbf4fc104da4f45f633db3837e24dc59a1f35f83f1363a9ffd2abc8bd4ffafcc335beb479a781f153d5fb5bc8b09fe3f156759e17f7474e097897fedcc7347fa73d8815f2efeb534ef22dafff7e1ecd7a67fadd4db18f7ecc73e779b0c7cf023db375886e73f00cf1ff4f784b711cbb39ffb366679f6c37b1bf93cfb31792e2afa73c9bd87b0fe2b803771c77f65f21efafd5701cf6c971fc976b6cbfcfdf73eaa79beee3d0fd1fc6e71dec709cf7ebdde442aff75cef3d0c7ef56e3b928e9cf55e19925fe6025ce06fdfc6b60cfc3f7774bf13e2e7abe6e390bd4f423e9bc8bc8ff8fc4fbf8e6997dbe5a7a1b5d3cfb21781b5f3dfb057a83a13d5f553d7f122becf2afe53d0f95ff0156efe39de7eb91f791d7f335d2fb48e4f9cae2b98bfc6d7dfe8e6cecb7999fb711c6b39f826716f987c978ee4d7f367b0676f8913b9ed9e78f3c3eb7dddf3e9e9fe2ffab67a3f18d56f79c63fcc00a6f6280ff3af72e32f9fff23c0f3dfc07aaef2392e72b8ce7e0dfbfd6e07918f71f803eb36e7f1898e7fce507c23a5068e95f3bf23ec678be2e78ceb57ee395e7afeabf36e7f939fe5f9f87467eb7e4bb68e5ff1bf4fcb4ff91dec5bbffefc9fbb8e5f9da7c17cffebf0defa390e7ab8a3771d37fc5f53ee279be22791795fc7f779e857afeb5bde74ef467e3f383fc5d7c1bfd9efd16bd8d3a9efddcdec636cf7e3ddec521ff1f9c371882e72bad3791edbf6a781f333d5fd3bc8dcd67bfbab3c154ffda817711c1ff97e26d84f2ec87e31918e14726cf7ee5f8d7d8bc8995feabacb3c148ff9ad933fbe747babd89c9ff4ae4393bf8d9366f6283ff2ae24da4fed72bcf1ff3b7dafbd87cbed63db3f08f0c7b17a9fc7f81dec72bcfd7e53330c08f9cf1fc35e75f8bf42e4ef8ff6a3cb33d7e64d9731ff91bd5f317a47f0dd4fb28e0d90fd5bb98e4ffb3f33c44f01f48f0dc6bfed6f53ece78be36781fc93c5f793c0fabbfdb8c77f1c4ff577d1e7afaddaecf79d56f1cf136e23dfbd13dffd1df15de60fd7ca5f4cc7ef809007866ff1f153d3f4aee037fbf6fe297ffeae75decf2ff297a175ffc7f449ef38e1f48e86d14f2eca7e20dd6e1f98fecb94ffc1de9b9effc39bf89a6fe3ff639b8a58d199efd00bcc1063c5f533db3177e02c27791eeff6b0f1486fad7943c0bc5fe35b7b771d6b35fa167a0de8f2cf22e42f9fffa9c0de2f9d78cef61f15fdbf536fe79f67bf2265affbfe01b2c3f5f2b1df835f7af8539db7f7f8ef0361279f6637116b8e847be79ee1579a8e23fc0e47948e5778bcf0a11fd6b439ed9b01facd373f0c08f8cf4063bf07c9df536b279f6abbe8fb79eaf899ed90e3f81e3db08e7d90fc833a2e7a2ac3f578a3751c37fcd71b6a3fcddea6c2bffdef426a6f9af889e738f1fd8e979f8e977fb3bcb267fe4f04d94fe5729ef6394e76b8ee70ff80fe93957fa8977ef2387e7ebd8dbf8e5d9ef79f6cbef5fabf5fc65fdd754bd8f78cf57ba7731feffc8bc8964fff5edf92bd0bf96e93998fc91a6dec458ff1fd91b2ceef98f7d1e2af80f2c78cef10fb4f42ebafd7f36dec520ff9f9be72f53ff5aaaf7b1cff3b5c9732ef413e59e73fef384b341173f32d4d9af22ffda9d3791cb7fddf3268af9af81de4703cf7eb2de474fcf573767857bff1a8eb3414fff1a81f7f1d5f335d0f390cbefd6e37dc4f47c45f33e7279be3a9fdfe7ef086fa295ff5ae77908fddd4e3cb305feb016cfc3bbff40a9e7fff89bf82e96f9ff24bd8f7acfd7106fe29bffeae8997df523473c0713fc4849cf5f75fe3549cf39f90371bc8d299efd90cfecaa1f19e27d94f0ecc7eb1918e54746797eadbf633c7f85fad7ac6fa2f3bf2e791fd33c5ff5de47f8f9dae15d7cfe7f5c0efcaaf0af897976f43ef23d5f473c37dcdf7bef6296ffcfd0f397a77fcdd41bccf87cf574f60bc8bf56e76c70cdbf267c13f1fe2b923711c77ffdf1061b7bbe8e7a062ef9914e9edfe9ef0fefe280ffcfc3bbe8e4ffdbf30c2cf22397bc8da79efdf0bcc14e3cff15781b553dfbf179ee3a7feee760a27f6dec3da4f55fc9ce361ae097f35f7bf326aafaff866f62dc7f6df1dc0bfee6f12662f9af77de4659cf7e84dec753cf573cef63f1d9afd3f3a0b35f36feb535cfc55cefb109cf2ca91fd9e16dccf4ec97e67d643e5f29bc898ffe2babf731c5f315f92e4ef9ff00bd894dfe2b9be7def07790f710c06fe0c159e1a27f8dc8dbd8e5d96fc7dba8ebd9efd19b98e1bfe2787e9bbf0fbc8f759eaf459e87407eb71b6f639f67bf266f62dd7f95f15c74f4e7a27b1345fc57bcb791efd96fc473eef4133fbd8bcbff8fcadb68e1d9cff8fc0dff513d67507f31ee3ddcf55f0bbc8fd7e72ba3b771c8b3df8ab341e98f5cf5cc86f99107de450effdff35d34f1fff1bd89c6fffae02c90d28f94f3260efaafa1dec701cf7ea9de4749cf5731cfc1e58f3cf5fcb5ea5f63f52c3cf1af8d7866d7fc4806cfbff1ffd6bbe8fcffb2bc87bcfeabdadb48e5d9af7d1f453c5f053c0f5dfc07e03ce71a3f90c5734ef3036bbd8990fe6bab37f1ff75ed6d8cf3ec17e44d8cf05f4dbc8d959efdcabc899dfeebf57d0c3efb617a06befa917cde4625cf7e32dec354fff5eb4d6cffeb9077b1c3ff077d1f753d5f1f3d0f2bfc07a4cf9da0c1143c5f6dbd8993fe2bac7746fb5925678b78fe5c119e3fe76f05cfc1283fd2d6f3d79d7f6dd2731ef303659d05b63fb25ae8396ff981aede443aff75d2fb38e9f93ae61918e64766790f9d3f0207cfc1013f92d1fb68e1f96a7c83b53d5f779d0d96f8917def2298ffafd1d960861fb9e94db4f35f2bbd8b70ff1f8e37b1ca7fa5f336c2cf7e1d9e59533fd2c37303ffcef12e02ffbf0967856affdadc5960a41f19e76d1cf3ec273dcb4afa9115de44e47fc5f03eb678be1e78ce877ee28ce71ef0f78ee75ce207b2ef22f3ffbb72d668f6a6d4bec180cfd74bcf6dfd3381e746fe0d3d9b817f767b135bfc571eefa2ddffe7bec184cfd74c6f238b673f03ef23a9e76b9d67e0961f69e540a1a67f0dc99bd8ff95edf977fe76f0dc4dfe5ef50c7cf323cb3c0fd5fe03c06736c34f60f82ecafd7f77ef6391e76b8b37f1c27fb5f10c7cf123a7efa3aae72b9fe7a19bdfedcebb28e5fffbf336e27af65374f64bcbbf56e87d64f47ce572e097877fadcc738ef203ad6fa29bffdae86c70c18f9c749615f223cf9ed9047fd8f47d047bf6fbf4268afdd709ef228dff2fc9fb58e5f99a7c2e96fa7389786613fdc8b9e716fe7df70663f07cc5f526fefd7fc87731eeffcbf12e96fd7f199e73829f95f23e4678f6d3f5362a9ffde29e8539fe35166781827e249b3711c67f8ddfc73dcfd7256fe38867bf01ef22dbff67e2f975fe6ef036f27af68bf42efaf8ffd69c1582f9d79e6781cb1fd9e359d8e05fabf02e5af8fff09e3bc4df729e7b56ee207f937ace477e60a8b35fc07f8dcabb18e1ff93f12632ff6b92e73fffeeb771c7b39fdd33f0d68f04f42686f8afee335be947dabd898cfe6baaf7d0fa5f83cf4ff337db5961f15f93f03c64f3bbd5791bb93cfb399f73971ff82a3fe7138341213028ec7f5dec39c80283c2669b15760e362b160403068544e2810f0e8941e0573098d916f6017c0959a150d8dd4021b0d800c488c5619ffd3b0b00d0c46c362c3e7f65600fc34e6ca0901d16e8ccfefdaf668dcfb9f389c7dcff502864df6342f6eb281c12870fb2ef2c20ec67c18c0f2b3e77be3077e7043f9065915f038c39c0cf7e3dec6770e07f3c10ec423036837dec78763c10ecf94076783156a816005804703200016ee0cd7660dfecfd063e78a3070144b1af07099cbd82e1f0c60bc5320be0c703eff6fe800303443811c0cf7efcd8801b7f04f90ad43e9cf0a0b18314423cc3db6d6c79e207a438588d8f1de80305bc17b48193ddecc10760a0e47ffe1f60d07f80f777bc0b9cbd5aa40bbf88ff7ffe3f3cf3c2fffdffffc11acfdfc1e7b7f9ff1bb8aa035f3c882379fe6fa9511cac9d9cc77ff66c17e6ee1317fe79c81b74aa3f6b00d6f8ff0338c6695525be2e7c617d5a0febc38a584f8444fc7097b52901e93b1b2aebd3f2f4fd4baeaf4539b076190feb83714c35a957d6b3b5eafbf094f52c82bea709637dd85cdfbf3458afd6a8af4505b09e75d1f7367bac2d4a68fd972b6b535a623dfbd677b736501d56c4faaf0f7d5f73cafa3675ac57bb64edb0157d9d4767fd1a55d60f07584f6016f15fb4ac0f86b13ec8c5daa21258ff55c27a56697d2e64dadbaed6a70de87bda9ebeab6db21ec088bcbb4910f50214be7a47ac3e6bb47e0d2aeb8599ac575b657d504ddfd788b2b6a8a2f5ec82beb33bac0f1eb25e0c4adf859aac07b0a8c759147d2f60ed07d958afe6c8da949af4fd6b56dfd9b97ef08af56c5255b37bd6abd5b27e0d2c6b531ad2d7695c881f8eb27ed8c9fa34217d17d6b09e8d9daf0b4359ff3596e00048e4b52803d6aee4c17a5e09ac67b3768b3ab31e40a31e27f0ec7b9a23eb059c114fe003f1ec84be0f2358af26a9afd9aaf5bc24582f34b3be2d95f54142037aacad1f7ae93b015ac40b4bf59d1db3fe8b42df0b9cea36e52ad5859dac1f0ab01e00a31eff1ad177d6693d9185f5ac6bbd1b97e1d54e591f9463fd90525f534262bddaa4466f43c8fa3023d607cff435cb67fdd080f5ac98b503e8f9faf080f5ec80beb351b2361a06e20ba4ea1625c07a355bd6b3767d6783d60b5b1cf8f1c2d7bf40581f46a1ef054cf58529900fa2e97b1112b1450db076a50fd61e60f7d5a2a4589f16c9fa343fac67ebfa6a94ac1f7eb2b6981062b33dac9d37673d9ba2efc34bd61655666d00bcfa5ec055dfd54859cf8bcd7ab72ec3a3d959bb0c05f1c347d613805acfbed60faf7d5f93cafa6197beab59b29ed701eb8536ac6725f4fd6b57df0198c86bb255c47fb5b29eadd1773543d60e33d177f640a38335223e0d4f5fa7a2d4d794de585fc093f5ec0d551d86a4af0b68207e58aaafcf067d5dadcfda7fc2f4835bac6737581fdc43d6a3e2ac1f16b01e78c47ab545d63e13446c5141ebbf32f45dad94f5ac0d6bef90d5675ff475a50ed62605cbdaa65aa53a9babbeb345ebc306f4759e9cf5434ed6b369ebbb3095f504a4d6b7b1637d814c7d0faeb19e055a5b9498f52c8dbe3605cbda6517beaec6ca7a220ae20138ead1a20058df66cbfa212feb0b5cea17d8d4d7627dbe0e4462fd5787beb31fac57cb643ddbb4b62804d61605b49e8d5a9b6c8e78310a5f4f33d4f7a19ff56cbbea6988ac4d096fd3852eac5dacd00f0a12f0b65bd60b0358df468ff5ec59aab305fa9a542b6b8b62607d508bf55fb1acff82653d0bc0daa930f5f558b65e4d96f54138a0ae26ca7aa1296b538263fd1a21f1413d631ff6593f8c45e0434dd6bb41307516cc7ab64e7db7079dbec695f5ec83be2e6a203e0d9ff5697cfa9e06a8efc21b7d671b65bd1046dfd520adbab0ccfa6118eb5916d60f4d59af66b53e0d92f54201d67fc9b25ec027ebbf48589f4696b5c9e8bece8ae8fbb0cd7a36567d5683f56abcac3d0ace7a007bf4de86cb7ab708a99e46c8fa34417d6775ac7ada23ebdbdc223625e9a65615c2fa35a5ac67d9faae76c8faa127ebd9117d4f23c6da949858af86c8fa618dbe13e061fd108cf500ea9857d3646d52aeac1f6eb17ed8ca7ae10ab60f7559cf76aa5bd4d1faa1186b174f591f6cd377353f534fab633d81d7fab21cd62e86593f74643d5b2c6b879db05e48ca7ab544d6a7e959cf1ead4f135bcf0ead1f62b23e88c5fae018ebd9068d4e74617d5aa3bea644a4ef413b6067cb647d9b9ff569f4ac0f7601f5e09fb317e8647d9b10e28f305e5ce8c97a362e882d4acddaa286d60f09581b2c81f5bc18f49d1784beb309faae06cbdaa288d60f59591f24633d1bd7f7343eac6733887896697ddb2eeb875fac37a2205e0d95f561687d3da002b18b607dad8a84f5693a884d89d94d4948dfd57af59d2db3769891be0f91d617e0d4d76516be3e5c60fd179cb529fde87bf1c257f7aebe0f03583f9cd4f720a000576364bd1112f16deef49dc8cbda600aac67f1ac174a119cb5d077d684f56973acbda3569fcdb1ea0528595f00553f2d91b5cbf0ac1faaeabbdab5ef6a9cac9d0767fd1095f5693fac4fb367fdf056dfd916d6a614ddf4343d7d578b94a9458159cf22fbfe6567edb50ec4b7cdb2feeb95f5ecad67ef543d4c89b5c38e589fa66a7ddb2aeb09c87d5db8c9da6125ac57ab646d4a4aac6fb35acf06abef611536355a06e2bf3658df46ca7a026e8867c3ac4de9ddf4b65ed666f5ace785c07a9644df59057d075022af45ad592f64656d006eac5dc6b43e4d80f56c07eb8567d6ab15ea3b8bc1da63b77ed8cbfa34aff5c201c8b330fa1acdc3d785a2ac4d2989b5557db09e6deb0b90ea0bd3d83ee4d2f7af107dfff2607d014c7d1faa59bb5867fd179df5431a7dff12613df1c2d787b3d4ce3ae8fb1092b5f3e2ac6fe365fd1094b54599592fbcebfb979bf52cd1da00de58cf42e8bb5086f56da3ac1f36b2be0d96f5c12ad6b36d7d3f4222361803eb0154603e6dcffa321cd61e8b23beed94b5cb9ed60f6063eaac99b5cb60ebdb6a59cfaaf55d2d96b545b1d6b355eab304369d95eb0f19585b545b1ff4d277b64bd6870d297296ae7b6c10b1c7a65f6703a1ef6dacac4f5b647df08db5cb62ebbf2ef475aa4b7d1fd6b29e855a5f0054dfc37c14795b296b8b22b3fe18c3c6d55c597bd49cb54521ad6753656d4aeea6ab7db2fe8b95f5c3bfbe0f1d58cf9259fff5d67702d6af0f2df55dac02f1ec5adfc3801439bbc27ab63e6b832db0b65988af1e93233e8dcfda6122fa1e44d4f7e09c628bbaf50460eb8736fa3a55a5bea7ddb13ead4f5fab75f87af08bf54232eb838ac87ac7acfef055dfdb5a591fec53f635b1ac0fa6e93b6ba0d185a8ac575364fdf1cfd7794db09ec5b1ea5f18ac6d96c3fab657d636eb40bc50ccfab01e455e7667fdb052df0b7cea7b81507d677bd5f7e1036bbfb28457cb65eda2b5ef4130d61b2f7c752a4b7d4ff363bd0033e287aefa5a5498f56016886723f47dcd2a6b8791b03e022c6b5382377d4d046287a9e86b510dac0f66e9fb1056df8366fa5ea05477000dc42e6b5a5b1401ebdd26887a9a58d6b3acb5cd0a115b1508ebc12e102fa012f142146a6f7365bdda2bebd9187d17baea6b00bdfa4ea00df16b0e115bed03f1412a7d5723643d3089b5d5b410dfa6cb7a355ca6ae96cadaa2c6ac0fe6116b5523ac67e3d4673d585bd402eb879bac2710b57e28cafa6126eb0b5cad17e6b09ecdb31e001fbd4e1341bcb0cf7ab547d60e0bd1f73440ac57a365fd1081f56c22f4b59894f8027d7d6f1365fd5785beb7d1b2b60008c4a6e4a4ef413e656f23c8fab4437d67b3f543497d6f4365bd7094f5c219d60b4f14f83094b547d1591f96a8af4565ebd314591fc413d6a4e2582f6c656d4a6eac5fd3ca7ab55ba9ce72b0f62839eb87d6be13c8b3b6580862878db05e6d90f52c83460f4b626d4a3efa9e56c8fa30b8489739ade7f5a0ef6c96fa5f757d67d352bd2d97f5bc0c581fac73fc3097f56b4c59bb6c87f5613f8abcf8e7eb1164594f606a7dd08df5421ad6a359e87b9b2ceb5910b14e6cd1773553d60b63f49d45eb3bebd677210b0367bfac2f50c9fa201de3d9a7f54023d6b302face2ead0f3b427c013feb7939e8fb579cb5df58c24613453c2bc37ab40aa01e1463ed516ed636f5aaaf29b5b13e4d0febd3fe585b8c0ff1698cac074341fcd096f542156b67d3646d4a45face06e8bbda28ebddb8229e2d55dfd542595b5401eb876bd64e932336a5207d0d6057dfbfe6ac9df7663d9b27eb87abac2f20ca7a5e11fa3ad5a4be4e95a9ef6c086b536af6c37810399bc17ab655fdb4417d4da6eaabcba6d6b314fa6db1ac674f589b92b39b1294d48377005ccd93f502e4881f42b076599ef56a9dac5753653df1cf57efa0d59de766fd10cd7a364cd6b317fabee695f5052659bb4c6a7d1a1deb814aac6767a4fa9785be8bd9117bd49bb58b767d2fa0a9ef1162597bacd157af7120becd94f5acd0fa612aeb876cd6b70964bd70ccfaa1026b87a1e87b9b39d6b36deaf36ad0d7a28cd6af2165fd1097f5435ad6a7d9b1be4d95f5c417d603b0235ead95b503e4215ec8c97aa109022dea81f500fa109f46b5b62a11d6f35260bddaaebeaf91656d516cd60f6515b81087f569556b9b19faba106ded6bacd687c5479ee6c3fa6124ebd530591ff463f635a3ac570365bdda2ceb79b5f535251e9bfec5c1fa230ae259b8beaf6165edb0147d4f43b55eb8aaefc14268ad0682785e0bac0f96e97b815ad6cea2fa1eb4623ddb24eb8b288857d3a5ea6c85beabfd757adb9ff56c096b66f3cf02fbec87c0e7ce39f820dbcfc0ce40209bd96083bd1f04e6b3c57ff5f0f94121900d3e78620e0e9f81ef6e621f85390b3be7e35bc87e092fbc987078a110f81e760e33beffee9f1864075cf8b3377eecf9c13ef11c8467fff500671f1c3bf1ecdf5dfc8083b16242a0f09905303c9a3dbf59e18dc0136f7cb11ff62cf6b0bff87f00fbe08117deecc7979dc081bf0219f1f9c4fff9c0078373ecd9fbc160f57f70ecc37cf6f9f97ff7f39bfd81fffad71b78011fbb8802079e3d82e7db3d00fbffcaff4cf7560771b29f81bd1fe2c52ff9817fff035b4b4ef0fea0444410215f2226ccff45c988231911c34e141dfe96cd021c9b82a8fb9c2b43a20bc3ad5a702ee7ede732454a8cafd0507664128923379ec9a4333a2c312c712ab45f5df8db552740fd670a03b9646093b6cabb3d371a49088bcf5d4639fc6f512442dcdb13864128dfe7934394e97cb66e0fe7f4c49caf647ea0a3d827215a9d960db904bb4a98c3ff6285970d5972e634563ae3369e0e494e980042affd907c3b4c98e03a97b01bc2d503e1eab51f5ce7136e9b1e926f0757e97c7240c34659002cd8d275a77c6e33d8170fccffbb970591f3ebb9295dbd6ad4193763d73da2c578af8b66dcae58c2ed88e9b4658d2ba224bc2c58c0e6c686c1403043b06dc102360c19d896511758e8c03bc028c0126a3f900470c40f60fab21f333e8525c4f03b9de02a9dcf62dc1496507d523fb0b52806c428f3f1e1d39c2567819340d726e5fffbb21e1e5ea5bb8493f026c1adaded55c2afb2ad2dca744e3f98f830e28de31630fc469b4a17364441422b0b62a0ba6684939b5c1c00e57909369f40c8f05c6301a0851038d62294d0a8b06577a74627eb429416cceb8a1f043fcc38488e3452636107d1caa4f587d1f0678bee0b6df174a5d517b08b777737e57fdecbf8c6bcc4ff4376f8bb67bbf6378525b05f75420a413ae01c2b97e31a6ec6cffe6f335cd81d0117d78beeedcf5eeae4aa2d0c7358ba51279ebb8cc2883a3bb8f0eba1c3d27907a58290f12a5fe5db621975e3a92ba385b653aa09d84de6c16f886ab5c334865b294c7dae583e9d4d0fd56754451bb7cbaa6be25975f3dce0aaae1c1d9fbb8c46e1e232af147fe6126d9b2f5cf845b1229eb6ee899803f48dba4be81b8dbe21a6285ca5f38d51855f445fa7f3c3b3752a9d4b6a89e7d4c9744f3495ee0b570f0d480276f39cd11745ec8cc25892a0e22d9d7fde55a6cb8dbe29cabc650134da6d837180142344c09001039b03bea5b3418ce79eb0b38b9b4c670b58458ce79e606363632bfa60e35b3aef5ce6d2bd633b5140484078fc8421039efb0cf661b8c0c606812dea3eefee96cf978d050b28e75f13e2e9920d0306253c4dbab165084228931efaef2edfa81d396f6c6d1830b04970994b376b297b4d4d6109eef2a5f18284070e2b387860bbc0c6b774b6b4ec8df009d216cae0bf2230e0e3c42dd3a9d74627b82e1c27274ce3a6d1d719cbc5e9d9d9c1c9c5d9d9c1e9c1c590b3b38bd35b92908b111763e94ea976767238ea28d0cdc9c5c165c0f1c666c1c60525cce182d28d616727b77cf1a22432bc785112d30c51f784bb7c93d51645940abf709121ea9e70a12dfc469d5017da465fbc2809f7996cdf144b370a234ff99e885d14f19b62eaba219eadfbe14dc10762f9a4be325a17dabad3671b02c4f3490d11a737cd10b724b1742edda51b85616797c14104b8386a83e9f3494d518d72802a67c0ee940abb52aa0b1729a24e132aa6b1b385e30e2dfc42ca07e27d5f4c5d370433ea4ce12e5fa5fb017ea51fdc1e4e175310cbe73bc27d26db37c5fb62d976783edd784a85e3280c15cba75386fb0176e3a733049e8ae793c3f3c961f974cac8e04ea7d3240737766165f4f58083eead0e875cfc3e3bfc194d0cb72baacbb7c3924475f976c890012eee025c1c8c781a7d3dd4c1bd51eba838dce0e4e8a838e0e29624dc147071d41d7676707a767625f010e00d802af9eefe9705297385fc39978b142a48bcf090024689bb191c5038e326d3f907a674f5eef25a772f830ba5abf722af75652c5b17c67361383d3b3ba51b63ea9472983aa52d33ea7c4aa14ee974efbd3075976ff46edabbca94058e620a4b80b62e1c4fef5c271c7f84863b77f9469d706c67d754da0d21e2e347bbe26fe369a8188e40241289c568cbd6a11ac3ce65cbf6b271780359ba3cd82c40df5619418783bb062ef475a1436faac3acc38a535882de0b8da7c462f47da7d2681b7d55b04539fcf9cf9081d2d5ab3ea32adce58be2f9a6583edf1121e2d6853decbd4fb743f5195501c3c3d27db6d5f6c49b0207881a245cdc7d00f14185871420ee5aa868d102c41d8813303ea840713783030a360b75541bed74a974edecdc76ba548270b104f1bec9137a2e33fa3e3330db1717946e8c15d5d809bf4e584ac2453576c680f1a680db0ecbb6c3740a224ff93e3b3463d8d98103defbc22b966d07f7d9c62e5c981a5343c44e0e5137ef7d317d53a0c87041e9c69832dfa794eab6607bb6510e54b6288729fca660739fd4e50b656b3b48a979efd4d942a36fec4ea751b7c70be3c5d4e4d44db6ceb61947a96c2edc869846a96cd196e99c2eddb8b5e094c0166d9b31ec4cb2b145930ea6ae8b5725c116bf51b6e6b41dd1c26e0b83989b9b9b9b9b9b939393939393939393938b8b8b8b8b8b8b8b8b8b838383838383838383837b7b7b7b7b7b7b7b7b7b7383738373837383738373837383738373837383ab50a142850a152a54a850a1aeaeaeaeaeaeaeaeae8e02050a142850a0408102053a3a3a3a3a3a3a3a3abab9b9b9b9b9b9b9b9b939393939393939393939b9b8b8b8b8b8b8b8b8b838383838383838383838b8b7b7b7b7b7b7b737373737373737373737b7b70a152a54a850a142850a15eaeaeaeaeaeaeaeaeaea2850a0408102050a142850a0a3a3a3a3a3a3a3a3a39b9b9b9b9b9b9b9b9b9b939393939393939393938b8b8b8b8b8b8b8b8b8b838383838383838383837b7b7b7b7b7b7b7b7b737bab5047816e4e2e0eee0d0594c469fcc77bd989187627d4c59170a34e6614048c67ebba70e115503726f11fc2f36526d0feec60f92a5be8b4dd637b47907af26e3a6da5d3bb6a9bf74e3364a8a35e904e6398583adf182ade585a8595bf1c38cdb8954e9830e150480cfa1496e084a97772716d2efc2adba453091224e43df1d8d85c650bb3c56edeb44db2b1459d6d9cd73ed9aa6e125eb23dbb20618204090712244868c006a2c58aba39172e7a54f9f6f4a8f114e6217da3d0bc1dce26b3068d193262c078e1a2058b1529509c305162848810203e78e8c0618284b608bd203c70d080c1020509101c30c0b686542b40200d6d808000bdcecccac8c67a891173581965e63ff4854344dde7dd1bdf49a9777578318ce7b385ba78df171d30e42c7cfa11a59e5426e2c2126e9b6d1f4c7c185175f3daa81fe2f7e9621abfcf69fc3e5ddc49a5ad9d1d3cb5b36b616a7b279d6658ba533be8b4ddbbf06751a1fecc66509f1cbcb05ba3dac1f9611985f17c3a93b0bb62ca8c3a9bf11dec4aebe0de602c5d3e67883e4335651ffefcb7b3bb934e972e9f13c48f978198f1efbd4f3cd8d57bcb4028f81dd4c9564d3aa7b0843b3ab836a53376a1cdd809b76dd23000604763913864d774de901bb4a6372548bdc84c1987be292c61d9ba30198726fc2a27bcce7ca66cc3a5ffbff1033f15c6a346e32415cf5d2a936d853a0a74737271706f6e392c1b73ee5cb83488fdf9a446cb32a4faaf43071b33ea2ccbb0e8005e96e1cb7f69352ecb10c5cba0e3bf0336bb086c766ed4f9648bd0a02c03860c726c6c6ed4197de169cb8c3af9dc26938d197d9f0ef0a2ceb60910d8e0972d459b822d7e990dd8dcd81975e2b14dd9a24e37e9e655dd3c360721dedcdca837dadcdcdcdca8371ab0e938000447041d763a5ceab3d5638bc2ad34840a1042def858bbf0268e13a2b7e8542304531c08e960c7225a84531f21600f0898024a991244d38b520526c19d829469a3a2c4061ba482403f9968732b6c100aa62e2c39f54b10b6c0ae0a5c70cd0112e9caa21b0e9e9c00b2654f38c7008622106595bc41695a2c0099b0e20dde14597e3fae6031f64c6b29fa71a8e82691a107753fa60c99e4497af1f783869a0e210628b9f523019248617a08ef7cc4ca33d2c95577c2479f0fa18c408012c20712d69a9960a8e46385245673765a3cf948b38230808b468e1ea7900009441021a4071d0a743f103cdd1e3e9c34d15107e7ed11826861489b8c273dc6c6e8c26bf2465aed935354dceb81a29a668a180b551661a8c2602726ac19f356bd40aa4c481c57a4fa45a0c3a31bb79aafd28f3969891c4c3e307ccc3d7c70ae1619102b74dde34562831caa3037f616d97131d42ac89c3d2b49b32f80e016ecddb12fda721991ee3590a4e764c2971a7ab4384128f304c8999e2042a176f468fde831619e7ab528c504bd172e9033c66478a2c7d621089c13bc325157b1bb4899359245bd2389578abde120ea9034aeb10a94492d61e652a815728cd433159f844c055d88471de9c015eaa58cdd8820b28ad2963031b5522418718f0131072a8f4fb71699c5465805b2c84893649a69462937d516c0291951790e288a274d9f5d0001e11744a674a4dc26d9d08334cd29a04c7fdc1961486e602d51464caed4620227057d91f170eb12dcc60bf62246960b1eb878150a8006a58f87213839503d3495e211254f9f5d5468e4f1985165c21b07b3183c10ac5140e89fc6a4b0a6af17b92baaa47c1c2c3833d4e685f4081bab0f5f967890ae90a219fa3364d10d21afa92f592041033c7ae760787b2487788446aebc2008d83dc8fba2f746887a021a00f2d7868aa2f9824a4b2e2242a23ca216da20e3da602951a1013a859a143a422949a15f5e04f053a594158ac7c60da30e2a5841e10cc4b0f33245f0dc95114cf1db43e1a928d1b3ca943e5dea14517ae27832301484ce88612e8f567694e35d3ec80bb403d42e21c236794b60087983cbf393e7626f4bad0631dcf2d614104a9226d6891da958751d1228c51d7660c9b5513833dc2164ba83c411344b3df2a4ec708a7ae1a439e64326671bb6506090ec643963540334694ad6706ed823eacd9b14729c4542cc1e704ad069626222c90508538d2699b530495e1f4634f3dc461476d98135e7580d2334baa2a9d94b2cc918ee95311f0140ca52938015330b8f3a355aa93161b6b5a2c84150e21233421f20a7821f92e0ddcaa565d50e3075788592a15ef99973c583b2a397a93b2252f064e01db094c94f8a1183fb6a2d508d207cadbb07cd3d7bbba6728d883822564b512b37c5264b295e4ccddc0170a89b3c48f1b02c05089728ba23dd524e161a787f44c55856f9961f28788869154ab4148b3087cb41538c9293a818333a6ce0a53c63872f8417269c52aab413d202c4a9329294c1e4a4a49703c85ea77e29e926dfda5c8b2ce1b223776c13b554da1b5326121ce5aad52e287449c1abcb908e593a00e950a2a3850e354c324038d2e3ea0ed5a75079c43b597776049c82656787c15d90276606e06005df8db0715706654ec28a2c7272841a0123002a194272b011b6e1e343f628471c84d1a6c353a61c1b801f12a0f8e12607710c5448aa25c371898a630cde9b361c710ee1d88141d4082ca6a038e478a110825a2103c71d492b9998566f340a95b68aac858d1b587edc68e1c6608944c2ed06842372074aad4161e3960d425f4ab83864d562e3499b073e168a4f6cc4b0716008c4c30c5d456ab4ea940cda680b53434f8d227b23ae6e6a7035503851630b6d918a1a3448a502e46c83f688461c0e13c2f4a4c0a1c147c303211a00728df18de0303763d3a24e89c6c83d9861c612a9ba210efccc78f9386bbd03a21915f2660051dc99c6e1802563520607b60ad1792283cbae305860528992b16c345d20c0d510196e31febefca1b1c70e430c4425dc4424bac22b0695e1023195c41cc5b0b101c3232329c8622880214bea6809a14c030c3f3092b480d5a7634019182c3cd10a6027442a182fac2fea9080a3e08a21202f80bc10f182ac0f11872229d2c5a75bd4e4263b4a176afc70a4827343858b699b005732801c5d6488f324ceac168e8ba11333821aa448d0c25219552e9f4d6a2dc4b470b6902310259004598a5a9c05a31e2cae68418b21f760110b1d7ca3c60cc180b278e06385adb32e7ec00fc915835634b95b01438b024f0c2608596100841e5248047151c14a451e028fd48c7109a082888a132a52904af1e6cff9044b85638a3d097d327d5284214507278bdc9812b34c018642138a3228a228ea54b0aa345c6da16061a712bc96ca09496a5434d22f044e6c39a13c31c1c4d5c4a1321353d898a8c1c1448248256295e853024909165d4a9038352541e706891f2442903831a2af3a224d3b02463502039a115f222a890823a2594a445c085e211685b02220c45d0807ab40d402310804930ce2c51c08362a1f567dc0f361c807131f3c8cf2e0c6839e070e1e8e1d327528d301da8142873ce68e0d1224578ba6234ab2491c241c19e2b7c3d012291b686c48700db1fe5f4a30fe2f7d998f4d0ea2eef36ecf8d36b883dba6f26eea9480f774a7152ad455a05081aec25c05b90a7115e02abc5570ab50a1aeae8e421d5ddd5c9d5c5d5c1d5cdd5b9d5b5d050a75142850a0a33047418e421c05380a6f14dc2854a0aba3a34047473747274717470747f746e7465761ae6e8ec21cdddcdc9cdc5cdc1cdcdcdb9cdb5c05b93a390a72747273727272717270726f726e7215e2eae228c4d1c5cdc5c9c5c5c5c1c5bdc5b9c55580ab83a30047073707270717070707f706e70657e1adee8dc21bdddbdc9bdc5bdc1bdcdbdb9bdb5b05b73a370a6e746e736e726e716e706e6f6e6e6e59084008600a4b88c26e1cb7d209703c75d097a24d811775b6e1459d6c11cadc12e8189efa8fe36513d0b84b0a15de24b849786b7397b03bd563a3465d690b6573a5d578cf8d4e6870b609c758d9c26d5319ebbf0e36d865ba71d4592641938e6a0bc3ed94ad758102013ca54cc28dc335da3635e3563aa11abfbd49806b7bf1a2ce364f53b84592ad19b752b6e793839d1dd082813fe8e67ffc32b721ff0dfc413dffe397b90df96ff607f5fc8f5fe636e46e12dc06dc29a668e5e1470bbe516f511d678c889519b200c7f8a2a4b60da93503c58a66a7b530888c0052145c3222928b413eec74ba85668212dddb1308fd32eac56571e0c3cfec829a1c5062b14d9e440837f80c4cf8137343722a038d2fd13f970eccd191f664111aaa0b6476dd6803f2e70a88026fcaa80d667b76a5589200c42b2983ee4c0b302217b3b26bd30a3b5e2e013e3f23b1a62e78694992b4ec50d13284506b50a5174f1e6038ab53e1e046cc6729060e268036443f4bb1eb2657a5a6e8603366aa056c804c320f01073602d4a16a004684ab56cc4902ca38eb23e7c35f0b660649757ea63934d0bd194b2a98b488432b380d3ab0d832d52b472c02932e10d9e0acaaea7558b02e3972941660831b13af8fa2576e171a4fda5c28e363ec112d24ac168991b56ad4280c2f76a56054c0c7da8537715c01376ce7f223c1541c60d1a94608a63428f52733756111a4f680cc8e45b408a73e2a187949a26cd3abc412b507044c01a54c11b2c64f531094fb014cc08b520526c19c7af5d1f1064867d5174d646d5494d860631497d61a8b25b4044855ed938936b792a5b04634315fc0b0441160eac29253bda0f9321c75e287a6369bca0aecaac005971c3d73bed2cc6821835586bcb2e88683272768a09dc1f54cc06ac0da803de11c031888a246ad40144b6d8928b4b22a7983d2b4584843d18902ad1296ac3dd656bcc19b22cbef410007b490a0d6343d40038bb1675a2bd192313f0d4e051112ea8559d14d22430fea9e9d74b1e9d98277c283d890499ea417d75872dcd222545949e9d0d474083140c92d14a7d00cf5b8e1c60ac80c49a4303d8439cee81121094b0058ac1a89e519e9e4aa3391e72ad4d0864f102c4c691f421981000584ac2852f713945ed60367ac3533c140298a110ab284a65eac528849623567a7a5d32622063a98aa5c9818d3ac200ce0a2916301754211580d6cad213b43022410414410240f9e3405b9c0c567cd1805ba1f089e2e051818d8b8845d5b17049c34d15107271ae8d522b9066852f25a00a285216d329c389a55c1900b1168bc9c181ba30bafc99b37938dd619f63cf20343013945c5bd1e279a4090044088d9579b840053c458a8b2088385181a323c120b020a44067662c29a316eba3ef472a5474acb2d0548950989e3885821b3a5cc89512abe6a0820d0e1d18d5bdd01159c146650c1cf11643fe6a42572309b7c64c005f834883146cdca123b6ddcd0c4360415f823d429419c0036760e5ed0d23385843b679117335586c85c2d322056e8bac14ecfa0313fc4fc6424890d72a8c2d8e082eb0993190115153003ecb8186a15444e9c3b33485a21681507294b9a7d0104b740019a8a30318c2098b25c635fb4e53222b560a34e9658942a0d213340929e93095f68300ad0806f901a4f5dd2ca384128f30488d9185049143d098911676b8442ede8d1f6e9b2eb0c8f2e52ea860368cc53af16a590406a6924cad982437129cc5c20678cc9e024d50a6786bd1674527888750802e704af4c2ea64adc081c10698892c6ee2265d62816122468a270e886ee1c3349bc52ec0d07eda0c442818c1c50e402b134aeb10a14cf4f27352cc8e8c432c52acdcca5502be414133d4093e2880a5497de9ca9f824642ac802253f7a6076baf629c6d8910e5ca15e4a001310d59224e5041d32022288aca2b4250c10b40b7d4ed5314b61e4ac522418718f014b33372c47a2e8a1a11ba3f2f8746b5199110f35c6e836e558458500ac025964a44112a692033c09a02cdb140930a3949b6a0ba010004a0ea9615875208f89ca7340513cc94553c13987d78b545da00b2020fc62481932b6a8a876bd48e30c40ca6d920d3d482950e1a29264e961cf0bb21450a63fee8a6892dca90993e78b153db5123e58fdbdb52923e5727c4bd0b6079080282326576a2d21420b8e991a466424e24526e88b8c875b96bc38908187547e23e64d8d17ec458cac16543c0962e204238a571e01bc0a054083e2490da95e640c9848737394c1c981eaa189542b3c5c50dcb970a6760a99a7cf2e2a34f2cc60143a7315e155160b03aa4c78e36006830d283950326428f8a0ab6c8d0242ff34f6828ea153646fd426315a9bbe5ee4aea45252c4d2504dd4843b7a680e169c196ae3428c973e619013b36e1d326363f5e1cb920ed60024c2389216ba63c5d2604c0252411292cfeed40ba579824533f467c8a21b41b42c3854e6c4d9f40d25b32f59204103387ab3c6e221b629144b8d1810103c12905d6052c789830c8ce6a02469c9211ea1912b61463c31ca0738c815cf08d83dc8fba2074b2af525974ace01193152575efd010241ca118f181c49a6184841006800c85f1bca848825bc741587cc9138fb824a4b2e2242de27740ba3327f4066ac448b50ba985c14c49201274a200e060a026d90716d701433690a489140847c552a80013a859a1432e2897304cfd7a4534e528ddd89326721d27bc809181c623ab1b8d0a7c08b007eaa94b27e815884e5d114227f8602b071c3a8830a56158063f632756a2c4e320331ecbc4c0f5c61ed58211bd11f5ea8650453fcf6507051618d451e4e5ec6241120a76a43a22ba2beb63a6a10f52065ad4436a54f973a45947ec8b2c167c7223c48a86a7770188a722cd21689600314182c0baf04180a4267c43097168949a90146058c9c94654739dee5031f9ba22a42bc2ca244e40e50bb8408dbdc0d9a1344d3802224d60c8021e40d2ecf8f1048347a91f5e84b49caf6b6d46a106302171e668f002c6abd4a6b0a08254913eb44833bafca4a70f2226bc500565d87040ab1cf831961afd90f426065b0e4da289cf95a6440d471c627b72ed2b5536c236808614e921e898a22e1ce83146847d02cf5c893629606c3101e816e2dc960e68cd361518ca3d8115eafd10bc21838b1174e9a633e649a82d9f2118f0e58a3a4b185028364176b8fae05789d4451d054c20cd5004d9a9235dab220593b1b12e18d05b13da2debc4911a709a849a7da3c8ac584511a21660f3825e6807a4b71e1838f343d00674c24b90061aa61a2c6995d2c963887368dad8549f2fa30e2c20823bbbeec09624511701b51d86507d6b6c1d2041d82b608e3ce580d2334baa24906338c461a9c28e0adb12596640cf7c8deb480e1c4918206864a010048596a12ac82b342cf8f1241a4248b04e051a7462bb5254dd25cdd2c6c81f1cc8dd58a220741094b65d6da064d80d3600aae027d809c0a7e4032e98d1424b433b03b8202e4d2b26a07180a9c0815c100a70d0f1c2e5932d42b3f73ae3e5a8e57822535e820d576f4327547440a84390359c6747a70679780258ff2d489743c3005951b3ba3fc18da456629939f14e38554d3564c6edcc942b744ae05aa1184af9db682b3e6ca0b044dec06a0b9676f575452623571e541afc8a55c65228e88d552d46aba0053932f39248931426eb294e2c5b48c150f186dac3432856acd0038d44d1ea4783840680c8687a19eb53e65807089a23bcee1617218cbd0bb1357d6b2d0c0fb230a062ca397143b466ce9a1040d84590b373b7bb35e393c5e8da9e6126346c1434cab50221b4f9ed850804242d22a16610e978366183966ccfc50d3344305124d548c191d36ec3a55494c045d4e0605d16266872f8417e6db91354264b8bd90e0c8c4a458c9938384109d2bb8370b0469b2d1214d480b10a7ca440a348245190fb85910c09cc1ada1b02a151a57a8a6c3205b51989410b3e5e20e16db911595d69a5d7218c90c091c40f63a754b778cb8f10337322f1bceee9ce272e46c70ccad32f4825687334863388ef4987425400ef991b4e7a0dd14092cb284cb8e9c5117f46a4296a739507b01ec3c4490722393d2495b261b6787e85cc716cd01d200844f45192763a4f62645d15160ca4482a35cb4ea8071a36292a03b023a40a14b0a5e5d856c95803505c373f0a6c9e6f48ed0d09271607a72a3b8c7400ca6da0119760d648149c20a716d33684e8651408a37129430a0436096180907e27084ba4086268613312d6a05b051c7490a29414eb8a9619201c2919e56b1aae8015142885b1595804fa1f28877b10651ef7690e94523d75a998053b0ecec30388e53af50f5f231e104f28999013858f1e832219389a3233f137006488b544dfda9314a10a83252336d44196550e624acc82227add7a914838a3481865a1b0123002a1941a870899a6252f040aa179ab00d1f1f32476ae0cc5ad1a2ca173c5bcc8230da7478cab416c8c694da21002e3862c00f0950fc7073620b09070fbc80a8220b6d0c5448aa25335be0892ae6f7b220623644461d9eb4b88f15a94360569e709013c37bd386238ea152b0527dd1c406c371a5611035028ba9a718eb86bf596b5a2f09cc78a110825a11c3ca02129628aed24819117324ad6462cab787038f0fb08c1178e8d242a5ad226b516350138fdd0a276a96c4381332c20f074f3e20b14dc9352dd30a4a360637064b24121334a1d8c0c8810a044da8048ec81d28b5e68493389100ac0922c6c3ab00ec8d5479d8305c70b2c9c887d72949b2c6be947071c8aa4569a6ecc21547ac8e840ab479e063a1e8a45218246d7d4281ea212940932f5726583003e01405198bec0c486a201b02f13043571178c5b54c680b0e01b740a764d0461b18237708ed311e8853e582803302147e24089271b58e612a276bdcc8f6465cddd4debcf841b2429364625ba0e6448d2db4452aee6afd30438a470c2e9a04946975c86c8c0621b71a15c0c380cf4719592a40ce36688ff0ee860170b548b4a80fc96142989e1438d22d47594ef49c89ad0c905380c60f96b54054b146a60846a650cb6eac62d5d660102b6b54ce18203a3008c85c637c2338c8bd3163ca840a2c08467c18b0a853a231720f10e4597141439b2263289459225537c4811f15888eba7af9f162e76e8d8fb3d63b203a11f521c1f5bb2279a1b506a67c6e8719275802e9f9d3716a8b198a3bd3381caf545dd87394a845852c13cbe0c056213a4e5c31babe9450b51a1086b62b0c169854a148f5153a61e4f886d609b346d305025c094935c78bb4992956213362646ced294b80e762d00f1aaef80679c993f6e50f8d3d76185a40b262e82ccaaf08ce4ab88948746597274d6c9aa08c717f45c6860bc454127314208401363a65a4e4f2426003864746528c01c2d0905401d1aa54397bc40bc3dc26006ebbbc18d92699513b8c491d2d21946580a245ce85b40d6238221140e5c6121fb2d10f05a722dc7a64d50524400b587d3a0690d1c54a0d50150c48b2341af3442b809d10a8762d31a565cd1b0537980a584136d9c3633a810354d094c8ac374b02f40d885e6bdd500049967baba3c482929180a3e08aa11fac8c0c8e1c8a64a4d544ad439c324ac448f8000a0d19bb52215aec09683ac153e18402f6140b148bf050f25485ac0f118722a9da173d71063b0800487450bb454d6eb293bc3283cf2e07a080746005f8e14805e7468a4f658a76f919b0949354d626c0950c2046536196de925555863821e33c8933ab7d33068d452a3052fa9e5206383123a8410a047644c47d04a421110b2e2ba3cae5b3a98094278ef60d922f5a51d6f2c2880e682832210a8bd36c78dc630aed000ced13a70271c89e120f15cc2852332310259004598aec204578ce29ca53674a2c80d1d10c42cf094526b1200821e8c883f627941b427355539fd80cf990c90da458665ad062c83d50b4aac4ab532043606420c6376acc108ca79f37c1233f4f2a2eaca5d9e83c41183d10052123a25695013ae0cecebaf8013f24296c2122c4c1010e1a2131506756e8c010b883231714c00414472e4d172c16b172e5e8e52349d409a70940d8ceb428f0c4608210384675335e51b31a0d1903a1871412415b6c4113c1598042ca108f4019419330f5b27180048651994805996a0a40e0919a312e6778be9677dca4721d54c7e4646a05c06c0c87182386fef059a5e44a81134eca48f0c193dc52a99222299fd0f40960a60cf5d2152a04e050a9cfa9003d8bc80a10c1f5c9831a1232a83c00d42054a20a05fe9c4fb05430ae7942600f0b00b84c850448e893e993a20b4932fdb86bc3a31088269993456e4c89518a19db850683412430aed4dec421ecb68859aaba620bc61108231f020039a0b4a8079a9a9236383e99d1722949b6cb0f800ab94494a8f48a4e9002553d3cc8e4a15252fce0311640523152d40010ac012a58551aaeb53aa9d1e22a5ae76a29a80c762ac16ba7c469556d12c88a2c35774d8d8a46fa258d4e8f1b636eadeee4ca34c0540e355a73ce6a2c22a1a6460c5c134095a93265aa10ad0f23c6d8b51a01a2ca933a6b7d64a2c03d500ecf11a2d370558f3c684050c802a34306332e8818334baf1655315b66a8610a05d91e5bbc4eb5f1a000ca416cc51ba91518d658c1d50b6940128b04726968aa76a0e9dad04a10af2c5f82a4aae0ca6c949225668c20358983230be0a855ae0462779ecf4fd10cc306b53c8178632d5a95d647841916206e60005e5419da49f32476d656d2fbb46b5091c7270e63a48c90b3618c00004441965cc8d4a2d3283440699c408e20c34676529c4a887889e38854839f9d7a1b436611dca752a4000ade546c1f3bc2c0ce9d313616ad3903ea088d24435e4088a6d58bb844609dc82c85b6cefc01f2f4c4f270230c0f1f775604a9acf8c76025a22109880928baa66c00c61bfe027532dd1ab088af549b1d2e63fad8e23b246a8c0e3c811c88b0b308c71f8534342b2e472f2adc18a37420cbd08d3382dcf0b1d5165152eb4b0935bc03b91edc8964e9410032749045924ba62005042ffec0607f01c33ad86c1f186c60b3c5afeedc442210ac830d240e8743002c7b232b14f6d711280c361a7be18d01802d66341e7833360bcf1a85c2a0b1f88be562f1172b828d090b741fbb8bc22fb6c0c5be9e8b6f60b108c602c3fef7df647f50f82e065f3c7e35f06d46540306ff23f1cd1a872d04128d02586058916dec0b40d1d82d847b7efe02e61e13b6d9000000129c4082239bd3de071eb8580bf38144761826644f207f957d154fec6ea191058e09cf1ebcd989ecf0617dbcd06c6876a0583eb1ac1364225b10f89b890d3030f60381c3e200b41fc50ee0073efc01fc8b282470630f087b9891ede1d7337048f73e2ef8f14dcc01cece0ac916048f67fb0a4ef89784076fbcdabfdee059f8316101b02fb34738f00e30247f769608e0756b615f43fb31ac859d85664204062e06f6160b3ec004e18bdd1fb22ff02f33a3d8179051389178fcf3311cd8c7e72281bf29388be1088e7c18db11bc94903dfe78ef17afc0eceb598617f0afb6488231073bf0130ef7e7277ffcebc3171fe3061e7628c7d2e3935838b8f15380680ffe6bc0b9bcd79cc94f1c3925ecc1dffdff8af01f07f020abe2bfd93df58707effc8b3ad0fbc2fcaf0f3a813e9de385232ebc809f68b8bf38da8560673301e34f6923dcc7742dfad9ffb9a8ffc6fe27205b0bc9af2503611d403fdf727466084efc622cfc6202fc62c27e31c13f0caf3fccf54711e30fe3e7b90c95e732009ec904ff137ecf6322fd6098ff0957ff8948ff975dff970dd98bdb6f22d2cf85d7cfa5d0cf45cdcfc5c16f22eedf42ea194b9e7f8b837f8b315fc9f297a8f37ca5f86b11f597d0fb4b449faba43d5301f16b41f0e3a8f36731f493b03e43d9fbb1e8f94960f9b1387976f2f75f39fbad5cff2a3ffe2a9bfe2a9afe2a753f1502cf4a24fc54c6fe29b2fe29579e91b87a465229caa5e721a81f4aac1fca9f1f8aa3e721f059c8841fcade0f85c0ff84d0ffc4cdf38f35ff1318bf932663fe108afe10819eab78cf7c717e393fff92bf3fc4dcbf04d4bf44d3b31ef35fa2fc97e8f8415cff25fd2b19f583d0f42b49f22bc9fb95344842ea5702e04fb2e44fc2e64fe2fc933057fc20f29ea5959e5f3a3f88f60781e3f98df023593e4f1bfd4802fc4768fd4764fd20203c3f313d43f39e9f74bf91bedfc8a3df48a3df880b23a87e23807e23087e23707e23cadf885acebbf017b1fe4500fd45ecfc45e8fc2decfc45c0fc459cfc45a8fc1ff27e2961fe22c0b82fcf71569e99789e996afe1a787e2219fe0fc69f08fb0fa9f37bc0f41c77f70f913ebb6bfe213a9e5d04cfcb4e7fbf102ccfca0cbf901ecf494ebf071acf4911cfc906bf07b93f08883f48fe8128fa3df40f44f97c97e407f2ff47a9ff47aabfc398ff47a5e71b43fe0e0c7e1f8c7e1f899e6db8f93be4bfc7ab1a6a9e69fcf87ba8f87b94e030e1afb2ca31103dc7e8f257633cc758f06f28f5f33de24bf1fc42eddf90e5df10e2df73b25762eddf433eb3887b6681f6bc62d6f30a5a1b86bf065dcf2b3e3cabd8f3ac82ccaf27f66c404c3e51e5a766783e41f75313fc1a08fc71a33f4612dff8631119f6fc53347f8637ffd4c93fedf13cc2ec59c4dc0f63f9e121cf21863fbc026ef0ecc1cdcfe3cb7307e5cf03c47350c12f85f34bf77e29905f5ae2c790e597129056f8df44cf1aa23c6bc8f1bfd11f7a7dce40e98f2ac210f747a37f74c20f957bc680fca1487e28df0f75fe500ed00c3f34c0ff64fe4f48ff53d0f3853fff33c5b3dddfb39af1d90eca9fe747cc9f07ff1752fc792f9eebd4fc7906fe0b03fe1d9e9ee9ccfc3bd6fcce55bfd3ccefec70a7d9df5cfb9b519ee5a0cd163f13c9cffcfb2d90788e33f4335dfccc1c3fd380074eff10383fde97b3c0f2b3a8f2db99fa2d0c2da0fd78247e3cbfdf2df5ec56fcdda7ddf35feefd4b30ff32c8bf74fe76127e259b5f89e85f1e788620eb579afa95467e65f34feafa95189e211c7fe58a3f79e94fd2f99338fe24813f29f6b7b740fdad9cbf3df3b7757afced89f6c4df8ef875287a5e70a94281bf82ddaf83c87f77fdef34fd7790febb2e7f5da9ff2eefbf4b3eb79d9ee1f6fc758aee0efc453a2378fb73f07a365325479e3f879d3fc79cbfeec7b301237f5df2cfe1fc7360f87334f871ecfaeb26fc3848fd3846fd387efc38f27e1cd16735eb8fc3c3b39aa9ff0694ffc6201d3f0522ff8d03ff9d8bff86f0b7d1eb99c080df062d1b9b7e1b869ed11665f86d0c7f3a5d3fddabbf06de4f63d35f83c15f83c04fa3d5738055cf01d6fc34923f8d033f8db49f86f09f81eb9f31ebd9ccd43fe3cd3fe3ed9f01f7cb70f2cf00fb65d4fa6544fa67bc8a11ff3158fc3204c808f0c328f4c3b0f3c370f3c3e072e487a1fc61b8f86188fd2f6efd2fcafc2f9aff8b11ff8bbaff4598ff85f07721eb77c1ead9a8fc5d84fa5d34faa5d2e7620421bf0b20bf8be9efa2860b122e84bf8be0dfe26feeec6fb1f7b780ff1621fe16167eb9507f0bf66761eb994a28166ffe148f7e16677e165a9e83267e16507e16cc5f8e0f8bbd9fc5f267f1e26781e25968f7cb85f957485ac1e55fd1e35f917f157e54e848c1e14f51f7a7c0f0a6f4bed1289f3e7cde94601ed2373ae34dc97d5392f146737ba3cdf1799fc9799fed799b019a63d6e76dc6e75d26a32cc3bb4c04dc7b8cc17bec0d8d97b7589fb718973f284890203e7f10d32a55a950594d951a250a549fa74e9c3665c274c952254a7ff0cf9e3f3869d29f3f8508fd41557f50cf1f9cf507effc414ebf9828bf180fcf412c3ddf98f4dc82c4bfa1c2bf61c06f01cc731ca6e7061f9e1b94f042b6c5a8c5d3af2546961f7f15bd9f0a977f0afc4ba973d2a489abbf09af268e9a084af233e1f02ba9fe4660fd465a640062e3ffe183cecff7e8dfdbf0ef95f8f56afd7a9ef4d67e3db59f6ae1a70aff98072ce787e7fe3d3552a4a33feae98772c8abfecc183f93c0cf14e0c6f8972dfe65815f79e84fcae918fe6d8a5fc7a6ff0ece7fe7fc7370fa7354fa6d34f86b247f1a2f7e1a1ffe197648fd30b8fc3060b4f8fb5b28fa590cf95980f8550cf959347f1678fe140bfe22039e3bbcfa7550ff2d167e2dbdfe2ca5a4e4f9a10cff27967e27157e27fd33c9f23311fb8fc82852fd89b8fa87047f21f01f84c31f84e863d0df83c8df63c35f8df257a1fc551c7f35c65f5df15747fc55127f35c3cf47e58f0ffc30849f47f47f0ffcd1b41fbaf73791fc78547e97d4efaaf8db597f1be56f9bbf8e2bff8de87f63f9cf58f3cfd8f0c768fe31843f0c30bf0b167f0b537f0b463f0b14ff0a54bf8a387f163dff14503f983b3f9706ff165dff1658ff945dff94babf89df9f84ec1702e8ff11e8d712e3af4af9abee5fcdf1572ffc7a8dfee9839f05a81f2ffe67d4f8610cfa596cf87358fa9938f9b3a0f8979cf8a558f8f1eefc50e46f03c9ff23d39fb4f4e748f3b7f8fa5bd8fd46e8fe14837e27337e29971f1ef4ffa8f2fb60fb7b38fa89a4faa18e7ea6963f8c98ff8b8c3f8b991fc9df1f6ff9794cfa5fa8fdd13ebf0b0d7f16523f9547ff9251ff1249bf90ea4f25f5c7c2ff79ea6faef999297ebcb2ff86fbbf08fb5b38f8b324690af8db0fff9d935fca831fbaeac781f6af80f067e1fb93127e16d07f4b8e1f88d8cf97f75369fc31f18f7ef9a11c7e26ad9fe9e36fc1e4cf22e1c75bf28b81f24ff9f11fd1f5eb75f8794472b9fc3734fc365efd2b3afd2992fc3a46fd59f6fc54eefcd538bf0a27ff9601bf96117f965b7f15b97f48885f48973f76fed33abf8e023f8e3e31ae7f0a53ff94faffc8f0e7c5f9953efe3b5b7f0e464b0afdd245ff0b407f0b337f0b01bf9512ff1404ff9157bf9405ff90f70752ebaf36fa63263f0f12ff8be05f76793be5cf11ff52927fdede8f97e86fe1e50f13e90fa3e5efb2e3c702e7af3ef85f5ebf8be8cfd1eaa731fd5f74f85bd8faa948fa9b54fa8bbcff0f547f93c8cf82d1cfc2f9a788f353dd5f39e62f72ea4ff2fa9f4b7e2973febc3e7f9336ff1112ff5eff3372fc2e0cfd400efc2edfff7cf4a700fbefd8fc2a04fd7cb97e17027eaaf49f2af97590fd3650fd292afc4d782525fd4e86fc59a8bf11593f90407f8f023f8b473f8bbeb7a8f46731f107a1f00399f0bf613fb4d3bfa3ecbf91fc676cfab3e0fa9f14faf91afd70f21f12e4ffa1e3bf03fe3342fd4d32fd6184fc5680bf11463f90317f55c8cff7e407f3f77b797f2d703f14587f921b7f12e13fa47f3d543f2cf6efb0f42f0ffd2d40fd2cf4fc2a92fc5322fdb0ae1fbae3670af87334fa97acfd4436fd49463f93e58f04d2ffb2fa71acf8c320ff246d3f4ce0cf03fe2ea87f8a223f17593f951dff110cff14d42fe5f44309fc3bf23f45cabfc4ef57d2e74fa2e11782e4d773feedad9fc6881f86929f0aa59f060b315f7e17a7fe14707f94d2ff64d11fa5d18f57eb7f3262d72c59b160a5f1b409d3efd6a5d2efd645d2a24487080902e4878f1e3c76e8c881e3064c081f7cd4dfad0bfcbb75799b78cbf60e870d1a3260b860a102c5efc6e57733f1e0d3ef46fcdd8678eed8a953850a901f6ed8ac29a3248991223e7aece94d61f7c032c32fa038d01ec308101e2e2840601c1e602b5d9a74a5cacfd6fd06a6b0b13f5bfe7f870d1b3662c4300201e23ad6ac2183060d1a342c2242840e14aef15ae3b5c68b0b172e5c862c1f3e7cd604ad095a13a4458b162c58f4e8f91a10070a3b341d3a6cd8b061c48891162d5af4e8d103c2cc0c0408468c10211224e83f1d366cd83062c4e8673bf4cd3a7440840811224488c4881123468c1841820409e2c2850b173e7cf8f0d1a2454b870e7af4e8d1a3470f162c58b060c182070f1e3c78f050a142850a152a1d3a74bfcc8c4e9546954695c61120605922f12740804844800001cb12893f01022cf155f1aa785538493849b02e08d7655922110102a15028140a851c84421601070e6cce2d8796430bf9d962317e7b43802067f68b6f06dfcf7eb108107c7d0df764ecc9d08ba1178305b26c5b9b9b5b82046d6d6f6f08e217531753b6117c7dbdbd018108107c7d5dfab3cdf690ed6e7b8ba688a680a280a280fe6cb36d6d6f6f40e089e7893c137926f24cb8b94990301c2240f0f50504e2856019b8b9b92058e0dbfe7f587e587e587e587e60f086e07f87640705ff3974d090e182dd189891f8f51398fb09047f3109bf9882296ed290f95285879d3f6cc81f56e70f8be13f8cc71f86e70fdb6023068b137f989e3facc30f164a52a31f8cd30f2643c40f96e1070bf11f3115ca94e83f62fa8f82fe638cff18e2376a52f41b03fdc644bf51d36fac72e0375ef88d067ea38c0cc618bf510484020202d4fdc50b7f114e4e999ff8e72792f9899a02fd444513201c3050404080b338b79f78e12736a8440853895f0863c42f7c2174f10b1998802082810720020f083c2040d1a62a550664dd803ae0000a02e80270da74e626e6264617800a8c3dd4d9c588fbd9e15c1d700010c1803421b00c78f6b34a8c100942c407111f3d78e8a0e1c2870d1a315cb460f9af87cacff62db0cd96d178bfbe581543060c5a0e0b162c58f0df0515061506dc026ef1aa785538493849b8205c106361ff39f86db226628b678af88e5ca5f5345091678229c29ae8eb8b1dfec763371008e3783c3a8f04081020d0a3468f1a3d6ad0a8d2d8fbd9cc366800830a830a830a830a237e11bf98ba98ba98ba98baf8d9e391018366fbacd128140281c7230306ddc7e3f3c4f3449e891d2548384938493849b0cd114c114c114c114c112e0817840bc205e18258b2c7e3d757cedd0d1a08854060b54aa552a9effbbe6ff46787b9cf0e73ff2c83b3c5219bfb679b1de63e3b64739f65d961eeb3c3dc6719b0c521fb9f03070e1cdec43dff55d37b88eabf0acf0a95fc6bcbe7e1d86fa0cbd920f047167a1f573d5ffbbc8d6e9efd7c3c0fd17e0391de475bcf57446fe2da7fcdf0ccbef9910e9ed9017f9887b340033f12c7fb08e8f90ae5f949fe06f09cb3fcc056ef63ace7eba077d1f8ffd1bd8f1d9eaf64cf5f71fe35486f228dffcaf7fcbebf413c7fc8dfc6f710ec37b0e0394b7f20a5e737fff77a1e3af80f7c781ecaf9ddf63c671d3ff0c6bb08e4ff6bf336227af6c3f2dc83fe3c7c13a1fff5c91b2cecf91aea7d34f37c9dbe8978feeba53771f95f8d9c657ffc48b3b342e5bfe6e26cfefaf3db9b08f75f593cffc37f556f639267bf18cfc0143f52fadc7d7f0ef09cf97ea0a667b6c68f04be8b6affdf88e72f49ff5aa8e7a181ffc0f1394ffa8939dec74fcf5738cf6c899f0086b7f1efd9afd1d9a0851fa9e94d24f35f073d0f39fc07f89e83cd1f89ea6c70ee47627a1763fc7f46dec70fcf57b367219b7f4dfa06fb7abe6a7a0e5aff35036f2286ff7ae3ac90ee5fcbf036da7af643f4fc3f7fb7bd8d359efd1e9c05e6f8917ccf6cf34716cf0addfeb50bcf2cd80f76e21958e347ea3dffcbdf04de464ccf7e689e9bedefe89b48f75f61bcc190cfd7576f63fbecf7f6dcadfebce0f9706cb0d6bfb6e02cdbeb47aa787ef27faee7229d3fd78337d89faf939e3bc6df96ce0669fcc8516fa299ff5ae87d543e5fe3dec417ff75fa2e36ff3f2ccfe7fbfbeb4d5cf45f51bd8b33fe3f24ef628fff4fcd73def0b391de47e7f3b5c2333beb479678ee087fc7cf431bff81516fe2f1bf42781f233d5fc33ce70b3ffbe839c7f985079e8b9cfe5c1bde4424fff5cc732ef203419d05067f248bf7f1cbf3f5f9dc6ffee6f5dc4bfe56f5ecf736aa79f6db7bee3c99adf32323bc8992feebab3711d7ff47e05d1cfe7f71cf5f6dfe3547ef229bff2fd333b0cb8fbcf22e0af9ffde9c15eaf8d75abcc11a3c5ffddec6e7b3df8537b1d17f55f53c74f2bb49dfc71bcfd708efa282ff6fc5b3f1c09bd29f375884e7abaeb7b1ccb31f7c96d5f523533c0f45fc07883c03c57e248cf731ccf335fa269afdd7b8f731c7f355c2f390d1ef96e97948e6770bf33e1e78f69bf59c49fd451acf4243ff1a90b3c10f3fb2d3f347fc67f59cf7fcc241ef6291ff4fcedbe8f6ecf7e747ffcecfb9f903f79e8397feb501efa386e76bd833dbe8475278ee2e7fd73a1b5cf4af913d67bd1f98e95dbcf0ff31dfc7e9f3d5c33370d68ff47336e8e65ffb7ace6d3ffbe65dacfbff946fa38c673f066fa3a1673f2bcfdff4b7873718f1f9cae95d0cf1ff4d9f73819f65f2fcb5e65f6bf43e127abe52797e9ebf1f3c3fd3df19de4743cf572bcf37f5f789b7b1d7b39fa4f791c9f395c6dbc8e4d98fc61b2cc4f35f80b3c1b21fa9e85998e15fe3f03e3278f6c3f53e1a79beba7817c1febfbab34150ff5a810385aafeb52667bf0efc6b5cde60c7e76bdffbe8e7f9eae41958e6476e791fd39efd56bdc1de9efffa6d4cf5ecb7e7d9f8f53eeb73f6ebcbbf76e84d6cfbaf279e8718fe03f1f375fd5de35d24f3ff457a136dfd7f68efa1adff6af62646ff2b94f7d1fa4cf77fd45921f05f8bf03ee278beb63db34d7e24dafb68f6ec67ea6d64f5ece7e7ac91c19b52a07711c2ff17e3fd75e55f0bf45cdcf52e33f126dafd571a6fe39a673fbe37d8daf315d6bb08fdffba3cffc5ff59cf5ff3b781f7b1edd96fd7730ef30363bd8f8e9eaf5e9e1fe0ffa367a0d98f9471f66bd4bf86ea2c50c38f743e7798bffddec402ffd5c2db28e5d9efee6da4f0ecf7f5fcf5e95f3bf50cfcf023f39e9fff133af0abfdd7dabc8b4bfe3f3c6fa38767bfb2b7317ef6fbf0261efaafa5de4445ffd5fa26aaf8afd1f751d0f355ca59e18a7fadc4dbb8f7ec27e239fffdc6476f22a6ff5aeb9995fbc3d43cffccdf6b6fa3ab673f40cf5f5dfe3543cfc1263f92d673e6f51f07bd8f2a9eaf6bcfc3bcff41df3b97fd6cfcfc15e55febf35ca4f4e7b27b17dfffefcb73a7fa33f279a8e7771bf4266afaafb7de44e17f55f03e5a79be2a9f7bf8b78e03bfa8ff9a9cb3414affdad99be8e5bff67906c6f99166de474dcf5735ef2398e72bf45988e75fd37b17bffe3fb8b3c12fff5a7ece337ee08a7711c9ff57e70d86e1f90fec3ddcf12378f03e6678be0278068ef891d037d1fdaf47ce0a5dfc6b26dec646cf7e5d9e73aabf98e34d14f05fe99eb3f52fda781fabcf57126fa28dffdae3f9abfe56f15cb4f516fbf2263afaafabde606acf57576f63a8673f39ef6285ff6ff906037bbe7e7a1b4b3cfba53d177bbdcb4ebc8d749efd88bc8f969eaf669e83537ee4ade77ce2077ebd8f0e9efdaeef63fc7cfdf0dc697fe7bd8f4b9eaf339e83c81ff9e8acd0faaf3179cea2fe22dd1bccc0f355d673d1d29fcbc2bbd8e4ffd3735668e65f8b3e0301fcc8176f6291ff4ae6f92bd6bff6ea7968e6777bf336329ffd28bc8f329eaf0cde460ccf7e60ef61adff5af6ccd2fd616fde4414fff5f96c0cbecd023d0307fc481a6fa39c673f216fb004cfd75aefe386e7abd873c7f9bbd79bb8eaff23be8927fe6bdefb88eaf9aae7f9abcabff6e75968e55f6bbe893cfeeb94e7a19adf6dcefb78e2f95ae06c70cfbf763c1bacfe48576fa29cff1ae9990df083813a1b64f5af2178130dfd7a7f315fc27f4f077eb5fd6b5fde4714cf57b537d8f0f9bae93d94f55fc5ce7eadfd6b5bde4444ffd5d433f0ca8fa4f2dc97fe4cf6cc9ef8096c781b433cfb05781749fc7f7a6f62de7f6df2265af8af359e87827e3746cf4344bf9ba5e74ef277aae7218fdf6dc59b68e4bf967913affc573bcfc3b7ff40f240a1ab7fcdc99b18e8bf7e7a6e527f467b1341fcd7e5fb38ebf95ae84dbcfe7f6a6fa282fffae15d5cf1ff0179ee140d76e1f9eff83ca4fe6e2fde4406ff35c4f3b7fc8df626b6f9af8c9ecfeb6f1befe3a5e7eb99e7c77f07cf021dfd48386fe298ff2ae8b94bfc0de939e3fd40bd3771cd7f55f45c1cf5e70af1260af9af619efbc3df64dec74dcfd735ef6393e76b8db771d3b3df9ab7f1faecc7e87d84f57c15f4fc16ff653d4b7ace4b7e60a9e71ef277a9e7af21ff9a9d37b1cb7fe5f30c3cf22399bc8dd667bf3ccf5f8efeb54f6fa389673f02ef2388e7ebd9f317a57f4dd473d1ce9f0bc259a0db8f5cbec1083c5f59bd89a0feebae6736c44f80ec19d8e047ea781f213cfbe57a13a7fc5739efe1f5bf0e38f04bc0bf96e5f90d58f6fa2351bc8fecb39fa5e7bed24728cf571c67851bfeb50ecf2cf03f363a1bdcf123539d6d1d7f6b7a1339fc571d6fb012cf7f69ef6399e72b7c1668fc912e9e8584feb51f678524fe35106fe38d67bf08cf5f8afeb54e6fe38a673fb6f7f1eef93ae15948f6afbdbd8d579efd966fb000cf57eb5920a21fe9e6b9ddfed67b1f5d3d5f013d77a73f9f3de759bf11c773f1fa1e93f00c2cf123a3cf79ce2f44f02c04f2afe978831578beb67a06f2fd48236fa3a867bf396fa3a3673f2fcf39d04f74f0261ef9af66dec7e5f355ee4d3cf05f399c15c6f8d74e3ce7b89f95f33e9e3dfb9d7a13a5fcd738cf6c961f897c1f8f3c5f5fbc8f6c9eafd5b3c2fed7203cf7ab3f33781b1f3dfb7d791eb6ff8155efa291ff6fce9b78f65fe5dec5e3ff47e11978e347eebd8951fe2b9c6721967fcdf93c84f3bbe5791e86fadd78bd8f6ecfd7bf8973fff5c53370cc8fd4f236d67af63bf41c3cf4af893d3fd2df76cf0ff37781e762a83fd787b34dfd3bd4815f32fe3535678365fe35e0fb48e3f9eae07d0cf17c0df0dc2d32f0cc8fe4f2fc566d0cf3ec177d1365fc57bdb7f17df6d3f0cc8efd607dcf423bff1af51958e5474e79ce197e16d2db287df6dbf02e62f8ff9aef238ce7ab82e7bee7def367e0f3d0fd0fb47a137dfcd72ecf4348bf1ba7b3c00a3f927956d8e35fbb3b2b14f0afc5bd8f629eafefbb08e3ff2bf23e4e79beea781b213dfb817917b9fc7f89dec359ff75ec9dcd7e36c8db68e6d96ffa2676f8afedf3f1feef7a1f113cfbd13a2b64fbd7e8dec60dcf7e62cf59d77f1cf13e0279bea238db7c7f8b3a2bd4f2afed781311fcd70e6f239a673fea33b0d58fdcf3cccef991109ebf6efd6bb4ce061dfc484acf6c8e1f293c2b7cff3519cff9d56f3cf1dc63febebe87acfe6bf12cd0ef47067a132bfcd719cf3ff237d8bbb8e5ff43f49cd37e76c9fb287cf6cbf436c23dfbe5e75ff86fea7d1cf17c1df03c04f11f18f22e82f8ffe067219f7f8defb9bffc6deb79e8e577fbf126f6faff1ebc8960feeb9ff771d4f395cedbe8e7d9cfc9fb48e0d98fd5f317a27f8dd3db38e3d9afc19b08e5bfbe396b1c7b53d2f126c2ff95caf30ffd2df73cccffdaf54de4f5ff2d78fe7af2aff1791b4f3cfb1578668d3f988977b1c0ff27e259e8e65fe3f13cb4f01fe0713648e85f0b7b66effcc8b6e72ff88fe90d46f7fc277c1e86fd0620bc89b2febfb37791c8ff17e70db6e0f97aeb4d1cf5fffd5978e15fdbf0fc19ff6b3dffef6f1a6fe3dcb35ff0994dfbc350bc89ef7f8df2262ef9af6ade4434fff5d07337fa33d8db88e7d90fc9331be54712781e62f9ddf69efbd09f8bcf5f62feb544cf9ff5378c6776c54fc0c37367f91bd6fb28e7f92ae47d5cf37ce57bbedc7f51cfec813fcc7936c8eb5f7bf02e12ff3fb93771c07fa5705698e65f7bdfc747cfd72fefa394e7ebddbbd8ff5f846776d38fc4f0dc19fe06f206ab7bfe1b9e159afd6b70cf433cbf5ba0e71cfd81939ef3819f8df236529ffd40bc8d779efd8ebc8b6dfe3f4d678362fe35f63918e75f1b3eb3697ee4823751d47fedf52612f8af76cf39c50f3cbe8f3b9eaf76efe39ffd263d07bbfc6bfd1c64fb91909e73951fb8ea6d04f3ec077d1377fd7f0ace0245fdc83befe3f1d9afefb908e8cf35e16c90c38fe4745608f6af4d78173ffc7f3cdec43bffd5d2fb48e2f98af63e967abeda7917a5ff1f98e79ce7172e781705fc7f1dde4652cf7e75ce06e17ee4a567219c7f0d7e1ff53c5f959c0daef891a0dec415fff57d1383ff35c1b331ec4de9ee5dc4f2ff157a1b733dfb393a0b64fe481fef63afe7aba4e7f7c86c9c1ff9e05d6cffbf286f229fffbae97908e33f10e77dacf07c253eb30ffe30bee721a5dfadef392ffa8935de460ecf7e63efa38fe7ab89e7a29c3fd7dadbb8ebd90fd21b0cc2f335d7fbd8e5f9bae36d6cf5ecf767d1fb08eaf91ae70da6e1f92fec6d54f4eca7e55dfcf2ff317a17d1fc7f959e9bec6ff76c70d4bfa6f69c5bfd4613efe399e76bfc2612ff6b836776c68f64dfc45bff5fda336bf78b2d781733fc7fcee72194df8dc759e383372541efa28dff6fc9815f0afeb52e6fa3a4673f31ef61acff1a7636cffdec9cf731c1b35fade722ac3f178af7d0d57f3d9e055ef891cdf711d7f355d1736ff93bd6dbe8e4d9cfc6739ef203519ded671b93cf7e706fe39c67bf21cf4349bf5ba7b36c9e1f29e17978f61b88f3dce86d24f1ec8776d6d8f6a634e8f92b9a48eaffebe7a2a03f97dbd9209a7f2df8366a7af653f3360279f643f13ee67abe3a7a130bfdd751efa389e72b8167608b1f09bf8f4e9eaf369e857efeb5ea735ef103c1dec7b9e76bf03d34fe6c7f13cbfc5742cfbde8cfc7b7d1d4b3df9d7751efff33f32686faafbcdec500ff1f873731ff57036fe3dbb35f3f7fe5fad76e3d0f59fc07de3ce7443f91c6f370ef778bf13e529faf209e59097f989537d1edbfaa781fd79efd60bd8d759efd8abc8b29fe3f1fcf5f8cfe354fef638de7eb83f7b17dbebebdc1d89eafb19e7bc6df979e876dff0112ef63abe7eb9fe77ce7172a381bacfb9199de60129eafbcdec405ff15c4f3affc2de04d3cf35f0dbd8937feab8fb3c140ff1a80b3edfcbbd39ba8e6bf267a830178be827a1737fc7f796781c81f79e36c70d3bf96f63cc4f4bbd57a833d78befe3d7fe5f9d7283d77a13f139f81697ea49703858efeb522efa28affefc7f310c9efe63eb7a83f0f781f8dcf7e9edec702cf7eaddec5b1ffefc273aa37f1c17f25f1cc4a7f2480f7d1eff9bae80d56e1f98f78a0f0faaf45790f49fd61fe3f47cf4314ff812567817c7ea49ab3c6b43725e5f3fd9ac8e9bffabd8f5e9eaf79cf3fe62ef0f7f3c02f07ff9a97e7219ddfadcf73f1d39fcbc35960dc8f749f874c7e37a7a5e72ff93bec6dcc7bf6b3f0266ef8af776fa388673f01efa1aaff3a7c0f4bfdd7fc2e8ef8ffa8efe3d8b3dfa8b346b537a5e5fb58e8f95ae539dff8812e9e5fe5ef006f238f673f0ecfec971f69e01958eb47fe7913b5fcd73ccf0ff4b784f711d1f315cb737ef303afcf454f7f2e0e6f23ac673f41cfbff5b78ce760801fb9e87da4f47c25f33efe79be3e791b7b3cfb9538cbf2f99113dec637cf7e3f9e73991f38eb7908e13f20e24d2cfe570767854dfe35f7997df223d39e59eb8ffcf026cef9af929e813d7ee491e761d96fc0cb73fef1033d3db39d7e6486e796f037f5991df32311bc8d5e9efdf29e81317e64fccc5efa91169ebbae8b62febf47efa28bff6fc8bb18fdffbc3cb34b7ee480b35f1bfe3532cfc1253f72d6f341fd1de2b948e8cff576b6717431caffe7e70d26e0f98aea7d4cf57cddf3260ef9af62ce3695bf5dbd89bafebf03cf5f67feb545cfd9d45facf13e963dfb953adbb5fefce06ddcf2ecd73c50e8e95f4b72d698e04d09d0fb989ffd2a3d77ac3f377813d3fe6b85b791d2b31f99e7dcf713439d0d82fd4844efe299ff8fd2dbf8e1d9cfec5d64ff3f096fe3ddb3df84e760961f793d2ba4f1afa1781b4b3dfbd9791b233dfb8579132ffd575acfaffb4fea19b8e247f278ce997ee2a3e722ab3f9789e706f0b7f24d64f25fd79c15a6fd6b72cf79ec673fbc8f549eafedf317967f8dd0bbd8f6ffcd780626f9914ddec744cfd72c671bef6f4eef22d9ff87e1c02ff75f73f33e0679bea678176bfc7f4a9ed9087f189103bfbefeb52acfc21bffda8a3751f95f8bbc8979fe2ba667e0941f29e54d4cf05f3dbc8f959eaf65de4700cf7ea0ce06bf7ea4a13771f85f193cb3877ea4dc73867f60a577b1f8ffcdbd8b3ffe3f366f629fffcae9994df52341bc8f6ccf7eb1dec430ff15d0f390c17f80c3731ef103fb5d34f3ff4d7aeee2dfedfbd8f77c7df3dc72996df3231b3c1ff99fd2d960841f59e97d4c3e5fe19e874a7e37e673f1d49f6bc45921827f8dc2814259ffda933791fdaf07dec357ff15ec4dacf15fab6f62f5bf6e791bed9efd8a6f62f3bf2a791ff93c5f99bcc1463cff0d781fd13c5fa9ef23abe7ab9fe721a1df6dd2db48f7ec277c1f393d5fd9bc8b6bff5f89f770d57f25be8fc867bf57cf2ff2f7f119f8e447427913f7fe6b96e722a63fd785e7bff91bc1db98e6d94fef6d5cf5ecd7e77d0cf0ec677d137bfc572e6759f74712df4500ff5f85e7e1f05fd3f5fc577fb3781ff39eaf16dec65bcf7e899e81097e648eb7d1c7b39f893731d37fb5f53cf4f3bb217a17ddfc7f9bde60169eff8c6fe3f2d94fee39dffa8d8dde4753cfd73bcfdde1ef3167837a3fb2d5f397ff753de74d3fb1ef4d4cf25fd1bc8d419efd52bc8f709eaf40de47e9f375c3815f61ffda95b3eca31f59f73e129ffd38bd89a7fe3fe0fbb8e2f9caf6fc267f933d0325fc48e5d9a0a37fadecb947fc3de7d9d0dbc8e8d98fcb59a0f547e6791f87cf7e9b9e81217ee4f34d24f15f9befe38267bf5bcf6cfe8f8bdec706cf7e7e6f63dfb3df9b3758daf375d59b98e8bf9e3a1b9cfe4856efe1a8fffae70ef0b7f319b8e6478e791e92f80f1c791795ffdf943711c27f2d7116987fe48af750d77f25f0ccc6fa9124dec707cf7e7fefe291ff8fce9bf8eaff037bce397ea08c77b1caff27e84d8cf45f5dbd8dd0673f0c6fa2a0ff0aea7d94f57c25745638e55f633e03bffc482c6783c51f79e8f911fe777a1fa1cf570cef2283ffafc5db38e9d96fcc59e1f35f8bf1dc667f379ffbd19f013c7fcfdf10dec743cfd72b6781a57e249eb771d1b3df967751c7ff07e55998e25f23f1fc12ff5b9dfdc2f4af917a13effe6b90e7a1a2dfedd2f390cfef56e84db4f45f679ded5d7f6e7b1b133dfb65791381ff15c159a38037a5e4f3ad7fa3781bdd67bfb9f711eef99adf47bbe76bf16d14f3ecf73e7fa5f9d718bd8d4f9efd6e3c67e4cf3679fe4af5afad7a1e12f9dd74bc8963ffd5ed1918ff48226f30b8e7bffc3e2a78f6b3f5365e78f63bbe894afe6b9a77b1c1ffe7e27d0cf47c8df236d63dfb0d9ffbc9dfacceb2567ea4da7b78ebbf9ebd8b28febf1e6fa3a7673f37cf7ff09fd3f3d0d1efa6e9acb0c2bfa6e179a8e13f407d1b3b3cfb91bd8d649efd783cb30bfe30eadbd8e2d9efc0f370ee3f107d17e1ff4fcc33bbe547ae3d7783bfe1e7dce1672d3d0377fcc81f6f628aff0a7dce217ee0dfc537ff1fa76736d38fbcf0263af8af239ed9143f010e6f629dff4ae93947fa8938dec5e4ff27e55dbcf2ff113a2b7cf0afd9bd8b7bffdf9903bf78fc6b71dec5eaff97e6acb0d4bfb6e43d3cfe6cfd366a78f60b3b2b84f2afe13d0f27fc0774bc8f2c9eaf069e8738fe03a4de435cff35c0f350caef367d6637fc048acf5ff437dd9be8e2bfc2ef639ce76b903791c57f95be8984feaba8b731cfb35f92f7f17dbe6a781e02ffb5bfe70ef4e7afe767f97bc059b6ec07fb741668e747a2792e1afa73c13dffd2df18de6062cf57516f3013cf7f049e8542feb5f6f95dfe4e7b666ffcc8e0fb58e2f99af69c973f30c7f3d0d2eff6e96db4f2eca77c17a7ffdf98f7b0d77f451ef805fdd7e09c151ef9d794cffde2ef4acf40f8470e791fb33c5ff1dec736cfd71e6f62aaffefeb6dfcf4ec07e72cb0d08f6cf33e923dfb917a834d78bef67ace7f7ea1a56776d18fa47b83159faf9d9e834f7e64ad7771c1ffc7e24d44f5ff099f81643f32c69bf8f55f17bc8970feeba3b35f83fe354d6fa39d673f23ef23a4e72b98e75ce71726380b1cfec816efe3a0e7eb9437b1d4ff97cf0685fc485867bf94fc6b7a9e1febef176f30b7e7bf9f6d1f7f8f7a83913d5f49bd89b3febf00cff9ec6791bc8d9d9efdda9c05aafa917a9e1bd49f05bc8940feeb9737b1efbfe67a1b413dfbc579ce6f3f1be77948e33f20eaf9d233f0c78f4472f6abffb529efa39de7ab9137d8d9f3f5d4db48e8d98fca1becc3f39fd9f310ccef06e4f919fe9f7a1602fad77a3c0f09fc06243d0b05fd6b3e9effe4efb2e77ce607d27a838178fe3b7b835178fefb7a13fdfcd74eef2cf6b31e9e8726fe03489ed9be1fa9e1b988eb2de6e60d36f7fc173c1b14fb9189de470acfb9fe3fb6e77ce507b27a139ffc5737efa3dcf315f82e8afd7f16de452dffdfa13771c6afe977c37516f8e747ae390bccf323d3bc8f4f9eaf379ed9323f32c173e7bf71bc897afe6ba63758d9f3b5d4bb28e3ff3bf23efe78be9e781b053dfb49391bbcf12349bd8961ff55c2d9c6f567086f22a0ffdaf79ced7ed6d1d92f32ff9aa2e7fed3c42dff55cfbbf8f6ffdd781b733cfb497817d7fc7f98dec5b4ff0fc433d1c6bb68e0ff3b71a070d7bf26e5f965fe469e65a7fcc802cf6deacf696f63ac67bf416fa3dcb31ff0f931fed37aee497f167b1e72faddfc9e5fe3bfad7711efffb3f25c44f4e78a7b66f97e24d99b08f65f233cf785bf7f3cf79c3ffff9d2bfd9b3c1093fd2d233fbe247f6fbb8ebf90ae99d01fcec863751c27f45f1267ef8afcab3415cff1a83e7dce607fa3db3707f5899e7a195df4deff989fe9e7b7ea1bfdd9ef3a09ff8f60613f1fc27e06dd4f3eca7e47decf17c2df12eb6f8ff843c7fb5fad75a3d0f65fc07e43ce7e39fe3dec617cf7e09dec51dff9f96e7b3fdaff4dcc7bf93cf5f53fe353f6783d01fa9ea5948e75f8bcf022ffdc8396fa2dfff57e039a8eb5f73f00c94fe48216fe3a167bf2b6f63f5d98fc473f0ca8ff43b0ba4fb91786f639567bfe4fb78e1f97a7c6645fd480eef63a7e76b9bf731c9f335c69bc8f75fb53cb3f18f143bf08bc0bfa6e540a1ad7f0dca9b68e0bf6e7813fffc574f6f6290ff0ae66d44f5eca7e76ddcf3ecb7e44dacf5ff0d78173dfc7fd237188ae73fb56721fcafd1781745fc7ff1815fc67fcdcabb78e0ff43f19c63fdc61befe390e7eb8ae7dce717163a2b8cff351b6fe39267bf19cff9d24f74f42e06ff3fb7b7f1c7b3df89e7ecf71b1d3d7f35f9d7f6bc8da39efde8bc8dd3673f0fcfdde7cfc1b341113fd2d3d960ff4842cfec991fa9e06d3cf2ecf7e24d8cf35f21bd8d459efd5abc8d679efde277f1c6ffc7e43958e547e27a1301fcd7b66760901f99e4ec17ad7f6dd6bb98e6ffb3f4268ef8afccf711c3f315ec0d167cbe627ace4d7ea0a97731ecffb37b83c93dff01df464bcf7e66de4614cf7e6a6fa38d673f086fa2a7fffaf70c1cf223953c0f85fc6e399e87767eb73fcfc14cff1adafb18f6ec17ea4d9cf05f533c67083febe66cbf7ff7bd87a7fe2bf02cb0d38facf326cafd57176fe3a067bf29cfc0583fb2cf7bf8f75fd19ed9ba3f8ccffbe8e1f95af6263effab93379887e7bfb2b7b1c9b35f8df771ccf355fa3e367abe7679ee077f4fdfc4b7ffbae23998f6231bbd8db49efd0cbd8b14febfe473aefbd946efe2dcffa7e3393ffa8937ce7eedfad770bd8f6fcfd77e17f3febf2def23ddf355f8fc13ff5dbd8d809efda0bc89f17fb5f23e6a79be320ffc12f1af997917f9fe3f34cff9c90f3c7556e8e15ff3f03c64fe075c3db3a07ee486f7f1ebd94fd3db18e5d92fc77310ef475adfc4b2ff0af73e8a7abece792e6afa7365780f5bfd57e3d9e29e3f57db73d37551c3ffb7e32c5be44706382bc4fbd75ebc8f749eaf44de47b5673f57ef63f4f99ae16cf0ee47963a5028e95f33723658e75f2bbe8f799eaf49de4457ffdff1d9487c9ff979ce6c3ffbe4acf0eb5f6b7b1f6b3d5f0fbd8fcfe7eb8567b6057beb479e382b1cf3af41df461ccf7e6defe3def355c473b0c08f74f4fc65e95f1bf526eaf8af42de6066cf5753cf6c8c1f99cf36fc37a837f1d37fd5f52e3af8ff5e3c030bfc481bcf4331bfdb95b731faec97e199ddf1238defa38ee7abdb5961887fedc3f349fd3de27938fddd5a9c0dc6f891a2ce068bfcc8586fa287ff9a7c1f793c5f393c1bc1de94729cfddaf3af557a66f57ee4d8fbd8cf7e949ebf7cfc6b749e591d3ff2eb6dacf0ec477cce1f7ef6d27b68eabfb2cf6ca01ff9f62612f9af63de4436ff75d1db78e9d9efccbbe8fe7f559e7bd59fd7ce02e7fcc833efaf59ff1aace7af57ff9aabf731eef9ca9e0d22f991b2de44587f901fd8f7cc467f24d83b87fd2c87e722adb75897e74367856dff9a85e7a2ab3fd7897711c7ffd7e45d34fbff34bc8d469efd5cbc8f6e9eaf3ede4535ffdfa5f7f1c5f335c1739ef0b370ce0651fcc84fefa392e7ab8c67a19c7f6dfa2efeff83f0269af8af3bdec418ff95fa06dbf0fc27f69cd7e4c37f03df452fffdfa267e0f34702791bf59efd42bc8b4ffe3f3e6f628eff0ae4b908e7cfa5f6366a79f6633e03b3fcc82acf5f86feb54def228bff2fc8fb28f6ec27ea996df523453c0f0dfd6e949e3fe5efb3b791d3b31f9be7aef4e7b13798ddf35ff15d24f0ff857806cef891d4e7a7fa3bc5bbc8e3ff0bf3362279f683f15c84f4e7aa7b1e6af9ddaaef628effcfc973a9e71b67bbc6df98ce067ffcc857cff9c02f15ff1a9a3791d5ff577c13b3fc573ccf45467faeb96760da8fac7116f8e947da797e25f60bc3bf36e6f9ebcdbff6e84da4f05f633cb30dfe30bde7e1da7f80f82e96f8fff6dec750cf57396783b2fe3505077ebdfd6b609e3bd39fcbde475acf5743cf17f5b7886746cf43e1bf96eb6da4f1ece7e07dc4f27c759f83a6fe35b667e1827fadee2c30d78f14f47c3d5648e45f4bbec11c3c5fafefe283ff0fc6fbf8f77c6df4368278f63b7b0ecef9d788efa2f0ff7b7b2efaf97349781b033dfb457917f3ffd7f63ece79be0e79135bfd7fc6b32c921f29e04da4f5ff097817c7fc7f90ce065ffd6b099eb3809f4d725618f7af61781f6d3c5f213cb7d8dfcbe72f24ff5a9eb35f7efe354bcfc0e88f0cf29c5bfc4000cf46b637253f6f63a167bf2acf45547f2e12ef61aeff9af66c64df667f9e59b63fecc6f310d0efa6e819d8e64792791feb9eafc3e72185ffc0fb26fef8af5edec4e97fadf206e3f0fc37f60c6cf2239fbcc1483cffa13dbfd1df149e8baafe5c25de4509ff1f8d03bfcafe352c6fa38a67bfb5e7a180df00a4f7d17dbece3d3fcedf09ce0279fdc8416fe394673f1dcfec889f8085f711edd94fd5737ef103d1de4717cf570467f3829f9df21c9e06a894e8dd6a4296c39442c82003061999a199090001d31340e09854220d07b3244e6154b4091400065d9c64f1ac064284903106000000000400004000000001ad10f607323d1ff3e06cd495ab1c7493d74b82073fae0c073e3a2a48619b457377b01d5ed0a5f9eef2704eb48863ca81cfe1d5251fbe8371797dfb5f22b399623324310b11eda36616c0d7c1205deab1722899cf10281c09978c1f52c5a5b2b807bc31efb2d2dbf412cf59fd6a8764b92a43b143db447033df223522b0e1cd50ebd3f222dbc3da38f2ae4e663878fccd5dc4824364c13bf1d022943b23aa43b1e07ac6f394628595e0f65e0dfeffb11dc7a99b64c5bdbb8bd4eda0ae2dd79de58d88d5d1542757dc77f49ea82fc1d3ea5d32697afacf530fcc4c6e59a278e23255c800f690b1b09d3b0517c6c7ca1b3d34b8196b0b47d35a4f4619e82cc94cd6f7e399f0f2d00b6b1054f532c2a8fcb2bd5b15c337a7ece2c36558b9c4ff6a50d87e298a13b208ee529d58dc4ac3a42606fc4ac4961b49e9f068f0c3453aae4f948f15fd9e4d91bc37ad48dd6941412ad32d57593a0da9da092dccdddfece587a5089e59e09afe589702247c729438170181bafec66536339ce39e7d199d6fa61ceea4e8debdf7bbf49044b72088f6d0420bb93a19bf22911ec76ea30b7c89bd6882a082b2fb57715144e593526e93c506f0a2509f498bf609164e1762dec085139f45e799a8c421ed41840cc6fe37b17b3386baf8be04f265c8a228f327a80427c08744d005ff57c4bb5ca169bcf0fa2027bf44a3f1c340d25ae0a01d7e188813bcba9f47c5492bf9e8e8e54cb86d089862da5a735ac1c203270463b1613b183e2ca4470eb277145e73f467e18b8681cf33567dbcff309111017c45cc0da5e23332e3508c3b105d2bb7aea832db1471fd47d60d321d8264be5efdf13c31d648b2efb8c77fba2b9ddbc1ecd4c002f6327c8364555ce106dedf45aee10b720d2a3ff47e7370944cba54529a868f7022961ecbd45e0cb63190967d3b81d9b707b206a30a301fa067c14da122a6bb9cac0a7c06e030bb402fb330e53328bdc38feaa0bd39a7509e20f0be6c51e18792a0b42476fe62fccbabb6784faed9e5349a86994233e03831cd37be4f18720992d073234d2f23fe83dcd4b5241c021ccf86e4c8817642dcabc1d6b5a05c4653bc806e320969f620e7f1d3ab617d20359ac4636d8e25fa8bb75c58cec8dc3513954397e020c17772682c698ba4d3069f50ca78b98790f1bb3f2c1b9a3224b0635183b9067136bfcbaf777d2bf3a44448d8a1edbc193ee0a8574025ed8098904d5e9e0f34a8dfe0161cdf8beaafd7fb984fb8de96be6d2d22ce48db8f82d4e9a8740ff93ffc18ccae489b42e328f67c97848fc842d797c2efebee9ce57ba104e6c20359ca83fa6f726a8000238906111ac54fc4fc19a0b413285e6145e641f19c51df1c00e7c7236da11b1508f9e1813d15881acf8f5d4352d683eda39a08fa51fb1701177c8b279cc92dc0ecfacb5ecd914f3acf6d4e7c2c3d49f94620601c72e0bf31dccb01ca832374ab0af901627834d0f23ca6afdd8758e2bdd5cc449bf51304f64de11980f84b5542887c6d7510f02dcf4945e7341f95ffdcede140d81afc1e0b2db01c9dfa5c2ec94bf8bf28ac982b1b16ff2458241f8a5905bf043e73e059072c77eaaff9cdf0e1133fe6c896455799dbbd4e5ade0ad4135aa32b226f06f0eafb483d061d71034aeae58243f8bebe1bb91b9fad000e3c106f0769928cc80e1f73e8774d070d634f8076c9770a38e96c86dcc676ed94aa16db3c57372b80fba80fce9f3e745b89417541684907c433d633757551aaebdf631277fb3e1d39baead97ef898b71a767b288fc22ea763ff65cd427409518c02ede254c27da870bb646e2c78df1443cc5ffb9731f9f996adf4510c1d88fa0bb5fa2cae29ea5dd5b5317e61006d554453fa477c28120573526afed3c5c4a5f14dde1d5f2be230bd807e4138bbdfb1d2fbf6cf37c243b211dc99a0c12d85a615deddf424b2d2e8c74f4f543c202fd5400fcbfc9bcc058963694c4ea95b9e58ade7f90184ffc18ce883b3d007ede04c5a086d39685596a29a1c151e204c81703fd518f8c9d3e1e79db0a5436a6699ce507c2fa0c4f67d189ba29bfb7fecbeffe8fbafe8a07204cd2b4271c17d520a5bd4660240df0ab834532bb0084daf382fb92956cf4791741e43281e398ebae27843ef9627381e1abb38e5b7ebaef0507a96fba52a43fd0e0a286179df4b24e66ef0c10f2436be063f88703fd5177a0ecb32314600a8ec4e9b86c704c4f701edb9c381e414d43971f7186e5951fa56b2a5bfdad306083b2c4d2ddc70bd6361190f2c4f340c48d12962fb2eb4fca0969c08b4b5888ef52ddf4a74118fa511263ff3ac9b4ff1ea746f5ff6699bdeed71d855875332a1c7af42e46753ae2bff1aca7b5417c278ab477f2a509c1e6ae4a0ef0d16c4afdc26bdde5cb41e9a31c48279c9552f5ccab1b01925171cefb3cbc0ac23f7eeddb90d0bd2550f03c22a7986daf7198ccba1bb365cd57e79ecefe165bd376ef4b1f4950757261cf27186bb6d8459f695e0fbbc31fd123cf29f7f1ef223fb09433ca7c267fdb08c6c5bd3207f82c2022296e07fd9e18c3ecd8c9502fad34c9c3d311a1f2612abaa86a2447d185222bf6f5b478b06349415bdad89bb720f895fcfd6c3c536bfe1d1eb31dc89e6e67cbddec435f1e7f2567cbc3936bdc40ff4a6e006f343e68f7e263cf6fdd2f85f9c00edf5b8cc2f9991999d10324d76f3e0986e6e4df8d1cf5e317de15f897931b19723fff77cafdc47715c270decff3e6798d0163ff5160ceb7fe4c6fb7971e560101b00799a71e95affa62ff13e53c8a7514ed7fb13af6b38121791fe36459ec2cd7f21d79d4866cde041f7037cdc9e647e56d3783b0c1ea189d1e9a5618176bb748de6a27eda95bd9627d717658d760841a6bff2459d7533c31338af9cb24653b943e54c551c238d99e31de3d85d0876abe37273f0b8fc51d4eeb4a2c01aca8d51f14847fe5c75d22f16f24a04da06fb70205a971aed11e33006fede74e1441976e4b41a24966d4043efdf9b1f4c4dd4bd96e0afae53f49c47a59e2f47ee5feea3a7644372ef23930f26278b2cf777d8e35f6acfef0d1a481e8cd406191a91ebb81849343422c8130b95a3307e27e824cfb61ab6aea4db35525f389096bcc35ab172a68e4a616cc7a87fee5a4a429806923b56ea58294f07ca6671d1ab8a962d4bf64ec53a9edbedb8ccce4dbceb9175b06d2c3b8792cefbf765ee74bdb3ebc55089a4c3280f396aaff913f8986fb69e8c71b4d8adb8da1fdffbc603ce796a01d69f80db80ddf45bd62712846b5c145bc56b8503f86ccd7e3b9df24206dddc35f65ea98af6aeeacca573937c9bcd697cc8ebd4c9e98d479fbd71306e2f40be980bcab0dd002a5b1b4fa9c70803ca1353818cdb0da0a255a3147b8c20a07c31159441bb0154b63492520f1105c81f630119b51b40654b6329f41041c0f98b695f80c93f0beb95298e4f3378097c6751e377e9f6b68f37a578cb2b639c22fac8594e649cd54e88966941e063407a9f6bef0cd94081091bd1f592b562c8881c0d3c982a9e3d9cb0115d6f17d9834360fe96d53fc2e6d5c47f7232a9419a40916da52d916a8494112c228a86cf256bc7940d5ada9259b15423a64cf0f25675dff414117792b762e48297b56456db9cf8bd03b948db3f4e66350c52e89957b6cd59a3512a26195e2f293ab6658ddbea3883f165643beaa7f7f4442e62ebd613b707188567e059100cdb00dd6cdcaac8fa2a28985c1bd108cbac50b99c61f03a260cd786e849c55ac73c334f7a17d5162fed704df7e20eb6caa49bd1741c86137d55adaddb63e30d67f61a2d758b74c87e999f3635d9456905e23fce4e887061246cc6eb07e3e6e63269fa31ebabb22f1dcaddb65d2866d9c323b39e23413248c23e11bda4fa58741fe31fe4361c6487a067c519e40b22ded578252ecb936d36c8a7bbe5a4c4b8cea73ab7ca1bb373413dccb99d6861460a6846fe976a65a7d6f6deb1099b76aba6c06af8e56a9be1743fa07602f092bebfc99aba713c9e75792a34fe7833d68bb1599089bcc29a39a682a5e43a7cec12e887e401487d3dad9b65cdee286f2a535a55515c2412b326deede35e96c4074e6d1efe3f02a6c13d42736968693345dbfc60924ea4109a39bcdd57ba6379cd8d092f554eda541ecbe1cdfb4c77ce8aacbd342c61a9b29600ff03bc36f0741e2067cd8c4c95005919b88cafaf0b9221d2f0e6b3a1d7cc1f9899b7d7930e8f7e37fe53b1e5aaf4796a2e4fbc140cf7ae9b515b3f08a70c5f3b87be7318bd5d4b5b4dc9759064ea3076ff09cf0c5974b67a5919025aec5a9b1cc8c8705a28cafa5abfd327ec282ffdf6b8753417a73e9036367c85d71099207474316fcddc7aa66ca53bcee837ccb63999f81879e6fca1b77ee613efb20d5cb00fadd208f8eddae5ad73d1ed2db7616f1cd45cb981c73d6b5a8e1f3300e754bee56f380617f4d1c4c1e5cf51fbfcadc66ae9ecc08e90b2c784d8f52ff6f06a370fa6befbe977d831eccbd565e63b2cdaac5e93427371b016b6912bc0fbe2274a620e96504b36d9526cce62e691f95ad26129e551a2bace2c731c8b01e58c2565ea10395712ad8d78146dc02cb9108785b9b3f25fd26d440e599a0bf545d3843f4e33792e7f358071f5de1a57daa534cbddf64992f7ae0a2bd478c23bab9d8c0072616797394cf5e76f1bf9580ab206affdd038ddd5b938b05fb4f6c699e1faf19125c6c399f53d03cd52b1500d8c73beee3742bd6d6c98c5544b0cb6c72ddf672c1c0d5df59915b16676b6d472326d5dbd12d62d8d8fa098567d69db7d9536c65b01b2f019a74e69847bdd4037b08b8eb199db6b9ead3869ab6afba662f5a6de7255a06094ed2db97d0c68953a6d79ef5cfad759a3d8bbccac5ce0f62cb32b77d536fb1b4bd6ef59fd2c49c25463af55d2a6713667b7261f2427550399b92ae0371fa0356309dd9898349f6b6c5c94a5ccb9ebb4af3aac8ea1e703e2dc0f7d2dccd7a592f0211c073f444484b3d8797f86759a865c5b6d788276f1da74d433f3b55d6bdcc6006c842504fae72f20447b1ceab8beef75b3c6fd871898269f4d8d61e7f65ebf7aef7bc5beca34bd4a9f59b5f7b14091d20d275da8ffe4b24da2fb70a31bc5cc5c3efefb4e93abde7635157b731e69a9dbf4fc344af081ddf7ff31ffb492318196ae078850fa92e9b5b22ca5befd87716577bd33de19adfa27d840e4de7fba2722c9ff764f557b67e562e925960af5e634c9b61b8766fe830171792f3274f4d9ffd523b7c26ca75ed171f3e77d1930a71a0bba71de748be9c3ddd0d59b8af53ebd01bf7e5cfaaf0febc17e641e76a3788161f5b2ea479f09edacd893b43ad73f1d5f98f54ea078ba373a7ab76f6c28614effd63966d81bccd09e1b1570f4ed3f46835d6fb52589f63f8c8918eb08257c1d5cb138e96bf254e01debeab22d1b07437a097b79092afbe9360b3ef61d546b04358fac103cf23236d87a7b20b1c3f9e21f7f5d12caa0be5471ca75331bd90d5e7c68f4ad0d3786cccf6a07f154dbfaa7bd3f55e39c5b41b72e047dd54d6f66d57c480032eb1310d08dfc21b7145adf39cba5e083b9ff88f55bfc6ee4f3777ab5086b537584b245b5d659f51890789010734399de7749731383583d15641503200346675f3bafade5f458682de7101bc28844a35b188ebbf2f5173439f2ac323c1bbcdc5ef8dc7e9de64d7e70392e43e0cb615791b8d58b64b54e5e94e6c0eeb92a31780c31893d764f9055efef1ed50425be30e7f2eea7121a5a8457c09fac4ac5e0c7f0265d9168a70fb029ecec45dd0db47c9f1eb00f2fbe7582dd549c58f6dcf0eec874e6b87dae5d890aef5af389b7309f9963e15c0023d19a273a3722de67d420aaef13eb29ec05040a5c2a2b92e241eab7e79b9a7b36e54676c6178a27ffd9c1c12477711aba2f63373a488c1ace992e0e9bb3de3a93a43787d898083744e573de45e2078a0caa0e1cbad7e75fefabab33379b015b6c53879a76e0eff1692b2f5550ef702d6cec11a05f8b4bb6eaddc5e388ff6ccb18ce91cb1519c4ba2df7f11f16e44fa9b3325da023fd323fe5d2acf1da11461d22d6628c5249b7cdf5f5bcf51b2ba9fdc23ced73d82135bec1638b7be9dbdd7c2cf8f8e625570fba8bb46c6de003c96c1c317c30bfb4d76295f70447a0ea8daef198557819077908e8e7136fc31ae39f7d956a7796432ce74560516f7baa01af16ed50fd72fc714f2620ed4718bd896ccf0a4c4b2bcc6c09ffce84c45c3413e2e92d4e2407b8e0125d0e5f8ed8edea222d37cf9dab3bac5f1a65f1a9fde18ea2970370fd9b4710d93765701515f3e1ead431c329f8f44a58405615406d4984bdb1748fe3ea630eac566c9a20c3cf255bba7d27a59d537fda576dfd4f68a96a28cd792b3a0dac41625c513925461fd785583ce61849ba4dc3f651e57ed3f6a0be9707e3dcb7a1a3815ab396947cd6d468252911ff42a3e121e0aab922ad92356f55bcb8f3957adb12d37d1774dad7202e9c5bfc49818accd686fa1839310d691b4f77a0d6e2c24784f50b59625fa8523ead03176306c64c1f46b449f58f5cf80d970ed6f792acbb6ef6367565ed9a639388fcf5d5dfa51116da0aecaeb2c13957635e86d949801eec2bbee93386e7bfe3880e1b85159375ef980fda075ab0d2074aba8521fc5ef330e0905746ca4b12009304f89e34f38beed86248c95b1934d9d112038d4f5dc19993f6b2b70343a1a5c3e0de8e8c5f1f6a1bdd80399db9ba330b3542386e1afb904e71cb996b0b358bd8deea2e1ddc26281d1c055972a3f27de5704274b60d560110bf5e09cfc6fdd2ef0484e7ebf821da883058e5dc75605b25073d9c47779c54e8b028d7e2fdd7dd9203c19b1fa63e02d97afa429d7ba9952b65ff3c2963dc8a60bd0d746e92baedae897a02dc6db5ce4b3e6ce387304de16f8fed5be6fef4762a20b7aeeac5d7e0f5f3c36370a262fe05585078b51a6b7039d40c10b402800f60abd1e55f636b547f55af08c7aaab587e6c987f9ff778725d287d906c6adb6f52041b078c6525bb9c672fde7cae6a390b1cfcaaebf08a1d17aef461deb3edd48995efea107264dd20117b9dfcb6959ca5e3126e4e89265eda74dc5793515e7135d86d895ecb3f042aadabbb5fdd34f8c74d7f3786ae7f60b8d6fe377b031f5bc8a0cfc5e73612c3ffdffe2713bd23fd0b4c6db218c8923c91d6e612c5dee4d7a55dccda3b5e6e32431e1b304aa44070bcb0fd8098ae7c5c1591df994c5c4c9e754ce6b59cbbd97a2b056e956162cc7020003a2f579cbc5cff2c728d10a9b3595b717498b7211da83d942ddcbe6035123193ced7a7df86510e435e88780ee16158e5cd2345c3d44afacf1b0551f510098f3c3a0b921dd1569bc13cd54e8f7853d3bcae78f62ba428bb7aaf153328926ee77fac5b8a3672136fab27d03e51e485f5230a0362cfa23c31f9be89b6b73d2a15504f00cae99bcc1b122670d1839133ed478d1dc26955b8c0513e72697996eb8be351aa59d8c92cd983b87733cebeb53decc61013b822db066e71bb7f97f800be25fc527ab159dd2af7a0b35d0f4968728ea0d538636b2be9c416e7b5e3b8e03a146afa0586f2a8670ff8caec6336c04550eeec3885b738d400dfc1d9824a0f67a6ce47c13f19b8cf6cb0918e3117cb35729e40e26cb2e373b3cbdb6363006fa47000b36702fbb98cfe9f459e20becd8bba93a1a9f4fdef96a7c88c4fb9e423e1f1abdd03bb0ce3f971bbf341e55501b9a6bfa7bd0b76c8b3f8c9c931b82025c4befe55494fcb775b4bb41ef0bf6a94c35d30953d6e7e4d007cec5c35acf54997226ec6c086f96d956225ed0c129a802f98cebf36a0dbc3c71c1d62657d9d7a51f3cfaeb7fea1c4cec7861011d35fb2a7badb4e2393680fe2f1532e3a59b526235ddd2a6ce5da2cb3e30cce7ae7b4de643356efd0eb23ec6ef07ff426f610af88e4e85f6a0bfc20fffb8c5be35a3d864e59d7ceaaf235ee800fb20082d7f83f6b51d84107b9c74a5a9d78e83c27864ea1b5faaac83686c2ff84f03ff5afd736f5b56207be6b37a29b41f6c7e167cbad938a867d21fcc706f1210ebfc41ff16ed5757b20ed5f81a77c4e99fb0183eafe0c367e0c075569d500e224ea113f10e44a65ebb8fb84187b8a90f74a29afc0e8c99af5a27d2b007c205af4a28ee8afa63cd7cbdf821d71b5ee944266c19bc85ebae4d137752373832ef083f94e4ce133b5d609772c0b4d56a9a059cd70bb6961b03345d377f2e166c281b06f39cfe241342854ebb0878e541efa13d963befe34d0ea1fc2d9bbd254ea2010b26d924049fe58c63ff6c7c5cb8dca78f8b0b2ff06b3d71d3b3b16a303fbe01998d8499d2dfe0f08c09dc1680097ae48c8d64b9b2dc70f2aafe29cf417c0b2e6afb60c70398cfcab66d605dc5f575e6fe41997242360a86bb906dcb8f638572decb3030ea6a19f0403586dfd07ff376d34888cf20641d4f1a2d79e2f6c309bfc9e9fd27ff0baded303b68c7f063aae34cf7429abf37095627faa985d3aa66f48c6cb29344137deaca5a7466e2f384b1e985e7fe69573fd5082b3b42957fb20a473515ae55b15caa0cae23e2d407d0d948ad748e01f1f10be65eb484f4d637ef9417cad8b6131c91970d47cf6a2c2ff0cc995b4a1ac59c1db753d92538d9be9afe786f1013f81edcdd6a52158d30a5d55e8b4752984b2c6dd2785c82e9b2166c36e9fdb0aa6fc53b365d5972ff1b6d44f379e8438fe698c5e528bbd13db5ef39d750e4c8d29d466db7d44adf359e2b70e480e6a1f055c17206aa1171c98054cbb5067dbd5e6e84077c8f91c9aee15913c1a9e9f116cb6f0480664ae9becebdd9933e64950e2ba304fd0e87b087253e0b7bb1624c4cd3ed42e4f72727d262f9bcebd5b07de8c1e13f59ac4a3f3a25c99dd50ac0ca5d510ac86663534bb43af1356f227d19e81e74c557a6f29013fff10302e1403c18f74d4dce6e886ae5fc45c6e90042a2199265840f5ae6783b1d0c11f68f857bc1100344602e746f3e228732e54bb4f27741619c2633ec1bb9bc909c79506647abde091ed7231ab1c79ee2ad667e57cbf490bc6d8f3a682a5dae08b731134b0dbd2b109109905c524f31ab39bf0178127d78fd1cd369ce2e9d41cb9a47ae3b78fc5cff04b4c23fe463c27f75a7887e9a2ffc3857bc575dbf5356d87b7c45d53c627e43613c66ddc7fdacde7d6694b5bdf9f32273641bbea53e5e0202ec878d6c188d95b1a73b13acdb077b3f4630b690ac18eab4aee9df1d87cd066f93b8f1bd37d14e190b2edef06651bffe61ac1f7233dfd79b83c5b2908e7007b8aa21dbb231fc3307ce20fa9a639241921f17d1753d822084bfc0f7536eee4a1a3d3d6df1cff1ebbbfbe45f15c25f851154335c2370ac267d83822e017f279cf91833e6cd99d838cb8f95cfec2aa4d5501f854e908e7c63cc22787987cb18a5ec3b3996d838ba7293a145a802d5fb844ffb6e9d6559ed267fc2a0eda3923b11005c7225c45672698f1a768db1e2f10f027fa100ded30818714ab6c0350fad27bfd4808e84524f10f081ac43ca6fdf79ace3b74ee07e1d3128fd36d40b1e81604df5a9889c596d6431bc3e5fd4e998188ced888e3ec33829656caf4a047fad1875ee95d09b195d8f26c88ac1ce91edad12594f58a4fccbd447f75b60becd9cae487c0907b2eab3b8a45ae21f9230f95ad3f7b4418969eb8c63062cd1ea8d90d51814de91abb516e01adee780a4b8edb2f0ef02388385ce281cec1cbc7b2e882ab3830634d3d639ece9741aee9c291a6a63565e3c6ae3ac816268c065d913ad14e3d8108673743b29239fcb3101ced61b498e9016b8567d23c2f23373be218be4534eea9bae8722d353c805ca9c01e2a3f590fecec279aa568af6df50d7a1a2bdc6d1727a069daad564cca7e079261654ffa69f6ce3a8b3b62a8203b69242e9c459e4a91a33406d74a536796025a8b0e17874284b286a081bc45300904cebcb88cf649d3ade69ab0d698591d1eb8d40cc883a58799735dd439bb72723703042a2070041374a1e18f89b1f820bcf2c88e23c4a759b43b1710770788c1031c1a760a00ed0a2906b3a43bbc13488be48861a2696536634bdd1064e63be1dfcaab8b2e3067133290370483b2dbe878315c159a31946bf113e286ab8e3dfad0cabb417a74a927de7f35ed800a02c3c62c59954a799b2aa38cfa71a507e4520354a012755733db4b27c8603313ddd8fba6554dc85a420a614929ebc22b505ea6dee20bf769116e007dd9967716b39d24c63352ca0cc37011f24117b7b4a0b4892b4b26896e5db3b101574e26c6f53d2e3962a1dddd0cd22055287cc9152dff745928bdfe6f27ff46e7066c87a44cecdd4426b9af3e2b4c5559393ee2d133d9d053458bfc8003fb5580379a0264a052d5d598d4faff648581a0f9046737fd944155e13ea7b8366449b6d8bdc64d8f2523fe088c67d68e5022de83d329704a2a3215283f0f3fb2016691d62dfbe75bf703cee3c12aa6891c1b4378e67c987da3d6f331bb45779e5b062cccda0d87f348aec14c6e1ed0ebdc86ef4dfc4136f4a510d1259d3cab8132f9cb6343c6da184150b425424f7a5167ffe7733d7b0b20acd01abe22393f12bd1be76aeb9d31b3817d6a6820df44476f2e144e4c6738f4a5114958889fa6f895857864fdcfe17cfd0fc10b062c442a15c1b46319e1f5a3393cd6790e8db84d71acdb845b7d71a9110b8610cbfb357019875a18c66b8f5e3bb11da43915368555021be2266d4d836fab4c1941994e592fea433c800f7a9b9fbfe12ba6a6068e1c79f6ef18c8a6379468b23d0caba6abc1a59dd02037401a93ef23e597eb40a8b57358eddc7d6a56206f2737c44e25d83fa44b825ab13d86fc90be874985ce6a9b33570f3b853093a07c7b23a6c3539461d7ab35d1eec3c2916d4e2cdea457d278fe90cdd2d1aa06182e87cb918835427e131437e554c25be8a7e94ecc6fa6bd40d592ef9ae03a743160a70c941172b518549a9c60f37bab480bef8a0cf8d96c80cbc250444e68cff81d9daaca8c20b3b5f2b61866c4ee8260f477d821ca3474d825e25750fcc30ddd4cb826211f38eb42d979f563ac1b3927b4015f5e4f0392538cde0c230dd31d4517e0c587d7089b1de11d12986101f40d4285c8402e284a871236b82a1b1075d2b4e27817502b953cab7132f7457c5195ac60d0027c4c294f4ee4ea93a831882a311e8ba535b19a13454450daa574c5cc4b955a99bad2e3ae7d18dbd4d0ccd30e2e39edce49da0c4a7cbf51d02e115311290e3573c36b3e1bffec621a93e15560fccf5d94e0b192d78f3cbb44aa276cce19aeda1846e96969d65c3968480511272a8a27180af7a61170d6357f3a70880e0518012b26704ae485e49436724369f1127d84b6143da0116f61cf919895109c5d90e9a08afcc5654b3a35d8e40c818c6a293980927fcba55b598dfd734a85fe41244dcfd3510c3c563f64a5316ec0ea75ee593595da94c0e6434efe7d6b4f76b8970a764dc0cdd95a8595a8e8da0ecf9721121d12c542f091b63d21ca35bb168ee309c0f5a508712a98724bfe5e2bbef2d04e3a0c35913087cbd50e8de391b50398367e1e5c390e7947e43883a078727a601a6c23e5107ab054e3c380ace4e04a2503abd5079bc154dc3301926976283ad2ddcb9bec07749b3ca95594dfadbbca97c0cd24ece9cd8753da9e1832a5164c0a24c8c952afd2c9cc8948c38b812443aff69f73a775dd24b9b1c694fa05ea94c4c4e148288eb9e711739a07a56b148ef964f652dae14ab84f93e79d1e7a872dec572ecfd441c7e918951e56cdbb2063675c9188d4bb5b8bd8d2caa171ee2874f03e8ce52d5de766a23184c6dbe819562e7487580671b4b23ccb463905da0741bdaeeb4d91864110aeebbad694691084c3baae6a2dabeea2a6d6295576290f4241505755bd29d36018865555d79b221d866150d575ad29a7611804755dd5da651a06415857557d5ba64110865555afb76512846150d5eb7a5b24611804f5baae3715bbd5bb1cd2d29e5bec6d5dda52cd468cedeab5b5ac79541b19359beec92cc8904da47ae46859a3a952296ab49a3b74ca5c31e55c397a872cb2478dd43db26859a257ed346bb6ca1daa695d5aa335b43f0b6769d6cc7ad968585874de7e2715eb3a5d76415bceaf94a3b3e674c1c2942ebb7829c432d8e91d9a4d6b762884789884948b6326763ffe2c426279cd29b088e0ad84abf5f0591c0244641d3beb9c73dab498c640a342b695f1abc54c5d012fa5dbc82b7f0fe2d1b8167eeb94269cdf1bf505ffa6b31684986dbf70c180d9b5794e58c9a0ee5b9216c03d83a1bdcfc0084284c9ab9257650c10bc91a1c19e4ec84e6df0f1c49caf6783a95302e479a9eb164fec015cb4a3faea4a79c1e259e783f45db0cd00b6d09d7f53ab0c2800ed3fb4f407176e383ed43a95ab60e796153650987c7383e044dd4c22b77703e7806621c2c476118b1ad7da558a57f86b1400ca0d70b27cffc152c31a9a63b23f84985b9c3dd82d0f3cf9d30e80c757db6a13bf43821150e31fa3844273b430246d99ea030535856330c1dec534ab54488e86e2abf7bd5f7458d937317dd735326fbf789bf74f865ea9d507a97ea66454bfa16376c31938f72288d58a3d3944f93c45488af9a970a5c9387c97a05cd9232b4691ec3650f2e01662b6073499c0bb5d9f8512e238e840e2b8c60f2dd5e81f73d7290d88a88b9360966a29ddad29f6bb6cd602b46f65b871c97ceb76917ca3fd23a747200c4a923088946ee3734a0acfe1806b13a15dfb8268097065109903fa7cab49865df264d07015bd6ea0c70f8fe233ccca27ebdccfff3fc527708e237f65c1464ad5097ade5f2f553b419f393688361c5634197410ff210bed7394f354afcca855cd554e9dfeb8cc53d8297a2c9ac4c6fbd94e11eb9fb7a3f3090640629725cb4296f6860e95224fcc27a0dfe0f65e89bc760b8caad18e9dde3e9e97cafa5a907d68b1eed5d91e6083601af98f3dae118c77e405b4cb32359d18a70de0f1b8b2e66e20e4f6dfb7fdcf7b10f60bbe249722978db85e382d0f7abc0f67aa9658be2a2286ff3b35e00ec4d7dc58f244ac96b6626a1d34b66beeb9bece1773dc490a1023d0572709f314a3d0027b8001a02f00448088eba0ddc68bc3af77c0ca99294cf1d1fa534c7790b6151d274c51232ae746500e59b2abd8105c8c35223551bcfd7c5d5e5d73e5d79ac31f0646128e79236008b69870cb9ef6a8a39181c54e5999647e7a267103cc911a65278ef31dc7b3f75975c4c55075ee8dff655cb8d8541e8e42c494d2b6b3c1dab5fd778b88ef6ce79771ef4207114ce877a0b32cf13f24deae0db0c1a938b47a37ddd0bad110804846da4a9544f3404c20e2f4a44117151d13415640377de91a150aa3df2b680e709bf874c51448b31a04318a50a332a4c3a843c02a51cb488af4ae46a093a3567aa4cf284102968e2af5f413271d7789028954aaa144a8740f6a15f491ab2e29967634c982894db71a258997065dc1405d35ac59c6f4cd740c5625eb31aa94e942d2204f4d6b9eaae9ce650daaa46cadb54da3a7b8c10ffad6a8c2699138708deb532b727a2974b053ba4e52a7f3a2d60153efba133c0d291ee425af099aa7cba20756d5eb1ed9d3a7ee01247c4d573e3da40f24edeb8ef8e92af583277f0dfaa75904103e0ad8ce12a8931a086011ec4a05b55e0661d3c1ae0ba16628217452d8b516ea673184530d1b2b87faa187c004b1dd15511324112c35b14551d49b2a42228bbd7551af30824f199b92463d6b231cc4b12775d4bc3c82a38fcd1648cd5b21f165d59e5e5690849b67db457c8400bb10ad6ceff27a85b7e085141c004d6ead4ce443167a3f7ce8d09b54d74f8a91f83e1f31af4bde7957e5b249fe87afd72ad34d4865b105b32ca3d7a19cc0d3b729b648727b3e92fd6d1886fb9ef5ffa7ea8cfa032333b314662e737d12c8a634889508beafd618f04155b2eba69cb23a6742f0a808a007b6e5c120808f3ca8ab212eb04460405827696caaa6c296bd20c5ecb3ff8329ab66e01b5a9ee122f7fa032054306e75fdf7464c3901c9cac292c1735a07586e43145b46b168deb8b808a3c7341a18338d0cb0a9148fc71759b7cb177c7e536963788f40c652d9f3ae14a7f5f3b5b83b80072ea513007ab089cb6f8eb4928fe11d972235fa3369e2b433d9710fb4240d9b381ac30126195624d60d6b81bae98ee5ba749b06ebd2410e72f4222fc6622ca2f6622e9ebb4f153221ac0df55a05a25b6cdc8ea6dce20aabf6dc5110d15f4f3943d7d1634c2043e3d9bc88a2cac1d2e3e182e01a9645094a413527982ad8afe9567fe9a907b93fa235c057c245fd0fbc810d321e99f23f7dfcc77ffc61ca6efc54a89897f89e632a2be9dc1425fa33d45ab7a623112ad64fa3d2c203e9752da4468f0d5291c2dca2f093006140fe50825653366bede4ac98e19dfdfa52b6771a19f9b21aab92be0e7a0b453b650998bc0c21e02a33ead0dc67c4e8a3281106b338fed95919e6e6036bdde8b99b0fc3ddc8c05ff969bce10138fe3afaef3242e40bffcc742fd4e2b0739997fe0e517dbf5be341feedfd0538f0d8ecbc8be2023f6aa2fa6811f6b7d3bb2e8e30fe97bd57be12c27efbc5f95517025c8ef3c903efebcf9fe4f1eddeec65803fcfcab63c9364a15f3b0d6260d0579c113c217b5fb3a10e276503cb1a961733cb23fccf480ac10137e2803775ecbd7ce3b798a4cebc853d4d09929d6da34883676abcdd7cf1b165834d8c2bdaa19d6873a68555dbf60acd748c82803b3a789222406b3128abfc1d68b10d1cf6c93a3c336366c16ebec02f2bf404f798994e7e171c3fe78659162ce43bd7991be797bdcca4fc8b616733e2710e32feb4631577f39cda6e053477370da64bab9c39d21d0f38c50335818dbb7a42935acf871cf0591cb4fb5ac5ac334957fc7a1cb222c068b8679e3be2c21e6df16f9ec6d7aa13ea98f1d63ca79efb3944976ebfb6d7ee297a4b6df2f671844d5ae7b5b79ac1be297f92a151e3ebd7b1793e1ac34cfcd15f0d59f541a11badc0c03046fa86b8fb693ff3b86d677055f8485ae7646f69f03a0d9cf74dc0410ceb601777ac96c9db25c29e350b94e28514d6dbee14b9a26b27dad8c6adee8cc6e1d5ef502635da1d0c7ae35a9b83cb474db2e09ad6026c81b191b1ae0f99ad4fe3bf32d8bf379cdd166fa769209bdd372cc96ceaa85fd4b66eb2f7463fe91b0239e53c3069a4957375028b32d16b74e935427347e5860dd6faaffc5ba791f622368c6eb8f0c4f8724a3e307ea4c7fe99e31a7ffbae9846d1399709e18341b64d7b00b7c85eed63ede5adcfe9676a5c687d64ac3c28937c6360fb152d892d3f8b59f7e86b80f098165b0c18ac07eb1612b23388db00031ca61660fa1dc418f74fc990ad7454ec72fc43efc9be028ded78d2b6dc780f750d51f6979b7280a72cdfb31eece4da8a4c46ce2397d6c58626dd6da8bb3b78deb2365d1c2b6fed4a0f217955a37bcd9cf68b8282baf9dac1321031d94f0f153be701dd562d669c1d6d7667e9c961ee88bc3d4db4b27bf6b79fe6cce9c85fb7c69b838f0c37214df326a3b97c3b9806f89fe063e26c46748e69b3e9e84c3381f6db7c02536dcfe9652def966cb2bf8030b458e0e99ccf16c603069c94b1971d077dc6fd62802dab155f99a31dfdc64f97bcd9d9668058da86f69af5b4397e8f65dfd9de6f56b59c4fdbdf95ffaa66cc7aafd3df85b7912ff7f47ed86f5f4ffad06b8ecd64dde2c406ae7536c1d6781b0ed2900b9fc4813a37f27d6bb2f02b3fe39585dc464b74f2580b6cdf4dc75878b2603a9d3ebc768e7a7c13e6e4d8387cb501f334a171dbe19961d78b9145ce54c7b44784d8d36495a1e85501c3a8d5c175eded5be63fd42867b1990cacc1cfd87a47076ac37fce598259f67b6bbef32fe20521fe3c6f998179072f7ccb154d35e23162ebda788ddbb73d61a1b68f084c5a10ba37b27ae5c97266e752ae533f98744de4c5b6c999b567fe68e56c77ff29104f8a0307cbdc8a73047d882d27fc0001fb4e773462821136c71aaef395878ddb9ff9bb35a2957d5f3b62edadae70c9a664d51161588bc74f1892055dfbd61599323b5b465ab83b54c7b9b141d6eb6b30afb1ef75d4e397e9335e369a2e95162ef604b04e6548578f593f387086c06c34c1b96eb02589cb38c9b5fcb9ef410fdb344c7e1df69a35fad3f4ea5eaf6a32c0ad7782167decf81b4fc212a399c5e8ad68951d9b8e7a4c67f5d8b16b6cb50eb2e96e4fc7bb2073eda5a7dd0703ba80971abfa2bfb961728dcfb8964757861986fbc046f52497c4d593c6aeb7d1800eac87c8cc82ebbe3c52bfdd87638e1e9eb333abf50eb0a58e33034386071b8781e6dc569814bb6ead1eeccb846df0d4f5b7e8c2310374a9c13ff66468ce6b527444b2fead30c0ecb3c15a19128e8b0607969edaa38fc7ccc79ea0b5d1bad3b43360abc9cd744f3f1387b9b9bdcd4fbcc1b3bd5b35a1491cae594b5d2bcdd85f16fed7ea08b88edf94ad9bfba4790c915964d3a6bbb3693576844c091fd0d9f5b05c64038d3daf680a8634fe14cd1e0f5fb788a36e65c073dbf6719d7c6999cdb67e9dcd6fa42b3ec0b40fcc2c7bc6dcf635aaadba78f156f78cc3a0d634afdee75bc233df395fa02e845e9a6e25e747f4bc5fef728f6589c7ae84f6e0d53d5e3b37199f33dad32a3b6a1cec1d9c68b7b5a339fd08de6710073db1f6e6b0326cedb0fb15da867bdef5022ebdd58c5873eb5d6992f9defffeb19459dfc9361f4bc735feed1855ec3663ac7393ac7c2bf39fc1c6d3f14356cb3d66ead8dd9041133a3352eb0ce25ca366c06fb486336393eb66e9b3345b9795f18209bb3ed486e5996d5bb3d1d51737a8efadd277b85a5ed39cc15836039be658767deb9140db7678fa3496ef99963d99937b598d05ad353bd3b5fc73cdb2da9c6b0adbaa7635c6096cbcceb7f5af763678cb697cf3ddaa2d1d36883cec03b88766da9ead9d56fbc63c56c814cebb4337f6124b6ceed47b074d1be1314e49af609c13bce77d994c33f76db367847da666f428e5ea2472c56910e283ea281ff467dc8e70dad57bb4627ed59a0570dc836a8b690ff0cc674be8f1d71ec38f6fb8a08b00db8f384956e6f8c25f7f10a71ac1fc3a2a3465d9500ecf706b2a3387d9719fd9c7cfcc871199155adcf89d0e6e77db1266e484efb7c26866d4e6739fadf9cbf1ff2aebb3f9c77bc2420e5eccbadc0fe6bc516dc416d3f5703a28faeecf6b5b2ee8dbae8a6d3e013a8859c4bd1916ddc34c2fb8494347bc09eb6d64cdd8aedb6851971a786397ce5157ffd4036ad0cc7a714d8e99665cfe36f0d3acb7b7e6fb7574463395e66a4cbcb770ebd675dc0598e7dbebf81d4db2dad59d8f9e58874fcd1ecdf80704d69d53df60b3d5f6ed30da7a8d5e431b45ddbd67b33e71ce12c184cb8138f79e7794eb3d5856e9b3dfc92866f3e91bfde40d78d837c7da4e180c9dc966564fbcb17918a029cbcf7b98d92abaf940dd8cff27a7257ca1316f93317705eb0db21a129b39d24ef7db92bfeccd5db3512b27cdf691804de233613618d028414398f135cb109c26a39a653ee8bcfabc919d61ab9e5bddcc92de39eeb8700316d39c5958de786615264d5423a0d3ddd6dd379f93a7d3e061c4738f7238d9b7cbc199f257cfe7dd77cdfa8d6bc6cdaded3a78dd626a6e956d271fc8b6e3637aef1eb5cd38d536130707010dda90ad1dd75aae5a5513dd61e2435868572efa67c93a7bdbd7e7da286837cc2906c91cf0149a564f8dea740cdca4afaac5a6896fb4469894c76590711aac9ea83ad5a456d9db3f0466de7bad6aa2b7ae980ca80bc74674ca36cfd60dfbe541be84b1d5c9e91ab0fd36233659fb1bf7264c37fde831e5c277cdd5a375cc46db90c5ee9ced30105677db2bdddc7a4f18fac29b30ced1c8c0c86079ae58844369da19e124e565a08de488763e81718eecaad76bf59bbac538a56418703b6667c72acdcfda12039d71ba0f64d0c1acffe485c531bab61a13a7d96e61b203969f9c49f708c0d62b3cd7ba080c67d230e7c5cdb65fe336ce362c76c340c8be81736829db4737a1e5cbfad5f4290734a7e46f4ae3d64d587fa918ecce7ba0bec17cef2ae6e2b21a27f403750479dcca8f992f77da472e56306e0119d7d6332473a365b613535d8025b32b774d93ad76e4e1de0831654e6b3b119b568cf16bb5e300ffdcaaea6eee9e5b5b3bd31327cedbb61bbd61ec83b62a074e96d95dc711a38f30ae03ecfbfab11a7d3f4616f7d8ada7f9ee45d3eebb8bb9f405ed75fcba66d19019b0f95b6d046fb1ab3779cfedfa364c97d99170e9f18d3b4bbde33f74e85db15f3da61f4aeb4453ffba5bc5bca50e4d6da663299271d6196453da828186b79e5df2946e95f766e8ce7ed33b062d1fef716b30f38d0164e34d5b6a41d6f069fd0679a430505c68c3722c1d145bba6b16c6d473256cecaf8cd1dc5f7618bf702bba93c1e92b3404f58634d95cdef6b27bc47fa8d156e7fe275ac678fa3ce244c65caaffcc4bafd01db5fe113e6f87744d5776f4d6b34187426bde1998b85624185cf676b17a8875d94cf85a2a709981ba7468c5623a27a101ba663d836ce1627a86d3d541276fa3b62ce553619416a2e8cd023969285094b8091938b8e498824372b7621b2628dcf4f5d38466e0c2db2e188d0f292b9d8497ec87f0223768c25389f7f2b7d0462dbcef1d2a5d7f76b44b20595780690c6b7ed8e569c035f90f91f9ed052130dc243c751e4824075ae261847e4fb225a81cfe251abc964cfa028e937f27a948a57928f8b0d44e83ce89f718dd2ca2d307d14011db5ae2f55fb34d439d3aba8b49ec65950f44b5c188cea2d55b7e0622eda77952f5c0ecaa3d382678b5e7c5f81da3280db6ea55c8a0d386be4c0ce3c0b4d87b58fbe8112ebdf80298b379bdc5a50f96806e949aeee6f2602ab1185201f2c6daaf4a45e39b965d3b2e66c6531325a2278edfce9ae4028208c24cc43192a59e010cdc1cb164589dd0ca5d1e8c930def3974f5db1ebb7a1b4ec0b9b60bcf6b35044bc10a6e952cedecaeb2b42c60d023cd3fccae994d58bc3b543650cb7a7ea08182407cc95d905ee744ca6e1f443f81be7f3a1ab4e4483f05acffb99d4917bb8bb86bdaf885305d5514b1466c5739012e9dfd7e055ab0ac82860140f858a905ddd412ee022ac7c2fd75457c0d1dd421ebf396f854dbd45443346ae693fb44c90b75384290ec39a3429b45da031b2541ed3442732f95ad489ae1a39b7f577dc4716313e537c80a03adb1e03fcd255a00125cefa0cbb556175dadb8e98fe5e06253f45d5600dc47f799167f6674a20a13ab48403d1dce353de1400377dec02ecd4a258aee08ac8ed5788f20697170a3fd7e021542315ba3bf93188bd00cb5c2d9d2c5311d7cdabce3040e82177f7169b43c7918d8e24315399edee46235fb23879d673023756e0283b159a5df0c431c2187358e1a4332f9db45a3871b92dd90ed7d9c8447c52ec374ba83582b119dd4c5d59fef99975043985ef352ce07470ce14f532cd0904e7f422db2eb26ff079bb6ed3c95bb82e17f1405c35afdb78c0d315be187187e0524b607e947ee86f91f90f0d194c17276eda06e80cd05a2b71af0d6e2db83c00b445595057a0691c0aa0eeb84234602adeb0339aeba9070140cd6fbdcbef331e8a64350bce6f3e74e3a76071c3ff97f34a9bf89908e1498f4b5bd348f253c3667f02fc98b33976f54f405fc329c54e386c506889f8b64aa0b1116bf78550d087f011c0fb7e64d1cb32cf3bf3d7f91f4374241e9a413b5f003458da79863bafcb8bd20bfbc4f11044eded39ab2b19593ef749afab269963f0aca8c743d3d8c08220b1b7cd0b41e343745629f2f8c93c8d3e634ea62db0e4443d090055842707c470c03c856fe10d531e6ea040b0d9b967434e3146e2517471fb26a50726c56c69fca13b5de5e627d2e2fcd1b55d4e88e1a516813ad093484803852d93fefa6ca997e58ffe461507ea38e1c244dbfd3c95797e6882ac143bf85ccd94192de522ca47ea9473542cca01e59f31dcf11ca7b99910795e05358a10cf421e618e091f05fa73f9df81c900ac9701b239a2da4f126de0bd45e30f2f3e15e832b1874df600dad60cfa784e876eb776c72b469741c69dc9593b37477669df96b2306448698e8cf330945ccea5ab808bf69e0482d96687d9fa08079d5753346b86eb66bae007a18c83d34813cf7c4a650c220ee03f2dc5e305811c1c590c5f0375d5fa61f3be73e2cb5162dc3cbdcac9c49011f085a81e0069f9c4944f778bc42f28b0654a4bbddd7b5b452309475d45f7729450e8544febff5c97c2512c910d5448ba0fb11511a97874461192adc476c47d8c7c37d4384d1304e20bd78a2eb16d31dbc28448e6dfd021f1e64500a56674a1e2430dba93186e8d71932637d6ce4a28590454ace37675bedebb9632c75864fdad6456a25d68dd92ee079cbd196340611518c09b6aa1490881f8a31ed2ce27c1e5506c26c4243203f2e2a30e158ef4472bf124e6daca3f72d8f34110e19f5b4c517e42b8a35620798529509730068e63a81922baf11e00a81a18d696d60360cb52d1f5c672020e38ba6d5a1e9213fb42c45ed3980fefc4dffb56231e7f5626fb808597f7427822988857921b96b7dce6ff193ac2bcdc7fada93c46a4e74c2305f15f39fe2f894a42ee692b91d19ae6f8bfb4ad8b1a900f809cb1c4a68b7cbe1d67a896a21e29e7325a12171620ff14a1b601ec80b5bb514b110b8510926ff86f1994b9d184ea61397a93ede1485f4f21bf35db8e7173b50c5756a1138008d5bf53ad6d7d8fd8034ad60733bbc1dd72d6e5260e9662998e74836059510545b832e90c758382b0918a81f2fe868108618eb5a8268e99b09899195a8999b089b774c886ea5dadda5259ca3146d52e2534bb111f03d71b1dec8d4b42fc53f50f51e376a2f34d2262c5298cb709a85eaf6ae7c61f8d8f41e710667f073c3f15da4f7078a8e8bd5a05698c949e41b8cfd1517f8f260971e1438f11d063e1c017dc27fcb2dc70a93c0dbd465f3875f0125b95e4f2d810b1d4458b1b5d236e71a643e4884c871fc1e667127b9431dadc6511b7e379638794b2d21d0ab26e22f936d30f9593c5bfa6d5fd18ec5de342653d939425d8532815e9a49c8ccfd707c3702d8d1e805184b3f98fb69d36b7a4fac995e066ad43975d7bd86f17c1cbb138bd8a2f9689eb35ffb6c18ab52c29e53dcac38fb1007bb4a97d520332038498c320c42630ecbb65538305891109fa3682fdc7b8f268d731bfeb1df8a050c20862635f06c1ea73e1792fe61541a09aea1957f63402a28dd708b2473011d0fc65b1f36b83c6106bb2fae21160133aa47f2b1f33a7716719d77fc9dcaf8a29f87d16df5913856b949376f8815691767eca3b63d8cd1e517d65cf9898fae284586790ce9580d372f36a02006000b17049b3c03e9459b7e23eb1e9c1e0e8527cb715884ddcfc92697bcdea9053158803429ca92f24c2490a5030ffb5aedc71efc1e35f4bb08fafb53c95035138d98cf3b8aa5d868a7731599f3acb7bda61b070976e918aacf04cbf4ae18c6a295ce1199b69d143fc8a116d3c2638b65956de991cf3e5da693f1597c68426560542c993860b825cb1ac0a52d3132af4f94f599c49901d48ed80123e038f7031fe26ec3ae24299407f6b591c2a0ba90f2e57b1b8ce1c209e067e82aa33a04c2f3b242c663b8794752fde042492d73084ccc2b51a044891eb7405a80ac0841d80ced1c3c0d5fb1b12cc78cefe3a7e52dbeb9044e433221db8f491dc801f04c8fbaa5a567577cb0803f4d43bba8b7b0087312dbd94295e10ee47316a334a640a6b5402074a9f917ed38c93b36113cc1aafea5bc8317d66495102f7d2e55108510a955778d35770ee8abd33601d5376cd2f6a170813fd101028f83ffa66910f5cfe0b499a6ba00cace688aff3dcaa340f629ef3b2d9de6029fa96a3b24e072d000cd52060979cfcae320c3b96a9267f989fe8620e20f1142b201bdb4285f284ac88e1431a81ca4ae0d2e7a79f9f38e9ae239a6780f7d017ba8cd2b1182453a2b969c26e214a6aac2355ce0717a420f8c1f81695a7674741a246ad96aef4ac101d94642c88f2140b613dfd5190ad1e36c9cb7b66004f07c9765388aeb8ef425b7141c8bafa50ffb4131caf9227350fb675607397979148591171ba6b966ce0ff72b113bb4b767226d5fcd6837b5f6e7a00c8540f13feb19cfb65859a4c1ab531459420eadee76a69b0da2f15f462e16a6f2dbdac932ca4b2b090589218b7a23f059e0d2124946a5a88b541933d848f2692ca6c648c2ddc9b429e855ae7f27404c8b9c26b060cc0cd06e18b8443403d3c0d9ab8ce604b79845fda0561083f42aebf1a247269e86e4a21771f984da0fa1b22e817496ee2d45049f4a754e01f15e246cc17fe46c7fcfa60b5981ae04b495cf98e405681ae4b420e450664d9888be289942cd030882d2b203ecbe1b5b279ce0635bb01252fe5d7292483188255b791c4546791367bf48fe2cec05de93cba0224c103094a88531083d3ebb72ef95b4631ee60b5a6bd14fd33eeffdffffbfffffffb7bf670cbe06d353d82c2d65a2100d8884260502b7b4b4b4b44429aa8406ec1ab5d6daffc11ef88f4d884c017b206120b023ef717ccf0cef0cf81e355e54152fcd12ef4a83f715c4bb02e27d2db2469380f76450cae2dd41e199d238bb29e363ac351c291f53df1d023c9324efce21df64c13b23df83c67b5ae33932ce6cd078a5997777f82755bcaba2f795c85a16c7ff6c7196d3e46387bca820bee9f5a65c785b51ac3581fdd217ef8c0ddf3df2a600795dd95acd17efa97186a3c4c79078537ebcae5f15015e57eb3d39f9a041d6563b4979ae849705c20b4be23d05e0831a79797ade242aef2ae98595670b82f7197a59322f6c87d70601af84c3d90d131fa351b2e2d5f1c033a52fab8817e63a9b71f29d1eaf6a8cd7f581332739af182c5e96d29b34010d196fea88b7a588920cef8e06be0985b39a1ede93e2e501e14dc2f2a61c795d169ce5ecf14bac351c413e36c6d9cecd3315f149a7885f02c17bf2e2b929d69af4fcd20daf2a7d5d80bcaa365e975206839cad08ff247c79427893b2bca811bc2df00c47ccc7f8785594bcaf11d676c878a611ac692f3c08486d907196837eac03af4d055e09e9dd30dec7837795801776c1bb52df9720673716f8d8f7a638785be98bd2be498737c5c5dbaac0ab72bd2f105e95d2fb92e15551795d6abc3a077826f0dd79e199ea78b512fe279297b5f3c2d677d8f7d0f1ee34e09b3a78d7e37baef818bda819be098e77a6e63b37de9df099c038cb09fa58eb2c2be37fac586b52f34b32acd95479cf032fcb8c173682b31aa5f716b0a673faa5365e56246f98a1351b18deebc0bbfae07d1df1aa54af0bcb9b42e26d29608de78b570ccfcb13803789871755f34d3e9ce5f07c4c917725c5fb92e44d05f2bab4772701dfd4f3a2d06f32c0bb63c937a9f03ad9de3121bcac306f982cef2a87f775c7ab4ae17579f1a250f82630d632a5ffa9e33d41f19c11af8c06be7be24c868c5d36de875cb349e1bd0dbc3c54dea42c94bcbc2cef8549f1ca40f15df9ee60f14c63dee1f81e195e161a2fac04673b6c3c93d2bb53816f02df15eb7d65795141dff4c4ab4df03f819c31f1f284e01523e5dd117a2604bca790cd74df21e03dd57c901daf06e57f2a795316785d21bc291bde1617af6a8dd74582339a46dea3e1d5e6f8a02a673c42bc6234a5a1176581b765c16b73c5cbc1c3abf1fccf1fef7d7c0f0f6737697cccc9bb43826f9a61ada682f78856e95cf14b257857dafbc2e1dde1e1990ef06a707c900cef4ae985d1f0da60e09588789da0af52085e9939bef3e1530d94f708f0ae9c785f15784f6a3c37c68b8ac0dbe2795117785b19acf118e015e373b619fc9309ce789c78c5dcbc27309eabe25d81795f0e7857e7fb02e4eca6c92badf1eed0f04c7bbcab07bcb00fde1d107c130c6b4dfaf825295e5607de249bf7b4c37333bc37fb4a60bcab2cefab904f351f782f91b39b4c3e76c2ab5df9a05dd359fd921c2f0bf5c2b0bc2cf08551f16a7a7cd095b38ce67d8ede94d2eb8ae14d29f2ba566738657ccc8a3775c8ebda596b32c92f8df1f2acf02639f1a2de7869cc7879cc37498eb31c2c7e295b6b42c22f99af86c90789f1b216f086a9f2b29678611458e341e415f3b396ddf13f5dbc3b609e0992339ea557cc0b67371af818f96a1ef8202f5e9b27be8d02bcacef854df1ae2af0c2be37d5c7db6a7a591478c3a89f4e957cb77aefe27b465893f1bef3e03dd97cd01d6738187cccca9beaf2b6fa78592bbcb031ded5eb8589b02663f49d09efa9cb73eabb93816f3ae1e5c9e04d9a4129cc7b22c073482fcb026f182c6b3738bc12085e9ea137298f578b7d901aefce11cf14e6d58e3ea88297077d93e8785548bc2ed7cb93be4975bc33357ce780b59ad47b5aded7d75cef4ef927437437bca805bc2d9ab526ad5ff2624d07e897f6f8a4b1f020e4bc32517c17e5d549e4994a78b3ed7fd2b5264f7e0987b31c998f1de05579f1ba2e59c301e463628451e145f1f04d7cacd984f05e246b59eb7fd07827e37b54585bf50ff2ae84de571f2f2b8f370c09ef36bd0fd1a7cce87f9078537cbc2da6d7468f57e2e0e599e04d22e13db1f15c01de5396e77a781fe47bd2b326ffd2082fab831776c4ab81ff138117c580b775f36a667c5094331ca08fedb03693c77b386b3c0c78c574b0961de07fc47851aeb7a57a5562de5794772688efc69ce1fc7cec86331941be2b7a5731bcaf35de9de09f9cd63200bc0f116b363bbc37e6dd41e49b2e7851167cd3d19b2adfd6122f2b91374c0c2f4f0a6fd212ef2a88f7e5c88bd2c0db0ac0cbf2e28579604de686efbed7c905ef1800bca80abec9e85585f1ba925ed413df74c9d91f5816fac202795322785d2ebc2926de16035e4d920f82e2d5863e68e7ddb1e2993ef0b20e79c3bc70a6538267327a678af84e042f8b016f181b5e169517c6c6bb67bccff7f2ccbc493f6f6a82b765e55535f1ba26f0aa48785d589cd50cf19e17af0acbeb12e4d5c6ff81e4d580fee791f754f45cfaa270f8a63b3e6543fc8f126735aff7027953476f0b9097757a613a9cedfc4c41bc3b8c7cd3064fa4bca7093e889097258137ccf9f2647993f47835047cd0d0cb1ae38565f2a6c0785b92bcaa9cd7659ee594f14b362f8f086fd20faf6a01ef2b837727886792c0bb82f2bec07807e47b7c784f493cf7e55525f2be2c78f7e87db257d5e4759d6bd906fee78d57fbc0077df1f2f0f026b5f1a6c4785b93bc1a1a1fc4c2bbc2e27d65e0dd29e2992cf0a68ade161f6b4dc2fc9218ef6acc0b9bf26a1bfc4f22af1eef0d97578abd4e7a7895c4ac654cff63c73bc3e53b0dbc2825be8992f744c6735cbc2bf37dfda1a4c459ce101f13c1bbb3e5991c795389bc2ed59a0c06dff9bca94c5ed70aef4c0fdf49e0654df286d9f29e2ef8a0435e950caf8b8d339c1f3e86f46a577c50785683c47b61bca83e5e1a3ade1497b7a5c75a16e67fd85893b1e0bb27af4d22afc4c3cb9ae185bdf1ae8a785f107877a63c931c8bc2ab72c0fb0ac0d9cd043ed62949f2eaecf14c13bc282edfb48017d5c54b43c59baae36d89e0dde1c037adb06643f4de08de1425afcb7bb50efe67016733df77867c3a59f21debac6601efeda134c77b04bea78eb36ccaff1cadcd98f1deccbba3f44d55ce70d6f858166fea8db725e68ca702af18a057bbc00745a1b4be2bc7fb0c60cd490cef98f2f53a415e99a4efaa389359c0775fde930ecfc9f0a2b47c53222feb891716e63d49f11c015e1b4a5ea988331c117cec8ff7d7f7cc719683f331435ed521ef0b0067380df8d81aefea901756c1ab95e083d0784f3b1fd4c73234e77be19b1af3ba54785364bc2d4ace70501f83e14d59f1b660ef6e7c0f0ccbcc5cf15ef66a6b7c900baf13a0770c082fcb8d37ccf6ae247961e3190e171f8b62991a3fdeabe355a5f1ba44b066d3c27b1c5032df9d38be297bb3a0ff415f561a2f4c698dc690f762785928bcb02fded4015e97cd7b1a7a8e87b58c8effe162ad0907bf74e51d03df13888ef7aa22795fdf5a93207ec9cbab1de083d657cbe483c67852c2bb03c43341e04521e0a501e45d59e085816fc6fd8ffae994f41dcf9a8c0fdf896f0a92d7f5c1198e071fcbe1d5e2f8a0195e94015e1a3dde1d4abec984777df8275abccbf43e5c5e141d2fcd1a673c48bc6268943610867c57e0fb92e265ad5ed80f67370bf81808673981fcd204af4ae875edf1ae76785f7a9cd980ef4d72f698cfa09077d57ff2c5cbba7961e6998d1caf34c1cb4ae2854de05581e07db5703607329ea6570c0cefa986e75a38abf1e1bd29de191dbe7bbd2927de56035e994cbedbe25d4df2c2467887c0f7c0e135f19e521ff4c71acf14af98d47bfaf29c964f337cbc977386e3818fedb1a6a3c12fddf1fec4a362effafc939ff7d4f34188bca909bc2e0fde54ea6d51795171bc3468bc28f09baa786fba57a2e4654d796167bca72d9e83e22c07fc18ec6591f1c240f0ae2a785f452f2b002fac885785c6eb0ac1cb23c92ba67b751c79a6155ed6062f6c881725e66d01bd280abe098877047ccfd099cc03be1be25575795d09785988bc6160785525bcae2c5e151fefebe63d4d796e006b3557bc77c68b6af24deacbf2f2c21c79590778c3b0b056b3c17b40bc2c0c5e989797c5c20b3363ad89ce2fd1f0aa30f0bec837c5c8eb02c0ab25f2415c5e55d3fbb2f2a61a795d14bc9a94ffb1e4bdf95e29036faa88b79580676824f09e0dad2e2f6b026f18f445edf1d2c8a1b4f4aac4785d1e50dae2ecddb5e37da2bcabd5fb1a3ac3e9f2b1205e27e13ba63bd301e499b67755c80b9be055f9bcae2f6f6a85b7a5c4abc2e47db1f0ae4c785f5abc3c1fbc49e9d96ee07dd03715c2dbfae1acc9e9974a5072e34dedf1b696d63602efb3c3d9cecc330db19673f4b14cde9d2c9e0993b31ca18f5df2ae3cf0c2a2bc2c0fbc4938afd6fa20225e9596d755c89baae26dbd5e1522ef8b82f745bea78c77e7789f13ded396e7ccb52613f825305e1e15de2426de1d2ccfa4801795c43761e05535f2be7cd6de59d62fec863795c2db22c0ebc4e71dd3f3a686de561d3e1abca80fbe29895765f4ba147957147861deab52e47d5df0b29cbc302eef0e20df943abba9e363179ce5ec7c0c91b31a28de2bc09a8dfa1e08d66e6a78a54cde95232f2c00ef6a022f8c7bb5a20f72f2a2f47869e238ab49e4bd3cde9311cf19bdac235e18ec4d51f0b66e7887e47bf0503ae35525bd2f28af13f355cae49d11e23b4cce70a8f8581367d909ff43f4ca3cf1dd0aaf867ed0cd9bd2c0eb0a5f14182fcd15ef567c8f77f6f82b66adc906bf94c3ab2df24145af4e26cff4c3cb8ae2855de0d548f81f0bbca89b6f3ae3785535bcae38de14d3ebaaf2aa06795faa352728bc63507851347c531c6b3442ef816f2a01af2bf5b21879c35859a3217a8f5ce321e415a3c19ab6ff73c7bb22c10b93e15da17f92c5abea5ed713afa6c1ff24e0ddb1c037856b38ddc77a78554fbc2e0abca70c3e28913735c7db02c11a4decbdf40c47f5312a2f6b5f180eef8e23dfc4c1abcae37dd1bc2847de16cebb238067aa626d0b791f2bafcc06be83e25de1f0beec78551c785f299cd548f15e19efeae87d2de0dd397a26d78ba2c0dba2808fd72693579ae23dfdf09cd0ab92e47d856f0ae86d6d7951e637017286a3c5c7a07865a4f84ecabb3ffc13325e94976f6abd27349e0be35551e07d71af0e22cf34c2da0c19efc9bc282d5e9a285e96005e98126b323edf69f0663cef03e675b27ac7a0ce6a44f0de226b1923ff53c6d90cd0770778530c785d159cd558f2de212f8b036f52cdcb33e54da2e245a9df34c8cb82e285497296f3faa500acd5a4f19e1bef89fb6d7c7938f08a31e15d31f1be2039cb71e4977cde19f5bb30afca00ef0bf5a6fe785dbb3458bc2b04bc30009cd58479ef006b2fedaacbfb52641a199def7ade9d149e498d3725c4db2ae46521f1c224f0ce78f9ee0367333fdf05f2ee407926365ed606de2499b39dd53325f16a867c5096f7c4e5b9f33d5df11c136f0a84b735f4cee87c47c78b6a7a5b25bc3c37bc49629cd9f8f14a31bc2c3bde30e3bb28fc13a1778b781f9a17d57a5ba93725f4b6ca7857e37ba6bcab35de97d2cb2ae38589608de685f7b85785c2ebeae24da96f2b8e302bbc2b2eef0b9117e5c54b53c58b3abf29907745e57da971b6e3c633c5ce6c64de6bc03b63f3dd1b9ed0abb2c0fbf25e14ec6db1d66ab2f7d6573be4837e785398bc2e286f8a86b7858512162fea8e97c68d9705c30b5be355f5f0baf47835107c10192f6a8a972689b31c273eb6f4aacad725c6cbeae30d73c29bd2e36dc5d6d6f53e3abc2b05bcb027efaa8e1726f3fec8f7b8717653c9c7c697e78737698f77a9fc1302bcaa1f5ed7015e1e2b6fd21767384f3e56c3bb62e17d15e0d582f8200cde5517ef8b03efce10cf4481b50c90ff01e35d89f0be947859ad370c0f2febcb0b6bc0bb13e599de38cb513f967436337e87c8ab92e37dfdea14f24ce1bba2e07d11adddf4f04a2238bb49fa5809efca8df7c5f43ec5f76c67377f7ccce78c8683f73af071b2d664875fdaf26a4d1fd4c6bb93c837617076d3c8c704f0b2707861769c399979c5fcf09ebc3cf7c3cb427a618cbc3a753c53ea5d1de0853959e351e41513801725c64b93c5cb7ae30dc3bd2a2d5e5706ce683279cf7c773cf04d52de9500de17126738487c8c00ef0e06bea984d7268f570ac09a4e1fcf44c1194e063e76c7cbc3bd49456f6a01af6b82359a0ddee3e05d1dfe09156b4d52bf54e55d55f2c24c7859402fac8957c5bd2e27decc82f729e26555e00db3be3c1978c58cefaae67df1b0d644865f1a7a593f2f8c88331943be337a53dedb02622d1bc1ffd0719613e69740787512f04c249ccd54f01d1f6734b0f76a78378df709df1312cf79511ae25d49e0856d9f64b2ef2a78b7827f42f3ae94785f127867b2efd038b3e9e29576cdc994770c94570701cf24be1bc33f41e2b591e49584389309e43ba2359cf1633ebc3b10f82601bc3b23f826195aead9cdce2bb1f1a62af0ba047086c3fa580c6b3c6dbc622a78350c7cd0141a84f09e54784e5c93f9f96e837779f82756bc3343df4df2ae78785f7bbc36625e098a97e7ca9b94c65a93167e697d4f453c07c4d94d201fd3e0e5e17993c49795c20b03e36c47f03e3fbcac06bc617078552cbcae02284de06511bdb004bc3a7c3c1300de94096f8b883050d69a84f04b3bbc3b463c53242f0ae79bd017f5c54b63c59a8e0ebf748837c48bdae2a591e27d8fefd161adc922bfa4c5abda5e57136b3643ef85e05549f1ba2279751879a614de9d259e899277c687ef20796708f05d09de5318cf59b176094eaf34c68beae19bfa58db4adee77cb50ffea79167684e780fe8bda97925335e2724bc63c477077da604bca999b705e5d5a6f8a0ee4d25f1b616f06a5f3ea8e7656d2fec8965645edf15f1aa30785d4767374c1f5be12c6704bf149ed5a4ef55f1b2c4bc494d5e14226f6b5f9b3a5ec9e7d5943e288db31c9f8f31604dc7845f02e42ca78e5f4a75486739503e668197b5c50be3c07b2ae3392f5e54ea9bb29ce588f14b346b3a54fc12093e9d4af01d00ce662ef8ae8f97d5e58529f2aeaa785f18f854c3c67b6f9ce56cf031475e5498b7d5f38ec2b2e478c37cafb6c707e9f0a6a0bcad24de151d2fec747663c12bddf19ec6f7b0b0a683e59714513a7a535ebcad485e4d021f84c4594e097e495cd368fe278f3785e56dcdf1aa6a5e170fef4ef94c6ebc1a241fa4c4990df91e25af6af5ba86de191ebe8bc0bb33c93791f05ec9f708f26a23f820335ed504de57d0ab9a795d3abc2a3cded7cc9a0d10ef91e0d542f0410538c339e263479ce15cf2b13c5e1604de303bbc63f13de0bb63bc8f082f4f0f6f921b6737887cac83571be3835478b5087c1001ce7018f03135ce6e7a5e098fb3dd5131f1ba24f06e2e6a00dfe4c5bbd3c43365e0ccc68d57e2795508785f4ede951aefab0467332e7cd7c8aba51f84f3a28cbea901efea82f705c48b52e19bc478771af9a69f3735c4db32e45591f1bac4e854606d4685ef14f0aefe78613befa989e7867851a86f1a7a57ae1716c2bb0ac0fb1ae2453df926215e9407de56082f8b8d17c6f46a947c1015af6a7d5d0638cb89e16393bcaa155e571867383a1f7be1078a97358017d6c5d933ec6a8f178673561381f70cf09efe203794ae7c9a01e4bd26ef0ed03361b1e6a486778c94270178557fbcaf26af1310de311cbc2c235ed8eb4d05f1b60ef0ae9a785f1478b789f741ade9a0f04b81bc2ba0f7b584c7c6cbf381574c0a6feacadb02e34579f04d48bc2b1bde171d2fca8a978689b39aa0f7bebca7271fa4888e1e6739477c8c044f5850bac05a8d97f7b8785302785b59ce7034f8980def4e08cf644557e55dc5f1c2f60ce7918fb1f1a68cded61f2f8b8217d6e5cd2e781f235e1e296f52146b4ef61543e5dd2bff648ab31c0b3eb68077878867aac0190ed1c7bebcda0aff9381f737be2786359aa6f7d4339938be137a5542bc2e06bc3cab3709fc3463c97b007867b07c7781351ba1f730395b2dffe48df734f34174bc3b2c3c531cef8e03bec9835713ff6702679bc5fb3c59b391f2de25afaa02efabf3aebc2816bee98c359e335e310078793279c5949f4eafef765ed596d7858017e57dd3146b3a3ffcd207ded3d17343671918ff33c5da0c17ef692fcb861746c73b637e4781d78909ef18f23de57c901eef4a8f1776b38633808f9d675907af41d08b3ae29b2ef0a672ded694331c313e46c5598d13ef91b1a673c12fd5f1aa5eef4b002f4b8a1736c97b1bdff3c2590e15bff42b93c777ebab427a5d0a785596bcaf13ce763678a628ce1603ef639ed520f21e1e6739377c2c0367371cf81809ef8a8cf785c9d98d231f0be1652df28699e15d35e08571f0ee687926069ce130f1b124de5512ef2b02efca8bf7d581331bf13d0cbcab0bbc307213f02e81efb1e34c87e999bebcc878750879a6ef5d85f1be3ef0fec5f798f0aef67dd93013c4bb7ae27d59e07542be63c017e580b7d5e43d59796e8a1206ce9ef29904d668c678af5cb3e179eff5caf4f1dd0f6b3b663c1309d69a68f92520de140b6fcb8997e7e64d12c0ab1ac0ebdae25545f1ba2cf0ee2c7d530d2febc90b0be24549f14d49afb6fd0f045e9ea03789cb7b02e2b9f5452df14d95bc1a08ffc38077a6c97773bc272ccfedf0a63cf0ba06f0ae65574fde979777a5c5fbdac09b8abd2e195e1d053cd3096b273cbed3ce7232f85823efa987e7aabc3a8a3c93092f4f076f92f96a0bf82020d6dee1a403c52f8de05dc9bcaf2baf4cd377659c0d805d8bef11d778ac78c5a8d66478f82e5cb3c179ef01ef82f13e3f6f6a04afeb85b31a0abc37c83b25df638077077ca62fde930fcfd5f0b200f0c28a94b6bc2a48ded7f6da68f1726479558fbc2f0e5e6df53f7dbc2a2baf0b8e77d77f028692166b3a02f8253dd63217fe07e94dedbcad17947e78752ef04c37bca8ec9baebc2b27ef4bcb59ce1abf94f3aa8e785d107879be37c9e8ddd1e299327935ef7f5e2f8ffa26d9f1ce68f9ae92b39c427ea9825713c10795f1cedcf0dd03de9318cf69f1aaa6785d615e99117cf7c559ce7e6c90b52613fc520d2f6b8f378c09afea8ad7858197078337298637d5c2db82e25d95f0beb2785191bcad0b5e9d0e3cd3798673808f91f1ee48f24de4194ed3c704e98878b78ef729cf7216f9250cce6e0cf918072f8a8a97468937e580d785c1bb52e48561f06a491fd4c51acd94f7b67765f4be14f0a634785b3ebc3bdf3379717643c8c77e5e9e0ebc6206f0ae26785f5bde95f24f8478c5bc29ed6da1f0a21ef0b676d69c4079c7a4f0ee94e09b6858db44dec786351df197fc787792be298a52182fca846feae2d5b47c90ea4501f14d8ebcaa9ed7657486e3e463329ce1e0f0312d2f0b016f9816de55d30bb3e1ace687f7b0785112785b4ed6323dfec78b1765f34de6bbeae38535599b81e13b069ce5e8f0b10dbca9d3db4ae175d2f38ea1e04501e09bbaac6586fccf18ef693fe88d4f3353bcb72f8b90378c9477c5c5fbbae45585f0ba9258cbe9f2314c5e55ea75adafb6c307696b32427c77c2333349ef51f0f2fc265df0ea6ce0997a78751cf04c2cbca72a9e53e24d75f1b62c709613fba5f1657df0c29058bbd1e1954270b6737a2630af6a8ff765f3b29ade240bce6ccc7825997727906f52bda902bcad4a5ed6f8c2ae7857e9fb32c05acd93f7baac35b9e2978c78575ade9721af0c20df7139d381c033b5b02673c47729bc2a365e57095e56ce0b43df54cddb2adfa5e09fccbc2b18de571a2fcf25af98125e96d10b53c0271a20de1be17d85cffa7859a917367486d3c3c788949038cb09c0c71af0a6e2785b98bc4fe07bee784f2b3c479ee524fd12f7a6d2b705c77b529fbbf29e34f820052809bd3b333c931e6b37585ea9046faa82b785c3cb03be49486b3c70bc622c78b39ef739e213cd12ef0de0dd8bef29e15d9dde570e6bab80f7b9614da684ef04f09e84788ecbab31f241466b3a35fc12025e12ac9d12f9eee6d50cf041e7d9cd1c1fb3e02c87848f4de00c67928fd5f1a26af8263aded514efeb02efcc96ef32b06683c17b127855595e17216fea87b775c7cbcae28559f2aec1ea64e09976785545af0b9157a604df85f1a260f826365e5413dfb4813715c5db7ac0194e1d1ff3e26561f226edbc291dde56182fea002f8d1fafea83d775c4ab43c733a15ed498b715f4a696785b8d9cd99cf09e06d676c2782611ac35e97ea9875775e575d9f1ae46785f57bc207853afd7f5f3a23ef0b644785724bc2f2cd6749afc121cef4e986f1a5f1510af2b917785e57d11f26aeb07357965c47c87c559cd09ef19b186d3c7c7c27877d6675ae4d56af8108c5823e49599e43b255e5409de96f8b24a78615b9c39b979c56c79517fbc3476bc364cafd4c58b52e3a51163ad0920bf34c5590ed32f912f4b8f374c094a57bcaa11bc2f18de15ec8501ad3519f34b677478bc9a93ff01e4454df04d45efa984e7b8359c143e869ee544e097387877f4f8261c25205e940edfa4c7cb53be494f7489bca7d50705f25e4356f380f7fe78b5323ea83cd309c1337539c3a9f9d894b31cf3631c7867ae7ce77a5581bcaf9dd7c9cf3be6c93ba37d97c6abcac0fb2ae14df5f0b6c878b50bfee7006e64f2a20c7969027935a10f9a39cbe13ee67af789f751bd0be59f20bdaad3ebcae15db5f1be96ded494b7f5c4590e141f637a5376bcad11bc7bfe132e5e0d00ff6380331c057c0c8d57abc00731712673c67757ce76b26712e25dd9f1c2665e1d033c13f7ca8cf92e8b577580f7c55ad3497f2991351917bedb5eed8c0f92727663c5c772d66ae4782f8ef784c37330bcacf5850df2b246786159ace1dcf1b12f5e1518afab032f4b91378c0cefc2f04f8e78531ebcadf50cc7868ffdf0ce1c7d97f4ea4cf24c38bcda201f949edd60e063e2abd25e170e2fcb016f981cd66828786f834fd911ffb3c48b0ac03719f1a22ef826a497d5c20b43e35d09f2c2566738371f6be1d526f04149bc2a155ed717ef05f81e282faa91b765f3cefcf01d25af66c407fdbc3c34bc495dbcac262f2cf59438cb41e4972e78b37d9f2e67376b7c6c82331b00bc073bcbf1e29764de15fabe0239cba4fc8fd19bdade16d1d90d191f5bbd33447c178277e78967dac0bbcaded70d2fab8d17d6b426137ef7c19a4c07dffdbcac08bc61cc7747be678d97c5c30bdbe345b1be69cbbb05789fed5d0df1be1e795510785f1ebc3c406fd2963725c3dbb2e2ddc1e19902797fc0f7bcf1ae28796125bca933ded606dec5e37d5878d7ca3f69e24dd5f0b6b6f8a4c3c42f890003315e94956f9ae36535f286a9e1cc26e8bd30afca8cd795c96ba3815722c0d9cc93effe7877be3c53045e1e17de242bce7290f858093e653bef13c48b82c0db42addd98f04a1c784f633cb7c59b6ae36d79e03ddd7c101e2f8bf5c2b4bc3b767c13cd5a93317e29899705e58589f189267c2f84b3cc84ffe1f2ae1cf0c23a50aa63191a2fef9190c75a13a25f327a75c23c530d2f4bf5c2b29ce128f2b1335e14266f6b7b5558bc2e4a7e69726676359a3ded69e6ec6c465be6949d9d3abd349bd1c89c69356bff0c0dcdcd2972bcab0ede57116b3a507ec900af8acbeb3264ad8920bf44c59b02c0dbb2e1558df0baae38cb39e16314785195bcad0d5e54966f42e44d95e0755179798078930cf0a22af0b62a7877a43c131c6f0a83b7b5c3190e161f7be213cd10ef95f0f26ce01533c26bd3c82ba167a7117e6b3abbb9e3633d6f4a85b785c41a8f1aaf180a5ed6d29b44c18b92f24d66acd578ef09f1b226786144efce0dcf04c819ce051fabf2cea4df457296d3faa59f339948be3be2eca68f8f3d795735bcaf39de5de37dc6b3817e1779572abcaf2fde1313cf817959e50b23e39d59e23ba6d70907ef180d5e2d8d0f9ab22d2eefa9fb8d3ca3e1c07b3cbc2af17551f1b20a79c34c79653ef0dd152faa002f0d17af3680ff91e45571f1ba36f0aee67861dabb23c333e5a174c3598d23eff5f1ee3cf24d1fbc2cd81bc6873305bca9df9609ef0e0ccf64c7bb33fc1325d676e63388e44d35bd2e2b6739357cac92b39c2f1f03c1994d07ef5560ed66ca2b7de04d6df1b628f0de14bd12d38b0ac1dbf25ed40ddf64c7bb0faf3df2b22079c3a46737633e3680331c047cac006b4d6cf8a51f3ecd20e0bd9d7765c80ba3e0b5f1c02b39f1a60a795d37afca8dd7153be3517ac5b8b0962df23f057853376f4bca9accf65d076b4d12f04b57bc6bc13fa9795546bcae079ce558f14bfbae72de97f9a9a692f70c795732bc2f363ed104f21e0c2fcff926cdf1f28c6f52106b362bbc67c9ab02c0ebe2f2aef27861362f8ae96d91f0a2c26fb2e24595f04d5cac3519c12fa5f1a660af2b006f8a7c5b41bc3b63bea985b395e47d845e56951716c7ab9ae37dedab12e17529f16a757c500def6acafb2ae33d691f14c7bb2efc9322de94eb756df0ee9ccf84c89bd9bc0fd28ba2f926a1b5268ffc52172feb821776f4a61e795d19bca8f29b0af0ae027961a957a5c7fbaa596b32f44b5dde1515ef6b92778702dff4ade1c0f0312cef898ae792785324785d30ace9c4f04b07784f323c57be33407cc7819765beb03fdea1f89e10d678a078c5a0ded50bef0b8d5753e2833e38c321e36355bc2c11bc49ab17c581b7f5c159ce1dbfa45acb78f89f21d6648abe2be15541795d603c19c09b32df561a6739361f43c05a132a7ea988b39d9c6732e25595e07dc5f029b3e27f9e785330bcad29d69ab8f14b4daced78f14c2178515cbc3453bc3a629e69fd9429f13f4cacd5b0dedbf26a0cf820a4339937beebe1999947de5bbdac215e18045e4d880fcae0e5a9e14d02e30cc7898f29a1b55ead900fc2f2f2ecf026b1f1a230f0b67cd67690f7a9728673c8c7ca785306785da75795c4eb7abd2cd71ba687f743bee787b52648bf84f4f2f8f026c17176fa7e1be3d5b87c10ebd588f8200dd64e817c37f39eb20f82e36591f0c2b4785391bc2e115e56172f2c694d3bfd0f1e67393d1f63e46c183e83e44dadde960cef89ca7350de1923be1bc19b6abd2e0dce6e04f9d8066f4a92d715f4eefcf04c8d9ce568f14bda8b92f9261edec7f89e14de55eb8509608da700af9809d668b2784f85f784c273e07b14df23c23b83f49d07de9d0e7c53f9ea4ce099a87ccabefc4f12afca8ff795f3ea50f24c5796a119e13d11ce72521f4bc09b92c0ebea40298db31910be1be4dd01dfd3c6d929fc8d8c57c603df59f1f280e01513e52c278e5fda795595bc2f129420f06a05f8a015ce6ec0f858eae591c02b4600cfcc44f29e9317e5c237a9f19e581f648057a3e07f045993b9f21df8ea60f24c59de999defea78513f7c5302de1d249ee902ef0ae97d31f2ee64f24d2f9cd548790f89b5265dfc1201ceb29bf7f1b2b60f789f2befa9830f72e4ece68357aac07b22e2b92eaf6ae775a5ef50341926be83f2de74f04a90bc3b987c930b6faae76de97086937e0c88f7c4c57351bc2ad6ebcaa294c4ab59f141e0ab45f2414e9ce5681f23e46529f1c2207957227861545e56015e5808de151e2face65d19f1be5aaf0e069e298797679257ccf7ae7c785f7e9ce5f4f14b3c6ba741bea37917877f12c58b12e4a5d9e3d51cf04141bca8ef9ba87877bc3c13ec5d0df2c258efa6ffc48bb52671fc921367381df8981eaf4a01ef0b83b39c093eb6c8ab4ac0fbaae04d6d795b79bc3217f82e8957dbc00761f1aa60785d69acd5dcf01e146f4a8eb795c98bc88b6ae19bd2586be2c42f0df14907895f0a81d2f92ec8f7a02fab8e378cb896cdf03f5fce72cef8a59b5735c3eb6ae33dd9f09c0b2f6acb3729f2f224bd624e7847e47b84785723786131bc3c1cbc4942673b1d3cd314ef56f13e152c43f380f7acbcac39de30e0bba3c233adf16ec36793bcfbf13d3dbcab485e58a864c799ce089e0988371580b785be2c2b2f4c8e77cd781fef55e1f0bae8787736f04d29bccbf13d349cddb43e16f4ee48f14c1c589b89df01f3ae0cff248917a5c8db3abd1a131f2480f78c97478337e986351b23de5b529ae165c9f0c2dc58a311e13d10d69ae4f04b5a9691192f9f6c2a782f029f74e0f8a5d8cb03803749857795c80beb59bb115fc912a5a6b5fdf23e326b4d2af8251b5ecd890f12e145217d13045e941f2f4d1d6b5906ef13c5598e003ef680f724c207b53ed170f11e94ce00ef0c98ef3279662693f72a586bf2c12fe9f0f2e4bc4922bc3c25bc4945bc3334dfb5f1a62a795ddf8be2e09b8e58ab99e03d2eef09cc7344af9301bc63467855e7ebf2e39511c1775ebc2c0e5e98112f4ae89bfc508ae4d52af89f415e9b03bc920d6bd923ff73c6ab01f2413ebc17f23d58ce6c7cde9bc09bfaf2b60839cb49e1631578573bef2b7d51317c931bef8d935792e36585f1c2c6bc07f23de6dacd101fd3ce788c5e319fad11ef63739683815f0ae165f5bc30a4b51a33de6be3fd84cff0785521785f2eac9d1af05d9397c5bd3028de95252f2c8577e702df34c2aba9f1412dbc5a161f34be2a0b5e5797f70600af44c7ab02e47de9bc3b7f7c13eacc868d5762bdab0dde9710ef4c97ef2e7979586f52f8a22e795b1ebca930af0be82c6702bfd4c1cba3f32605bdaa3bde97ccab8df03f15586b72fe12d1598e968f8979554f5e9797d78905ef9809ce7216f04b19acf1bc5e3120bcdbc23f21e25599f0bab4785157be298ff7e4fd36829207af8a01ef4b8397a5c20b0b634d0684ef02f0a288f8260a28bdb12643c47728ec02de54166f6b022f4fd19b04c8ab6ac0fbfa795320785d51ded4d2eba2e19dc9f21d06ce76847f32e53d7df11c15ef06f89ef34d35795b2dbc5be59f30f1aec4bc30165e96ce0b53cf6cb678a5d76a32794f9157b5afcb86339c047ccc8c970703af18f05d79795f8dbc3caa37e97b57ebfb1a644d67ca2f0df2a6c0bc2d04bc2811bcad6fada68ef7e478b3dfa7e8d52ef9202dce6ec27c0c5ca389e4bd1b5e194abe6342297d664602eff1bc2810bcadee5591e07d5179554e5e9796b32c7b1f20de1d139ec98cf7057ccf196b3c0e78c578709663815ffa606d8685ef1879531d785d25bcac335e1809ce70bcf8981467351c780f919765f3c27c784fa80ff2634de7855f22e4e5b1e14d0ae3dda5f7d9728673c6c7ae389b617d97c7d94cf8dd21efaa7c5f622815e095897d37c6bb73c533897951ab6fd2f2aaaabcae375e94246f0b83b59aa2f7b4f834c380f754efce14cfd401af87f7a4c47341bc3ca737c9672dfbe035d8de15045e18086ba73abecbd64e877c67b39671e07fe058b391e1bda457e5bd2e285e9e09bc624238c399e3635dacf1d8f18ae951faf2ae68ded70e9f684eef61f0bec5f78c2fca8c97c68b77e6efcc785760bc2f0f9ced7cf04c54acc99cf05d082f0acb3719f29e9a7cd01e6b3a1efc521e2f4ff62661f0b29a786116586b32805f3adf53081f04813755c6dbcac0594e968f7de0d5a2fccf06de93ea830079796cde2410ded424af8b7b514c7c9306d668f6bd9e57e5c8fbdae06555f226ed6bd3c72b7df0b2e078c3746b3520bc87f4a610795da8370be17f7c785356de96162f0a8e97e68cb39aa5f718795593bcaff13d95cf8db06643c62b9d5e14256f4b83f7943e97c35a139c5f9ae12c47898f29bdac1f5e1802de1514ef2b92359929be93e14581f04d4abc9aeb838a38bbc1e2634d5e271bbc633278553aaf0b7d5942bcb0475e1d063c13947767e89916f0ae38f0c2a0bc3a783c13eb5dfdf0be10b02693c4772abc360e78252cef4e1ddf34f3ae32f0c206f09e00f041077857b11756658de6c97b01789d50f08ee17955b1f735c3da8d111f3b9ddd50f1319c17e5c337fd7136b37d57c8a7d305be53bd3b8a7c5306aff6c307a5ded310cf15bd2c9e17b6e5e599e14de2626d678b6702c1bb19df539ec994f15d0eef8e1fdfb4f3aa94785db077f5c6fb6a7a5501785d43bc7bc7fb4879575bded721ef55321cd8c7de58a371f29e066b3538ef65595b2e3a7a3c9393573b3fc8e6ddc9f24c8cbca83c5e1a38de950eef2b8ff714b42c175e581aef0eec9b825e1e0dbc62c87775c5fbaae4ec66918f7d709639799f21ce7260f8d8055e1406dff4e54d9df0b68c7855eaeb0a64cde683f72c709643f3b142ce6cb8f724393bfc6d646d41f827df9a0d12ef31bd1a013e888377c7876732648d2604ef9def6a86f7f5c68bdae19bf6785994bc49bfacd89b0480775580f795c97bda7e0b5f14cf3771795955bcb04ade5db11e79c35c59dba07f22be4e4678c7842fca896fb2e4d554f81f4a5e9e9d37697b5538afcb877791781f9c351a30de5be1e541e14d5262199a9cf79eac6513f81f35d66a74defbe1ec06898fc9bcaa2e5e9725af86f44116ac6dd2fbacef8a012fece7d5bcf82013de5395e75858db2ddec7e7dd61fa261b5ed506af4b8877b3fc9331ce7232f9a5efe5a179933a7817e37b5078598ebc616e389329e43b205ed509af8b8b77d5c2fb3243a9cb9be2795b34bc9908ff63be2928ded6236fea01afebc96b93f44a4dbc2a175ed719afd6fd8febd57cf8209a97e5c40b8be4999934dea3795145dfd4c8bbfaf2be18f0b2c81786c5ab1ae27535e06c0683effc38c391e46374bc4e4e78c790b0b67dbccf0c6f8a87b735c6ab12c0eb42c20d12bc2ced855d39cba9f2314a5e9515af6b9257cbf241a8f794c5734ebcac2f5ed807deccfb1f2c6738217c4c87b59a2def65b1a673f34b6fbcaca11776807785c4fb82bd2c9f1726849204deede19f60f1ae81ef21e4dd199f498c7766e63b36de151bef2bf6da3ce095b29ccdc8f09d236f2a8cb77581b39a22defb62edb4c777a757278f67e239c301c0c766584cce6a2ef05e21efe97cee8635992cdf8db0d684e997d678b5d70719f1b2327993502f4b016f181a5e541b2f4d01d632f47f8a785799bc3017de1925be5b7a776c78a63fce72b07ccc03cbd01cf19e09673714f898f72e0dff6489b3457a1fed6515bdb044de95cdfbeae14dc1f1b6c69ced58f04c4dbc2c2a5e1825af8d14ef06266f0a8db765c99bbae16d75718633f4b1a35745c8fb5aade970f92531ef4eeb9b42784f341f54c7ab59f9108a7835473ec8cb3b23f3dd1a6b4daafc5296331c4a3e66c7a79a2bef45f1cae0f19dfab28e5ed8025e5414dfd481b38b781d0aaccd94df2de06569f1c22e39db413d13015e8d8a0ff25e14202fcd1d6fcac9dbaaf26a441fc4f3ca60e0bb25de8de37d4cf84453be17b47613c4c7b2b39ad67b80bca8325e9a2e5e993dbe1b7a5167bc345fbcd3f13d565ed60b2f4c8d351d1a7ea99077d5781f716dbbbccfbe2a0dbc2f13d6b691f7c1e15d9df0beb8786f3c78250a9cf190e015d3c2bb24dfe3c7a705e47d68785704785fafb59b1f5e49e9dde1f24c0d7877b867dae25d81f0be08f0eee4f04c067879f64dea79b5493e488a77878e6f92595b0bbc4f0fefc9e783167977a078264bde93ce07f1719671f13f517c9a19e43d9d57a68eefce7707926ff25e2d8f0fca614d6686efba77c6e8bb0ebc5a073e888b35adc9ffecf1a204f04d4bbc7bc9fba46739dec75eef26f13e39efe9f44172bc2c3ede3003786752dfdd719603c62fcd9ce55ce09704705673be47c53b23f4dd04ce96f25904de1924be537a59197893b257a79267d2e10c07e963600259cb3cf03f71bca817be698d17a5f4b6c63519f4bbf1cd30781f02bc2908bc2e9ff7e4c67366ac35f1c02f99f1a694785b8c9ce17cf9d810af6a03ef6b00afea8bd7c501250dbc255e9d469e4985339935bedbe15515e0758d795536bc2e39de9d45bee9c919ce0e1fe3a2d43adbd467c0a4d4c6bbeae17df1f1ae86ded7015e1d0d3c130faf4d162f87d08bdac0dbe24089866568f878ef856768b6f704a0c4e55dd1f0bee0787742f04d54d66a6ade1b7a59af378cd0bb50bccf4a6992f744c373529422f0ee5079a63bd67056f858faaa7c785d7cbca8d8db225f95cdebeae195e1e3bb2cef0ed13339e04d9d6f8b8d9745f3c27a78bfe1330c9c39d179c7bc3643c3770d381be2d52cf0414d9ce140e0636d9cd568f11e1aefca83f765c4bbd3c3331572e604f58ed977c9789fa0572bff4703ef585e7badd588f1de1aaf36e683c278578cbc300d5ed51baf6be9ac4688f7ba7851237c93166faa01afeb82b39b443ee6c1279999ef2c389369e4bb20de5519ef0b04af0e079ec95cbb19e1952e39c3c1e4637dd8f19e909ecbf2f208bd496fbc37f23d697c5a31ef83e565a9f1c262efea7b5f519cd990f05e25efea8cf725825727e999d0b31914be5be4ec0b7e4f78770ef04d13bca919ded615cfd0ccf15e0b6737627c4c759633e697bcf73480e7bcf766835782bd2a1d5ed71d2f8f00dea4a1772702dfb4ad6509f81f325ed4eb6dad9ea189e1bdeed542f89f06bc3b0af8269feee84581f9a609fcf1b2e278c378af8a86d7e5c65a93a05fe2e14d11f2ba6aded41a6fab03afca8ef7757a4f157c1002de95cffbfa7256d3f45e236b3465bc17e5d5faf8201e5e9e9a37e9837785c80bb360ed4c9c60f08ea9e0e579e14dbae29559fa8e8c57f3e3837a78554bbcae08bcab3b5e18cd8bfa9b70383b8dbf99f19e5c780e85b31cf2631278531b785de2dac98fef64de9d2ecff4805795c9fb9af2a688785b87bc2823be49927785c1fb3a7a53e1db427acfc0f70c72564382f714f0ca287d27c6bb633ed321af8d21af94c35956fe0f10af9310de311ebca918de56154a689cf140f28a114189e8078b57abe283be339c373eb6c58be2e3a599e35d45795f63bc33eb7792bc2c302fec012f2b7d610638cba1c02f79f0ce9cdf55e0d56e782033d6b23efee78b57d5c2eb32e37522be63bc57dbf241abb32c85ff297a5328bcad23ded5efab86356d8607e1cbab49f1419c520767376f7c8c82f784c10725e06569e04dd2de1dd73789f06a5b7cd008673c45bc6264de9425af6b7c7710f04d14ac9ddec87242f04be0cbbae185d5f19ea23c47c2bbea795f462f8f056f920bef6c7c8f0b2fca866faae3e541bd49dc7b127aceca5a93287e8988f7d4c27327bc29f06d1dbd4ec677cc777673818f85ef03f8ec8e1735c84bc3c7cbdaf2c20ef944f3c37be3cbb3c19b94c39a0c95efb8b505f33e33ef597c4ff8ae32785f482f6aca37a1f1ee30f14c95286df0a21af0b672de1d179e890e8f9037f581d785c2da0d94574a9a11e2dd897d93959787cb9be4c759ce1bbfa4f3aa26785d5bde53019eebe2e511f38a51612d87cbc7c6bca92c6fcb8ef704f441ae57857a5dea7b946511f1c25a4a547cd241fa254c5e96206f98f2550dbdae41de1da4677abdb7be278eb53de47d6a58e3c9e215b37a5733ef4b87b319007cb7c75906fe4f96339b7d8f01efcc10df81e05d85e085c1f0ee08f24dac17a5f34debabb3c8330de0cdbeff193acbf9e0638fbc5a131f14c2198f12af989a77c5bd2f275ed409dff4c5998c23df09b1d6c4915fe262ed068557eac0590e077e697b65f4f80ecbcb937a93ba1725c437c15e1508af8b00673418782f873755e56d65f1a2be7c93eb5da1f0beba7877ac3c531faf2dde1d067c53005e5406df14c4abc13ee808a538de55ea7dadef0c0edf41e04de9bc2d17de1d1d9e6990576701cf84c29916c383e0e55d2cff248c3795f4ba4c78b7e79fdcbcaa0b5e17106fea8bb715e6453df2b674de55e27d9aacf140e015f3c19bc2e16d797186b37ecc68191a34de93f26a407c10005e14d03739f1b23c786104f874b2c077a97735beaf2ade15085ed80b3e5a3e653ffccf11efaa9117b6c1cb03c39b84c5fb01be677d775678a6365eed8f0f127a516ebc3465bc27f23d647847bcaaa5f745c35a8dcc7b589699a1e4bd095e1e2c6f121eefce1cdf743abb79c0c7805e16132fac022f6ae79bb0bca804bcadec2ca7915f7a7226b3c8775ede531acf89718623c4c78858dbb39d099e69894f3a5d7e69cc9a0ef74b7cbc292f6f6b901725c23735e1edf0b2ac78611958cbc6fc8f1cafcc25df49f13af1e155c264adc91abfc4c4dacd0baf2466cd2688f794ded422af8bf55e81efd1e375b2c33b66869705c60b13f32ef49a03d66a7ade2b5a6ba2c52f1df1a6d4785b1c78750c79a6f1dd31c037adde130fcfd1f0ce7cf94ecc9ba2795b2b9ce140f1b1255e56d00b73e2acc690f7ee7879287893a4bca7279e2be255c1f1ba98ce64daf88e873735c8eba25986268af75078f7867f02c59b22c0db5ae465f5f0c2f87857e1fb9ae2dd59c03769f07ec7f7dcf0aabcbcae05bc1a0f1f745abb315f6904efe6f13e53d6b20afccf1a2f8b046f12cfabe2e07511b1766ae4bb9c3775c5db8ac0990deabdd65a36c9ffb4b1a613e59704795763bcaf31ef96ffe4cbcbdae185e921f3c49a9328ef18155e54212fcd1f6b1995fff1f2a622f0ba3858cbacfc4f10af8ae775fdf06a623e088c770a7c4f1e6f8a8ab7e57a792279c56c674e9cbc63562f2bcb0b2be45545e07d89f0a2c86fcae25d2de085f97c3a4de03bd43b17df43be2c0cbc61b8bcaa185ed71aef0e1edf64b3d604865fc2f2a6a4785b1038c389e3635cbc4e04f08ee9e0dd17fe8911af66c707d940b3c56ba3c82bf5f0b268786170bc5a063ec80a252fde25c0fbdcbc373eafe47a53e8db6ae3ac4689f7c478778a9ee99177477ca630de95015e98ea3dadcfe9f0ae7ede1798b39b087c8c3bbb897d0cca2b4382efc0785124785be18bcae19bf0783fe37ba2bca80dbe8988d74900de313e6f6a8ab7d57a53dfdb327a676ebe83634d86fc0e0425315e2d870fdaf3b2d60404bf84c6cbb3be49779ce1a4f1312cce6c68de73c0bb0af3c2c4339a31eff9f06a3fffa3c8194ecec75c589329bf0352da418e1d5ed607dea49c9767cb9bd4c7ebe483774c005e9e01bc4948ac3541e297847879727893d0785741ef8b894e91b31c327ea966cd46cb7b22786f3478a5d7190e093e06c85926fecf0fef7c7ccf0eaf0c07be9be245e1f1d2bcf1a6e8785b2150aae4d510f9202defceeb9b805e56f8c2aa38d3713d930bafce1dcfb43ac3717dcc8d331c1f3e567466a3c62bad5ed4216febd74905ef1827ef0ae77df9f0a632785b3cbc2b22ded7035e96256fd2ccbba3c4336140898c57479267b2e1d508f920f58ce78857ccccd98d161fd3517ae20ca7888f19f19e52782e7c55dfeb8ae245d9f1d2b4f19e36f8a005bc5a181f84c23234a9f77c5e273abc6364f8740ac177139ce518f24b16bc9a181f94c2ab72e27541f23a097ac708e0d5bc7cd0056b3a3dfc1222ef8e08cf74c5194e1b1fd3a29be12ce78f5f72f26ae1ff48e065fdf18649e10c07041ff3e313cd05effdbca810be89899f2bba04bc1a07ffa38057d3c0075df1f28c79c5407935127cd0192f8bcb0b43e455d1bcae1dce70a4f81813efb25075bc346cbc27189e53e1cc068e5772f26a3d7c90cc33323cdf61f06a461f44c1bb93c4334df2a69ebcad1e3e6544ff438077c5789f105ed603de303abc5ad0ff3ce00c67cbc7bc9cd978ef5d40c9e8dda17f22c68bbae2a569e2dda1e29992d6321ffe878817d581b705c27bc2e2b9265e4dfda09c5755c8fbe279d7fc275bbc2b395e58f6aebaf7f5c4ab02f3ba1c797789f7d1393b86e579f226d1b09609f23f617c3a317d47c1bbe83fe9624de7fba53eaed141e39794de95f8bea8785547af6b9157e68fefb69cc910f25d97b32633bf64c2cb83c92b66857731f827a99775c20bf3e25da9de1796b39c223e368277f5811726652dbbe17f8478797ede241dd66c90de2bc1d9cdeb63dbfb1fdf23f4aaca785d98bcaaf0754db1d644fda522cf8957e7906722df9d129ea98c77e78e6faa7979c2bc62b8d78603afb4c45a0d15ef99f1b240f026a5ce7228f898025ed50baf0b8db59cf26391bc4e78de31aa577bc00781795184bc347dbc2bf23d057859585e18216e8ce0dd71e29934f0eeecf04c84bcbbc23ff1f2ee44f24de2bb23e69ba6bca845de96ccebc48377cccfcb92e40df3c3590d13ef8df1a272be297db78cf7e1ce7462cf84b4d6e400bf54c5bb29fc932eef2988e7b6bca82adf24c7bb33816feaded3cf0731f2dab45e49cb9b42c0eb6af2aec8f765c5abc9f14134bc9acfff20f2aa3cf0be567877f8f8a626af8a7c5d56bc2933de96065e56082f6c8957f5e57531f2b272786177bcaa0e5e5711afd6f33f85784cace998bf9480576be3836038db85cf26f0a6b8b755f4aa72785d75bc3ce49b04e6dd69faa61b5e14956f6a632dcb799f2f2feae79b8a7879747893d4786da27837c6bcacee85457146a381f774785317bcad2baf0a7d5d7fbc2c43de302ebc2aa6f755e54c07f64c2fbc3bdb3369f1ae56785f61bca910bcae16ce72daf8a5262f4b8c1786c9bba37a2624decde19f48719693c62fe1bc3650bc1b5d5e9e18de242dd66a8cdedbe25dddbc2fa1f73cbe278737e5c6dbfac07be2e08318f0a6b2b775c27beae23929d69ad0f14b4fac2d24efc3c39b22e16d81596b62c42f05f1ae4af0c26638cb56ef23c45a13347e69895705be2e29ce6c6cde7be47d8eefa9f2f25cf026c1f0ae34f0c24e58fb83df44ded30dcfbdf09e9c788e88339b2f5e295bbb89e195c6bc27f3391b3ed12cf2de0caf2ae87531f1a2a07cd318af8c1ddfa1af4e24cf64e58ce7cb2b265bbb89f24a1e7807e37b06f0ca48f21d12ef3fbc36e65dddf0beea7835373e88cacb8ae185b5f1aa8a785d8fbc2920de961faf93ee1d03f46a28fccf24673601788f02efaacafb8ae35555f0ba8ade91f89d9c978784378988351cf0634267391ff8a5ee4d5de0756daf0cd37705785351de1613efce08cf44c67b7ae1b914dec37c4f1f6f4a84b7a5e5dda9f24c7cbcab28de57989785f3c2ceb39d9a6722e24dedf0b6c4586bb2c22fa5afeae675099ddd9cf1319e571be2837cd664ac7ce7bd3a2078262dafca83d765c4ab33c83379efca82f7d5e50c67fb180f2f6b8a178681f7a1e5f1de2420d66e80582d900f32cf9a64bf44c2ab638167a2e17592c13bc682339e215e31a7f7047ccf189f681c79afcaeb4483770c066f0a03afab5be341c02b6683b526e32ff9f06a517cd0f66a02f81f47d66cc27825ed75f2bd63b65715c4eb6ae4d5ba7cd004af4c06be73e25d85cff6789d9caf1208de99d3776abc1a121fd4c1ab75f14125acd190e03df40cc7e86342bc32747c670625b7aee42aa6e6469a2769aedb90c875517228415515c70f244ff503bd758b6984454522ca22949a5d3ace48452291b8acde8843097e64879e271aa229f8759c3a24725fe8450889cb2ade509a20488a27ea799d788e1ce90d899c16541d42854e1bca9443c1511dbb543cc1133dbd70851aca740dc98e33bfad0bd3f414cd21914bb308a95dc02081214a0108080c916925f4a85c112aad081165c1a29ea92a25ba9a9b187e694aaa1b386ee090c8a9285191d0962d264aaa28510c4451c2f1a84b914743c985e0b8ad26f78de6d8a5a33743a99ee996926ad771ab9786a1178232946b787edb08aedc67aa28ba995e1743b985aba87ae6b89adc06aa9ed9711c95121451d223c3113c3b72fc488e1c612853f23ccd55fd5031fd42d04c8b4678a13c49d22349731c456e04b9f14b8b5c28bfd524bd2e25bb101db9cefc386e6ba13cc50ff440543d37f14cd3ee0cc129e5867e2aaaa21f4a7edbb692e090c8791d0be59686263aa29b0a7a1d98a660c77152ca5505c593f4b66febba71dbc22191034388529aa7098a1d7a9e2249a626971aa565a9aa6af78da9697e1e888aa6372472aa1720b21079113acab2912b9469377aa6e89d1f9a762027a64322b7a50b972c42a96a640e1d8d9c08a5443d7224b933f4c0d154b76e2d42ea3a125428cd755bc32e55c5711bc771352a3acd0d5228c3b31b37b4fb5491e4ce8e0b878e442e44a1fc386e4d3ff34c410f0557721c123995a8680b2a44a47e5185b8141975e13a90ca15a1114e28cd6d55d52ffc4455445195533b14065086aa487a5d878e21e86d691a0e895c97a2a22d421ff77147485eb07c1d47e58a500b2694223aa6e2c67d2848a2de28a24322379a369c2a7c5c48e58a90584299a2deb66e6bb88a9bca9dde39247244ea96944815fa3a5045b36c39025520a11455cf03d7755dcf14edc0d41c1239242f5d88b2803f56a40c1d992c00018560670e1d01011da9273752b922d465843244bb313dc74e3dcd900cbd6f5d8b8490b26ce97244be4096e86672e17aa2e3b7a5e6a79e53c6f20c43f14b430e44c9d323d7cde3c29043d52fde14b1544d6ff3b8aeeb4252243772050222432a5784ceb01457350dc794044fd2ebb8300491b8ac1e5882e2b9a9e7477a1c776ee4c70d89dcd091972e4246456bc8bdf09526788ee329a6a1687ea7e77d432267640ea11b27e49524b971dcf9999b7a865fe86d432297129942424529d7a95f8cd423d548fcb122e527851f283f567eacbcf083aa360461d240e6d095e027821eba912918a6dbf6a943226784742444aa48598c6ea8020404348254ae08f1c0959bf779a1da8123988a1d496e58bc9cea916964547a20952b42346c65da6d62f891a94a8a26679ee090c8ad425d4ca32088bc785cd805959d07a6eab871aa1882dfb7894322a72265310a02e400a8e4b8ae03b70f35bd114d43551c1239a18e4332b91815ad211297d50b45283f8e1b3b3414d30ddd3ef53353a22c4110792922121ab9114228d5500c3755dcc271dcc20e4cbd1f2b527e3acf8b51d10a0484c46505fad93a2f3f5bba70c9f263e5851fa10eecc2258bc7c50b9187c465f54e0194a8baa627b76ddd778626789e432287c46515c24294450b9a85c8ec42aa4645e847e58ad00642d98d2a796e1c48926ab7aa9d382472422287aa5f8482308d4490ca15a1f1032e4545425b522321211b0f8c848c8a8c8c54a1232f4642421574c009156dc152b485020e9e04e0c713da9245cbb641071a78e0e3c1931032e80003107ab60b8490b678315735fdc0820e2a38a280820a00a00535cf2d5b8a8c4098c0548d2a70a26a1142124225c2808753b150c03a423aca22847ac18acbc2854b9107aa2ecbaa1a75d12065018a2352bb74312fd8e102a0a30e91e548c86bd2191de57060d62c46ea17a10d7052f5e848483512328f525415e2b48460d705558f848a90ba803142c59b0ea9a8c84828558dd4d4a84828043b358b1112083642a84317d4ac485e846838f4280b170d66beb0472604bbd3136dec3821218a824012c2429465e8c81cc220bb603921212e42464548265ab4658b298424848ac12a34544b48b22a1793e8a462e28b1e706e1522480a6723f84c049f85e033107c96c967987c36e633319f7de0330f7c96f459073ee3c067977c66c9671bf84c039f65e0b34a3ea3e46c92cf5eaeac109c5b83c4523885d50d4aaf6e08e4d50d60a264f01390ab1bce57375cf0ca06495ed960c82b1bc0786583d12b1baabcb2a1845736f0bcaa8102af6a10a4a6070f5ef560c1ab1e70e65981f0aa0799573c90e0150f1d78c58324af7870bde221014d7256515c78850590575154a8c9e0cba80cba8cca408b0c2b7587cd293b3c90b2039253762c6205d3292b924e59318153562ce09415789cb2628d53563c71ca8a2da7acb0615426329cb242ca292bbe53563c396505eb9415a7535574e0541514f851a826164cc042a550990c312a13a4519960411df1e5d411eba9237048ad36a754a6536a25a7d4d629d591532a21a7543e4ea9769c52d798b14971c0c8290efe38c5411ca7383063141f14a438a0e21407419ce2403dc58195531c4039c5c1778a839f7f07c58525a3b888e495160f4671f11ac50523a3b8386414170618c5c51e3f14a8231a19758401461d71c6a823b818754413a38e301a75440da38e2061d41121fc39165480031432a83eae8cea0386517d9c30aa0f6e541f198ceac3c9a83e7246f13182517c50328a0fd8283e16308a0f3f46f131c7283ed618c5c717a3f8a862141f4c8ce2c388573ccc318a8fa3517c6c19c5070fa3f89061141f5246f131c2283e4418c587cf283e2818c547ce9fd2b16135b9a1d9b1c9690280099cf0ac54a9511b40416de0649406b1511a8860940697bcd2c2334a83088cd2a091511a18324a8339466930c6280daa18a58112a334106294065a4669d0c3280d6418a5810ba334f04669e0334a030a4669a0334a036d47667593d20100ce9526af7208c1ab1c2cf0a356399caf72105fe5b0af7098c02b1c0a7985c319af7078e2150e32bcc221e8150e15fc3e610514f8eccca07c8c78f54239ca2788513e44a37c5419145edb4ed98071caa6895336419cb2d1326a83ef948d0fa76ca69cb229e1940d08a76c263865a383d384677ceff8d77bb7f885f26352453b699996d1e0f8703e1c0dcff02e0378568abc3a4496232f4f8ecc2738a400c30823a8436439caf2c484129ea84364391a813811d421b21419a943141911d5901279298a9212d5a06e394d284845548e5475ad2155b76431a340196184a32c5d4c212ea810e7c1a3cae23f32024ab764f1721e994f908a889e0c219932fca30a553cf0425fa837d050812fb0b34a17407488828a8d8525842c60f81afaf0e23d961a2224de4acd0f31c48349e2cf2a280d1511b068e1a9d8a035fcd9c517b1de0af9387ce0a980c308159e869e57ef10a2e26980e1d5301e089f5f9f3c9535a298e473a8e0a95051a591d7e1863f5dd06323e26f818615a054a9c2c20d346c3c1beb2b1ee070830d3558a942c30c32c4400586175c68610a0b52a2942b4051811cc510fcbc8edb36340259219452f4336810fdcc082110082a905156602184131edfc60c7f92e34f427faa218b16523835e04f769c70f8930d6d44f1272a2717fec4c229ca9fca3fad7052e14f29fc09853f9d702ae14f249cc653f827f04fdfa9fb13f727ad02af51402b24913f34c81641e4b5430a21e4b5413403bc16c81f7deca101cd6b70686ebcd6c61aafa9818619af95f15a015e23e3b5315e13030c2f5eebe2352eb4c8c45f0e4a0ca50aab0b5252d848307823b9f935e41701bf857c06c667697c86c667577c08407c06851c583e6b03898da4e6b321b2203efbb291cc7c66c51b89cc6744ef86156f24a7cfb6309191918df1990f9f9d9fa59fc1f1590f3d59646bc8b17ea6461646f6480018f9009cf13e5bf8641180ec9fc0f1b3f1b7a1c69fc62606265b2c0393fc06071b6828fdb61f38f11f34f11ff14f840f3de880c2004a000a81830d7c5668610a0b52a2942b4051218511c8510cc1cfebb82d084884100400c2071e74d0420b2db030e3f33334d030c30c32c410031518fe272eb880c23fff0e252c8037f019018887b79ddf747e6bf25bce86f3dbcd6fa9df50bff1fcc69a08a17fb2fa27db04bf392161cb7edb3f8446a4fc24849ffce427a027856421e8674a0f3a58a103155668214a0a21a850b6d0420b535a9832654a39254a59b610c20a2ba890420be594164411c429a508650825500825082efc4c07a2148ee3b8effbbe30044120a01042104184eff3bc11462049519c4161068519147e464ab9c2f7799e00cad28a942e5280429022454a092442082d4c8912254a942851ca29e50a53a2941d17a5ecb82865c745e9b8196488810a0c33fccc0b335dbacc74f999ed675a6801a88516a4489122254a942851a24499d2c294124884105a9852aef03350a04081024505155450e1675a985202a590c2082390e428863f53823f5302793f538a1042598ad0c294520020fc4c0b25d0cf78e081073fd3c2945284101a99e28045125184055707531ca0800430b248228ad0f100425c0608040f3dec80230b30a0b0828a2a9e70620926da58030c018640e20822668078190a0840032f5301482020738197e172c4cb685165bebc0c1252962caf72d02087f86e74f05be59783286e14fde9853fb5808523473104c9510cc1cf5329214731043faf9bd380e7fcb2910c5d6428ca720a8165cb16241888c0a4455834e0495115b89870fc5a60d032174d10a22e30d860830d361c698274465260d0d269c0e3791e97aee382ce85cb962d9a2056a4c0a085052d588e12658b14185cd032174d10a22e3154f0423b5b0cf0ab4e0e1dac22f470f4d7e07536189da7600e7d1e398a21f8795d00e902481740ba00d205902e807401a40b205d00f9374cc2c42eb9248c4b2491c4124c22c900269734c0026b74c002314c20592416014422c0c82011b063036e1ca246181bd0639134381006220d706401978401c8227ec4f8b0830f3d2e01048f981d6b5ce2814b24a163031d885d420725bfd1c1081d7260128625721c22c71a61e2c024160bc38e37e040c01a98ac114b03930d8ddf5c69c4b60658a1e2a960408c077eebc06f1cf8ed92df2cf96d03bf69e0b74a7ec3c06f17f84d92dfc2fc16c96f16f80d92df22f09bab01bf39f21b037e6be437467e5bc06f8b207280df0601840f3c7eb3e3373ae2f8ed8ddfdaf86d8ddfd2f857b04165014b0d961a2c35586ab0d460a9c15283a5064bcd468e62087edeb7917b5e0e1750a1c8974974f8e2ab90f05530791e2ad182fe0c618021f35b34f055bafc0c203c1659bc880f26024fc31e54a061a509161b95922c1ed9f261a9a1c203158d0a174438a92c293f6c6432f0f33ccfebbaaeebbaae23e28a1e8af81a62c024094b624881e152c50a158ac4b05f26d161b55204954968d8030c0f54a06104c64a133003c0623304824ac90d1964f108160fb67c5db460a9a99202151e66d042459b810b152e6c80c4860a2aabe4c8c1c951a5503b3a3938363433325a764a954aa550a99d944e2a278593b249d1a46652327b70c3f6397cf054f6a950e0a57b2a384f4584a772f33640f2546c9e0a1529343e55c6a7b8f894169fcae253507caa894f31f1a91c3e85c3a76ef8d4984f89f9d40b9f92f229143e157eaafb54d0a7803e25c2a742f894003ef5c1a73af8d4069fd2e0534f3e85c1a77a3e15c9a72af0a90b3e25814fc13e05814f39f2a9463eb5c8a714f914cfa7589ff2e1535e3e85f4a9a34f197d0a884f75f954d1a7883ec5e5535b3ea5e5533f7c2acba7863e85e553eba7d44fa59f423f757ecafc94ea53a94fa13eb5f3299d4f35f954cea7703e75f3299b4ff97caae653349f9af954059fe2e3537b7c4a8f4fe5f1293c3e75c7a7643e75fa94f6293a3e35c7a7b24fc5f1a97d5b448adfa0f84d8c2d8c2ae5575961539971c69b41861934bc192d3c7933566f86ce97b154c6235f0603be8c432e838d32bc3c1994fc18577e8c127e8ceec708fa3102f06244f26244e0c578e3c398e3c308e3c3f0e2c188e4c180e4c178e3c1d8ff0293ffa203ff0523ff4596ffe2fc2f7af82f66782f34f05e00f25ef4f15e5cf05d20f15d04f1593c9245229fc594c7228bbf828f2bc6b0428eaf428fafa2cb57913e1505782a84a022ca5321be147f140f88e28887028502857742cb3b01c32b21c42b61f44a10bd1219bc1216bc12399f44ec93c0e493b0e49398e49188e29120c023e13d12aa4722e791580248f10498e109107e1123f8228e7e0812fc108cfc10480f869007f3c78319e183c0e483b8c00701c97f49fa2f07f82f75fc1723fecb97f7a2c57b99e2bd6cefc583f792c17b01c02355f24814f8a328fe8889379ae38d7478a31cde48ca1b9df04624bc51f846207c172dbfe5fb2dacdfb2f35a20792dadd7c2c86bd143cbf95a9e3c160f1e4bcdaf1f78959257c3bc0ac8ab6bbcfac5ab4fbcfabd1af4a70b7f867fd6bcd0212f74f442e60bc9f042ab17c2b9874cbe07027c0f437c0f46cf03d3f320e679a0e47980e47700e27720fa1dc61d765e8711bc0e48bc0e32e4d0c1e7c0f339dc7c0ed9e370c9df90c1db00fb2a937c9545be4a1f5f4589af92e5abecf05552f82ae357a9e0abf03c0d2400c1cf10e66790e367f0e26750e267d0f233a83fc3094f2590a772c653a1e2a934f15472782a273c95f0a978608619af85012f82f9c308432cc45f8e80b8682cabe7151c6ca8b2420a43801122882f5e908e8c80e85244c4658b961fb20c6159d5143d3d204731043faf030202020202020202ea388ee3b88e1cc510fc3ef080dbc8510cc1396e0e4803016ce9162184f892e54b96155e9882c39429367022387221f7795cf764058e143548bd76c66b5b1c4506028f5c144555e18a952a34146d99b21dfc432c60b9c34b117ac7d12652172e5ab21c8acc3922135e03d5f88bdf93137c3499ffc93ee093ee7fe23d41b16a582aec2aacfe271921bf99699161f10fd173c1ca21441733bea789cfb87c5680ef21e2b31fb2a1cfd4cfccd7cf76f83ff0590d190d7f3201017b51648fd7f4e809c0ffa5a767a49e09bec7c96756d685b5fc587238edc93e9be133197eb38669d0808a0b2cac15bfc289a0280004ceeba011c6ebc8f13a79bc0e035ea77b1d1814f919242419255e467d4d0d305e33e43504bc16c86b85bc96c86b356cfcf15a1eafad56665d7c94a1afe1684f9e80c99200f30f432b1051a1b2e58517d25798e83341b6fcc2599ef2e4c90a456b1d6882fca47bf2e4559df1ba40f0a2b86f8ae24de5f0b6be78b5463ee8e8bd11c02b59e04c868befac9ce16ce06378bc2a99d775e5acc601eff9f1ae74de177a5633f4de156f8a8db795f4da5ce095c09ccd68f01d20af4c07bea362cd2687f7c49cddb4f13100bc3a1f7826f5558d795f52deedf81e1bce70663ec6c26b53c92b19f11ec9f7f0f1aec4785f625e8de983d838c3b1e2634ebca7259e13e25d61e08591f02e1aef03bea8d337edf09eb478ee8957c3e283c4b39c397e097556b3c87b7a9cd5acef59f1490692ef8c585b04a912ded610af0e24cf14c3bb8ac00b0b7a7792f7f161cd090def9828ef6ae985597953446f6b8f57c5c8fb7a729641f99f2eef4e0dcfe4074d1e6b35e17b605e96f8c2ac78652af9ae895715e5758df1b212f086a1b2d6048c5f42e25d27ff447bb514fe0703cfd408f29e1def0cce7771bc33477c478297c5c50bebc0ebc47bc704bd3c94bc62c237a5c5db82e45d71f0be88586bc2c82f6df1f2b4f02655f1ea50e0996478518cbcad9ab59939deb3795561de17f8aa88785d0e58e3e1e215c3fa9439f13f4dbc2728cf8def547c0ff7ea08f24cdd9b92795b2a9cd598f05e11af4a90f7957a79ba37a9cbabfac0fb2ad76ab8782f8d7775c90b53e16589f0c2987835d6fff8f1ae42785f499c1dc2af22ef4ed03365f1f2686f5206af6a8bd7a581b31c427e8982339c0a7c4c8e7795c2fbf2622d73e37fb47859217893542f2a8d97268cb31a38de83e3dd39e199d0787744f04d31bca90fde1696373580b755c47bbaf25c0cefce03be0984776700cf74c6cb53c92b465c73f2c23b66855735e57595f1f2886fd2975795c1eb427a67d0ef2cf0a2a27c5319af76c2ff5ce0e5c1e14d1ae34d51e07581f0eeecf14d3967361bbc07c98b1afaa643d67446f8a53fce6a48788f88b31b4b3e36c2eb44e8551af33a79f28eb9e05d75e085adf0ee0cf24d3c2f4be885f5f1aa34785d41bc2b17ded719af06e6839ebc29d6db9ae13db5f15c19ef3af13ea9b31b283e66f36640ff73be289f6f02f3ee08f14c1378513ddfe4e50c07848f5d39bb51e263333469bc9a201f842a71f1aa6e785d73bc2b2bde17256732587c47c3eb848757e9034a55acd92cf15ed3cbd2e185e57176e3c4c76a5e193fbed3b296adf13f59bc1af73fad359c2a1fcbb2c6c3c72b268357d3e183322535de999cefe478353e3e68873785c7db2ac1ab29e083babcaa3ede17ce1acf1faf9827ef4d7e405e56102fcc016fea85b725c5cbe3e44d225f1505af8be82c67f5b144d67450bf14c79bc2e26d4960adc91dbf04c5eb847bc788f0cee4f05deb8ce689f74e7859237893582f4bca0b2be35565f1ba2a399349e33b1dd678c478c538517af2a256f826325eed8b0f1ac0a7d398ef9cac6595fc8f1b6b3456bc97c29acefe121b6738437cac881735c43741b2a643f34b6e7c9a29c17b16bca8355e9a31de0df99e2cafce98671a7abfc0f7fcf11ec6f79c709623c4c742f0ca60f29d16ef8e1cdfa49ded04e099a4784f393c47e5d58af8a00dce721cf04b1a9ce148e06370bca97d5b0378b7c0f7ecf1b2d41726c8bbdac00b43e1dd81e39b76ad09eb97acbc27017cd0033ed110f21e95d74907ef980dce6e0af958005e9e13dea4245e9b31af14c5cb2382570c0baf4a02ef0be83d59f1dc126b4e7078c7b0f0a68c785b899ce5e4f14babd72602af54f4f278e01583c2ab69f141e45a0682ff99e3555df2be50785189bc2ded0c026b392c7c2cccbb42bd2ff55326c5ff38f1f258f28a21e1acc683f78e5e15d1eb3ae4d528f0414baccd30bd77c1bb58bc4fcfab61f9209d351d27bf34c7aba2e27549f2aa0c795f137cda12fec90d2f4ff8267979b5301ff4c4abbdf14131bc3bdd3371f1ce24f15d6ced54c877352f8f0c6fd216af8d23afa4be361d782526ce5004f8a649de13fa1c0e6738607c6c8af784f34179bcab23de976b6d6d3e830efc487176e3808f89f0eea8cfa4c8ab63c733a9ce9c9c5e3131bc279e0f12e4d52cf81f4268b438e3417ac5ec9b6ae26d39f2b22e799368d66c54efb95ed522efabe74529e06dcdbc5a023e68cbbb63f44cadf774c673602c33f3c57ba757d581f7a5c2ab7dff037b978af7a1e0e5a9e04d9ab27683f4b17d55525e1719af66c38b50b486d3c2c7d6351c3d3e06c6194e241f9be35d11f2c278de8de27d78d69a70e097ca7851e93709f2a60c795d3aaf96c1ff18729691ffa3e545a1f1d280f16a967c5016efcc0edfc1d69ae8f14b517c9a51c07ba99747cb9bc4c78b5abf899097a7ca9be4c59a8c0adf89f02e1def83c2ab82e27555e0e5c1799342f051dfaff89eef4d65e07581ef0a8df73582b31c293ed6f4ae1e79611ebca93cde96d2bb53c5337960cd6688f7626f0ae96d19e05d15fe09102f4f93370948c98757c5c3ebcae35d2bdec782331c463e96c6bb63c83755b0d604885f3a7a556abcae11bcac2b2f0c8fb39910be23e4d51af0415fce7278f89825ef4e0ccf84c7cbc3c29b34c59a0e05bf44c78b22fa2646ded4136fcb016bdacdffe8f1a6a8bc2d2ade9396e77cf08278f7827f6273b61efc13ef0c67e96306785522785f2fbc3be93325f2aebe785f49ef6a8bf765c9fb24df03c8cba2e30d13bea7189e5b618d6701af180e9ea1c9e1bdefdd89e2992e79550f785f1dbcaaf17555b1b60d789f1cce7234f04b402f2a856f0ae34d69795b78acd9d8f0de075e96052fcce8e59179933438dba1e09998786d0879a51a5e16015e58045e8dfc1f0abc2b82f7c9f2a604795d326738a78f4979773ef04d2c9ce1fcb1f2658d799374d666ec78efe665f9f18641e1d3e901dfe9bccbc4fbecbc3b243c5301ce789878c5d8acad1eef23c3bb43c83739596b52c62f29b17692e3bb7d530a785d4ede93071fd480f764c6735f285539ab497a2f01ef09840f7ae4e581f22635f16a747c909577a7906f02c0bbf2de17146fca87b765c6598e091f83e4d5eef8a01bdee5d913d173e8abb1f1412fac69a8ffe1e3bd8eefa961991927b4264bfc129857c692efa27851327cd31bafca7c5d7dbc270a3ea8901795c54b03c5d98d141fbb39bb09c1c750785759bc2f0d9cedf43c93132faacb3731e06ca39ff9725653c37b4fbca8ee9ba438bbe1e2633b9f686a78cf7b6542f05d172fcf085e3153d66af0786f8ef734c373515e1e0fdea4f3b541e4957438cbc9e1631a785700785f5cde94cfdb125282e3134d1befb1f0b298dea40ad69a80bf24f4b2d678614b6b38247ccc7c9d80ef18ee55fdbc2e306fd6fdcffaa254dff4c3594e25bf24c2cbb3bd4944af76c40705e07d8def69e19d417d67c76b83c72bfdbcda131f047426e3c57735bcda231f24c4da0d0daf84c9eb44f58ec9de93cc07cd718673e5635bce70ce8f75797988dea43f5ecd84ff09f36a18fc0f025e569417568017b57d13142feb8a17a68177e1789f12d66ccef732794f167c90216b4d2af925325e96961766c87b32e1b9ee5d59795f729ce93cf24c53de14075e1709af8e049e098637f5f3b6ced789fa2a8de04565e06dfdac658efc8f196b4d847e89cbbb35fc13275e8d031fb4c5ab63c93309bd1a0e0f84c959ce037e698317b5f4b64638c381e46371bcdbc13f097a35453e88e82ca7838f39e0ace688f7c0784f2c3c67c2cb4a7a936e5e5411df14e62c87898fc5e4e8e1d544f81f07bc97f13d2bac3551fa2535de9d3cbee9e64d8df0b684785575bc2fed0c87838fe1f0cadcf15dfaaa2ef0bec457a701cf24e5655de00da3e5e519e14d1ae26c8689ef9ad678e678c55cf0b26a786173ac3591f24bea5a930afc52183f55acd160794f543ae2dde9e39b745e9e2e6f5220afe6c1ff30f26e947f12c45a930dfc5201ce6ef2f81806ef847c8ffaae007961a8b31c4b7e29e8dd219f698c5715c7eb6a7a353c3e088777af789f0bce6c4c782f036753d6f6e79f70f06a4ffee7909735c81b26caeb448477cc07ef4e0bcf34c78bc2e2a579e24d89795d297c9a79e3bd9af774c273dfbb627a61359cddc4f1b10ace6ebcf818ea454de06d4df0aefc78613aefaac9fb3adf151f2f2ce7134dcd7b19bc3bde3375f1aa76785d78bca7289e3be25d5d795f78bc3b579e499077d7f7c8b1ab8677a6e83b4bded4066fcb5cdb49e3994aa014c499939a578c96d709eb1d937a2f3ff363ad668ff7e8785915bc3020d69aa4f04be83bb3df9df1f25ce015e3bda80ebe89002f0a92b70580f72abea75bd3c1e1970c39bb669dacaf12095e15d0eb5ae23d1df1dcd18b12bfe98a7759f82760ce6660efb15e1409dfb4c59a8c08df71f0aa60ef2b841705c53771e05d2df1be26b0a693c12fd9f16a5c7c1009af9312de31e31a4f1eaf180cde05c03f91599359bf235f270078c7b05e5515afeb02de943785f3b65858db9c93cccd77179ce154f0311ade14ea6dc1f0ce107db7819727ca9b04c5bb227a5f0938dbe17926255e0d8c0f3ae1eca6041f53e11d8def91f2b2665e180fefdaf04f9e7857dbfb6a624d66cb7724acdda4af4482f7f4c10739e06c2783677ae22c47905f02c0d9cd073e66c28b9ae46d3d79772ef926282f0b8817b6c8d98ecf3341f1b24c78615cace904e097f058cb7e5e0311ded4186f0b034a56de15242fac3bcb71e16392acd180f0de076b38327c6ce8ddca6a4b7c1008673417780f871775f44d0f7859e70b03e4aca691f7f8785766bcaf10bc2b21de57035ed47e530e2f8fcf9b64e555b5ded7072f4a836f1aa2b3e16595e04d72f2ae80ef31e36565f0c2bebc3b957cd3003e65ebff187176b3c7c73238cb49e4977a5e8d8c0f82f2a6ee785b24785397bc2ef26ccccbaa7961422f6ae69b7af092785978bc61467835f3836a5e94256fab83f734c57348bc6fe07b0ef0ea54e099667853e2dbf2f2a292de9600de551aef8b04af06810f1ae2ac0603ef21e04d8d6febcb9b92f2b69a78b500fccf226bd995ff017356c3c17b46af06c50705bd3698bc92142fcb8717e6c7da49fc4e106736da7b8ebc2b9ef7f5c3a79904bc877a53366f2bca598e081f83c0598ef8b108bcdad207adb17652c077386b4d0cf9252b7a7078752e79261f5e151def2b3ba3f1e03d0fd69a0cf14b5f5e16162f6c03671909ffb3654dc6cb770338cbe1e2974eef8e04be897b794c78938e789da4af92085ead920faae245cdf1d2a8f14947885fcae4cc46e73d08bc37e07bd8f8a1e2bdf995be7897e27b80ce70943e16c8cbca5e580e6b19ec7fd2786fc057d2c0ab25f1411ebcab455e58066f2a8db775c92b0382efb8389371c07760ce6e46f0b114d668f278cf85339cec63515e96062f0cccdab6f13e24bc290bde560e2f2a93b7c5bd2b385ed82f6afc262c5e94936f227a6f685ea98cf764f41c967785c90b6be14da9de560c673b3acf74c459cd04de13644d8685ef82ce7266f818065e55f6ba6e7853ebdb0a859c5ea5f9a1dec671aa478edcd79da0ab0cc371ecd25534531154d3b36b9529a782a1178a68ea8164ca8d1028c9ed4c3bd253b75315cd501d420ce2f48052edc035f5448f14b71f2b527078a4dc5474edb83555512eedb88d7440499ae0379e28b78a1bf8a56303ca4d5dc37125432e15d713ed3e474a4f35d76d25570f05d353dd440694abd7995c1882e2f885694a828d9428b87961e76da3e88168778a4322a76e318f368f88132325b8925df7ad23a98aa2b9a9e4024a711bc72ee440ee5cb7cff34e05949b798ee167a6dbf7a52488868b949f897aa4a7aa5fc879dd866e318f84380e3cd2826641da38454a311dcdb453550e3d49534cd3444af4e340af1357d3233f9053c50494e06976a7ba81604aae26ea8d8894aa4aaee3e799a1876e2a7a1e52ae20fa7de0f87969ea81608a1a526edeb8815fb876def789a0b922a0fc56113dc3ce23bf6d153d2f2d045d41d50c378eecc414cb01cad54bd52ee444f43b4d2e44b91c4e8494297776e3e88d626a7e9faaaa83941ed9715e28a2aaa78622187e4322a71a79e9d2719d2025097ae2a986e1e87d21b8a1ab014aee5bd57504d1ee4c53533dd340caeddc4e2edc3a153d3912153b404a4ee4cef54cbbef34431344c13fcaf4fbba50244f2f054d9354cd931f25f875e32a7e1bb989e0b816090d1d992c7c7d94a9f8ade1297eeab6a2dee681249cf8284591fcce4e454550dd42d33cf72853f35b3755ed4cf153532e558bd211cb498f921bbfcfe356efdbb6955347f39447098ea6f77d29b88a9bbaaa5f3724721bf7c3098f1224c56d1dd14e34c57335d1b573e107ca0f47b271baa314d7cefc3c6f25d7d3fcb61014b79cec28bdf4e4c2d0db423424bfb44b8744cec84815ea6272114f7594dc689aa4b7a5a6ca89a0d8c5e452fe40f9b1429ee828430e0449955b450e4445b343436e8ed254bf0d1dc16f43c1aef3bcef0341394af0ecb8d5e4d6531d55d04bd70e8f531c65789a5c4a825f3876e67786e357431112d1142a57846438c15182e7c69126387aaaba79e9ca9dde28d18efc3cd5144532e44413698b17a42d5ea0fc0001010101010101adea0a404046eaaaa69bf8692438b951821e678a5eb872231772e906aa716aa35cc5b45bbf74edb66e0d4d8e3cb151a29d897a9b677e68ca81a60a760638ad519aabdaa9a0c8a966ea99e6b99dd4283f7205d7331cd3152451b3eb845091bc70111ab92e8d32e4428f44d7ad1bbf734c49f0088d525cd1d304d76e4b5772544f67946717721f0aaea7677ee1767243228766d97204047432a31cbf941cbd0fecbacecbe3544639aadbb88ea0b99a274a6ea37a2a406976a36a86dfb89a670a8a2b2a8e604e6494dc9676a3b98adfb68d9db7a6a7314ad1dcd03434d1ee0cc790dcd621910ba1c000e5e7869318e5b99123a97aa10772e0770aa31c378e03d38e1c43ee43cd95ebe20446d9ad9fb78adcb7aaa6b882e2f645999adde98d1b697e66aa7e1d3a24729ea8e5e445a99add0aaa63e871e82a7aa8ba7e5e17e5479e28887660b7ae27d979dc911b272e4a511d37b34b470e3441120dd1d31625b77ea0297aeb686e5e689aa778d2a2143fd553377514552e1cb72d5481ca15a1214e5994a979ae9c4a7aa0979addd78ddfd091c9029289121541f942218a82b002043474640e8d5894ab088a26e871a448aeabb9911f3780d315a59a9e2837a69f4886ab9886db90c811097546272b4a114dd3f15c377415b9ed14c12a4a343d53d0343791e4c26f04498e0b8a83bac570a2a2544d7034476e44d7150cc335edc8234e53941eb98e5dcaa1a9fa9926d99dde7792a24cbb511c5311dd38d2fbc094ecbc38455176aa7a72e0969a2917ae1e59e50445c99123d799a6797ea3f7a51df9e5f4444986200772e0378622b86d4245e9487472a21c4d70fc524fe556520dcfcd3c35516ee677862967a2de89893244cf5434c74d14c32f243befb444d98521caad29888ee0e6719e12a59986296aa21cba92ebd7a9e42989f25b3db01339734d3930dcc4318b1312a5aaae2b777ae23aa6e4079a6253e98de7a892df2aaaa0ca7961c85492e7787edcf799ea478eea111855cb071a694ba51872a169a25e68822b4872e2172b4d8e43d54d14bf74e3462eec8644a18d43322a3afa34a5b2e34e534dd7ad0bc36ffb42b004e52a92279aaa1bba9da3c9a920094a743bbb30ecbc2e05475086ebbaaddc49765b088264068626825244396fed40125d39d0333b510b41e9a5e488762927aedfe79d9baa81a00ccf94034174ebb8d0434175cda40c5514454f725c3d55ec546ec4a40c372ff446af0bcfefdb36551d537e2bd77d1b67925b88a6a887d4f9a0892945cff3cecf0bcf0fedce500c8744ee28cb1675fc4049a29d8982e167ae5cc8ad23e98152354911e546f02343130cbd534b2a4d9433c18fdcc0d5fcc6aeeb3a50a2dc177ea8989e29f76d27c7691c28b9d55b3d32454fb15bd54f1cbd04689794e4c8a11ca7822229a2a6096e432257547655344bca2f1cc5efdb40923cb98ef44242a101e88b917ac475605022d5c8c80b2a86b601d44bc17125b7503c87446ed440b98567888eabc7a127099ea9aa65a03cd791fbd20e15c1504c4faf23aaa4e440f324cd3315b754dd3ab5a3a45c373435c70f55bfd10b8981121cd3d0f344f0e4c2eedcbad044325321920397d02629c77133c3cee452eff4d4ad2b4ab7ee02251782a07a72a24886a23782e890c87ddc11c91d2165e192654b38832649e9a9a46a92aaf879e40a8660b674e9620a15a5e1105a98525d43f03c55913cd3550c577348e4364e2cca22d4e5088ca43455ef1c47504439d2343f8ffb340b94e189aa6287a22627a2a9b892e38f1529404069111424b54a17ad02a5799e63f8ad6aa792283a7a219189761c054a0e4dc7745b41ce133bf2234323d38b1423158cf902121a24e538aaaae7a11f187edee871a736815225519204b753edcc6df5507048e4908a8a8c38aed324509ee9896e69d8855f1872a3786e836811283b13ed527215d5cee3ce51110d568ea8e96de6c991e0da716b4822797995e2988edbd9a1aa989e6a2a7a9f219aab54d714ed4c4e25cfaffbbc900323449151975639ae27ba9e1f4886297a86e1d97185681028bb0ff5d48d14bf3525d573151f50aa1bb77a5f78ae9fba89dfca79dc23a5da795d27aaea089220eaa1e8d735a239a044c3540cc951e4425504d3f368e41a507ee718aaa1b885a0f78da248728e94e206a2293986e0c87d26077a9ba731a024bf55e53a7024c930e4547224029342f94943af91f21bb90fed3813dc4c944bbb949172fc44b1fbcc105cbbf314bf8edb03680b28c1ef13c96e4b4f5345c5f50383d014506ee2198226c9919dbaadabba0d89dc88a42d52a25fe781eb179e21287aa0c881a129526e9c6a76a2f9a1aab98529c8454ba4ec4494ec468f1c515535c98d4b40b9a9a03a72e2b97d5e279ae0f96dd1102949af0bcd30ddd2cf135774f586444e8822158080525588a2331489b4434a0e0cd7754d45530453941b3913c95c61ca6884d4e54833a4e4c0cddcc2534cd1740bd7f13c85ba2e55896a00024a1150aa1f48ae2018821c1a6ee7868246485d78d00a2941141d517323bfd0dbc06dfd48aed30e5076e128a69dd9a99f3824aa5b4ed3088d9052e54eb02343f043bf313c49f0fb0629c75505bbf1f4c0f55c3dd44335414a53e450513d3ff04453ee044336340394dc087621979a61e86d9c287a8194663aaee1f675aa7772e4468a46eaca09711fb70cd00029cf8e1bbd0e15d594db4e944ccdd0fe28478edb44f223c7503c4dd2fbfc28c150fdd6935c373444b98f43fb284f914bd1750b41545dc56ff5d6d0f828b76e3b557245cf7005b71414f728bb6e14d18de3c091ebbc6e25534d8f72f34c53f5b8355dd77014bb8ecca35c3f5355c171edd0eff33ed1fba2e1518aea788260aa9edc8682277a0aa1a6441e27c475dc1da5276e29b88e24a886ebd7ad3f507e42cf8e725b456f25c18e4ccdee1447914452ab6875946ba78e5eb88ee2f951f981f2c3c28f95177e8080c42f4040e2978e3480464799a21c1a7a2b398adfb8a5a43747b9a99bf889dff975a7d799abf786264799aae7ea7deb9972dd776e1caac5519ae6f891dcb7ade4869add6776709467b76eaa478e5d0a76e397866e686f94e73892e7fa75dc2a8a9b789a432227d6a1b9518621e9ad20686e69d771de760e895c1733bc810ead8df204c37423c7502541933cbf543542c120ba3934364a6f0b394e0dbdf0fc52921b3d6d8d92e4d68d54b751454f4f35c14f53a334d1703545731bcd7448e44844b4344a4efd46134db9710453cffcd0219143808646297aa1f77da8b86ede897adc3924725c97887646d9ad27198ee0b87d5fa786a43824722951d129040645528f46eec78a94a320c8ef10cd8c92e338154ccfee2351b3fb3c540849c5227248aa942bb432ca133c5554f44872e34212ecbe2191fbb80294aa9a86aa8a9e20288adee6a94322d7316964945e179a29b99ee4ba8a1f7a8e519a676a76de6776282782607a8ae3179a18658a92a0c7a9e0c98d2b87aa269a65cbe945062020202020208e134d232fa66887164629a61d97729ea882a448a2aa51102a51c80d2199327c434866970f0c52a7820646f971a8c7a56a1a6e62178e2aa9760163050848dd62a2474661685f9462e8ad9ec7a55e3876a7ba6e9a17e5baae6037ae244aa2684a829c205a17a5297ea7d87de9266e9fba9a2417a5b99e5be88d6ada8928796ed962a21f51948ed3b6283bce1c4dd20441f2ecc28ddbb88c485e60208a026a5a9421c891aac789dd3872dc379a5cb20c1d01a95b4e13e9cbf6695994e1d685e2679ea667a2ea8a8a4322b70a21a947aa51c765318dac64c192c528060d8b9233c593dcbc351cc7b33bc18edbb42b4a0f0dd75144b72e254d744c4d2b4af403d7d03b57141c49ee3cb52aca4d0cc9ed0b530e0d4d72ec3c2a4a4e2451ef3c455535c9f4fbd41eb4294a50554990eb3c103cd714355720a0113c3a412a57846ad0a42843f10cc735f456f1ebbcb0f386448e53a1eb46f0e8d4a228bdd14b450f0ccff0334d1425cda1113c3a1a833428ca543cbdf53bd7d30bcd701bbb274a74154574f348715d57f5f34ecd89d2ebb6b04bb76e3bc90e1c4d301d3923f5c823416ba2dcce91dc5473eb4c5125516e1b12395234528f3c8d89d2fb462e25c14dec366f34536e48e484309188c01c8520186a4b94674a9ae27a72df877e21f89d4322077e31684a94e42a7a239a9ae48776ea77aa36684994e197a2e9969e1fd8a9a4b76d1a12e548921fca792b9782e1a692699722a3158080ba1419a5aa6a84740201914d65ea819f88869b499a6adaa1dd18ae195379aa6838929bba9129f8a16b3a247263c779114a8dd421b254010232528f80808080964a35dc54ef04b96f04513425bb113c3a122a42023b544d8ba08ce051153214a95c1172218b956b0776dbf8819f9a8ee6d8ad4322d7754a25eaa15b9a7a6b3a766068865e911091897a25283d522439eee33cf223bf55043791ca1521352341697e1f2a82aaa97a21288a1e1a8ea0f4be0dfcc6cdfb3ad3fc506e14a95c11ca9289a014c14de45470e5507214cf8dcb42507aaabaa55e687a27189ee62982a0ecd67315c535dc4c4e3c5590cba43c550f254571e5561005396fc5a4043b91dc42cf4b438e34cff31b53aa9ca8ae2909aa22c87969aa7a9e985244d5955b378e34bf0d4dd3f30365fa8160878262caa55b6782e0f779a044d70d44b7ee04c1140d41d4f4bca4f23bcf0f15d5f3143b506e2718921b2786a9996e1b297235641c28c571f4528e53d34ef5d62e54bd4bcaeedbc42e44d56d544dd3f4c62191cb2c2957b2db42d4e3364ee4c62f4c3750861f7872a047a66afa69a044418ff454ee3bd36e235190cc40897aa9c875a6388a9bf9992a2a829594640a761cd77de467a65bb78a9f474939a2211882eb46926a0a9ae80804f461a0ec5075db36103d43555dbd494a0edc46f0f3440e1ccd8d3bd7cebb40d9715f3872a2ba79eab789660a4586177e404232494ad3344f33444d5245414ff5ce30e5a97ea7b98da0398a5d24e5e79de43792e697a2dcd99164e759a0f4cc93544d4f1dc1b50bb72ead40998a9e2a76e306929b19ae2305caeee350545dd3ae13b710fdbc0d8a0c92b223439354b7d044cf75033fb09b4099825cb886e63a8664789267da49a05441750db76e13456f5d4f6f4bd114a0283f507ea600017531b900011d19a94040462a529628b20894ea1a6e9b8982e0e6a5e2d69119ac1cbd513c49703d53740d5152fc5e65c7a5e128862b088a26d979de7747e62ac313f5cc50ddbe2e3cb753f4ba56499e9cb89de84792dc11044a8f53c1d13c556e3d4df304c123ef0165fa991db78e63a7aa2188ae5b15d92325da89a7e9a51d487a26a78648472a92f9829844e680f2f3ba74fcc873fb44d034d1b5eb1a509a69378ae24a76eac6a51d37769d2325879222b8816b086e21c96d6ac78052fd3cd53453b31bbf46cad1fb3e941bb915e438cefb56464a343c538e33c5ef0bd10d0cc9ae5b40896eaae78923da7d64ba719dda750a2857cefc3ecf4bc38d1453f41ce5c81629c91105d1b113575504c9140dbb4e9132dc567455cfae1bbbce23bdb54ba4e4c470fcb6af0b4db453cf10dc1250aa262a92e4a89eded6995f28723d64889424ca9964a8aa66087ae908a687941b47aae7468ea2078a27997e8694db38aa5b2a7edc48a226baa19e9721a03c4db40b51ce145192f3c86d9c232ba44cd5f15bbf35dd3cd444bb6ff4bc0394e9769a27a77ae2a87a1e2165c7899fe9ad2b777ee7a99ea7e70d52769de879e21972db487a5cca9154ae081d9109529ee2f671dce7815e178620e919a034cd6e24cd505ccff11455aeebb627b2404ad53c3bcf14c513f4d674553b40ca70f3d6f0143972f3c43034d53455918c8aba9086ec8f5244456e0b5574e45675ddc2cd8f72fdbe0f35d13155bb94e4b6f48cac8f3255b771dd3c343cbb5555498e8f52e540af5b531154b9b3e3ce318b6c8f325d3d9233cd8f244d34053fee28d3a32453530541525439ae4b532ef5bac8f228d56fec3c9053d313f55653f4c6c8f0283973ddbe90e3b8314d4593cbee28c77513d7900ccdee33372efd323bca8fdbc28d33bd341dd5b553bb34b23a4a75ed542f154d7545d56ef5d229323acaef5bd56fed54b02353f0ebc039ca35e5d2b323cf2e4dbd30f5be6f94a31cc1ce0b49135537b2ebd06d75228ba3dc44d1333d5135b7f54b4f12fce02849310d53523c3f74f5cc94f4de28cdf5e4c2f43cc74e45c7eee3dc284355345393ecce2e05d19f54f4f2a31a79392ada82fe9ca2971fa1b11bdb28c1f053c1cd4c552e24436e5bd9283933e4d6f42455724b41d14cd728576ee33cd2534dd2133b2e0487440e8cea02a97a6a9426a88e62e871e3fa9166777e64646994e1a8aa24397ea1e98522ea89de8746c99da2987eab19a6a99a921fda9d51a2a689aa6a078ade178620b97e1ee8456646099eded975a4f97962d7a1e006c60869f4c01859e9be0001ad9195516e6a089a5cf881e0c875a01816a0fcd213453bf45b55eee4b6f57c2123a3f442d53341904ccf30ed423315c728c7adf3b64f4dcdd11b55aee3c428576fe4b6ae03bfcffbd06f5bb3304a4fed402fdc5074053930e4d64f8d0c8c12dd4053143f2e3d53cef4d6f1fbbe28c3f534bd500c43d353d1d14c91bac400043452b922f425f3a2f4d2d004c570ec38afeb42b0eba2e4bab0233b9413557445d7f50302fabe9042dcf745c645399aa3b79aeaba921bba7d5bb64569a26b38aa5b378ea229aa211b9916e516aae21786200a9e62d78164964589925bd8912719a29f9aa6613a24729161519adeea9d9eb8a29dfa91deb6665794689a9a21a79ea818761b3a8e276456949ea972ab07aaa92a8e63078e6a9a55518aa96a86e40a8e5eba7621a7aa671623f4485d41089424a3a214c78f5cd1d023bf5325418e3b2259c8a650539d4c8a32353f721b4195ec467334b1106501936e5c1112d114b0eb6272f1408ed3218ba2e456510c4d144539355c47cffc81425494058b4a0310101050c70971e01f1914a5d971a687766a1aae2407a65d141a7e98c89e285530fdc8cef3ba6e033bd55cb7cea32373c2fc3671edc0affbc0534ad644e971e7a9aae2887ade0692a497315192aa478ae7ba9d63f8752718664b9464277adf397a2728aa5d1aaa4322a772f172747a391241ee28dc3a2a578460c89428c1cf5c4392dbc0f3eb3c303487448ec8c8cb49c210da902551a6e4f68d1f2a865e076e2ab77a65644894667a9a22a7825cd889dc1992432217723f565ef8410202fabc6e1bc36c53198a9be989241986619892a2c95486dd399e1be8a9e0488a23d92d95dd399ee1b88aabb7ade1099a9e172bc17103b7145dbbf504370ee43c4fa934d52dfd481424d1ce5b535024edd81294deba72ab2a8eea3a76e8977e5df7a52341797ea1e9ada2caada42aa61c3882f24bd7edf444702547cedc36741441099e1cd9a9ab1a86e04872e318827253bb533d5530fd4690e4c60e0425baae2b8a86e90982298772a2e7655272eb669ae9fa859fb7aaeb360a711d26a579aa1d388ae78672202786df98120cbd555539d25337d223476f48e44231e54a7a6a78a6e4a8866a7a9ae707ca0d54bb0eec3ab0fbc8b53b4fcf03a5c8a99b4a8e9e18a25b678eab97549ae04a72e2baa6a70776a177761d284d544551320cc1901b4f6e8b172e425f152020208e89e540d991e3ca8d5f7882aa87a6dd7949b9811c287a660a92df697622372472a91016a22c475e3c4eca5a52aa2aba89e1d8a9216a9e9eb96da0ec3ecfdb4c143c3774e4d2311d123922750baa7e1122b928ab8132e5c25424b99414576f3cbf6d48e442750527360325a99a6087a65cf771a9687ae6d875316108b92e2617a18d43da4aca6fe4b84e25bd4ff556f2f4404a4a6f13d593144dee1331508e9c7a86e9988ae6289262d14a94450849c51216ad4453b6a84444e6101424550a233b49798eaa08aaa899aee836929cc845453a0273c4791728b7ed43d54fec509424c313e5dc58494af124c32d0d5512543baf03d55568e31eb061ca70eb3ef5134f54453f8ee3ba219168cad1511846527e66a77e6a08821c67862b780e89dcc70171507eb6f24786177e2c50aedfb882e6a8a21b1aae61ba0e895c2a74940508888b90aaae2ca86a941f283f475fb29837fc5879e1e7088948252aeac2a5c848c562129930a86a142e280e40405888b2a8eada890ed80a94e646aaa349925d087ee7b96d2051d1162840402a51d11631a440798ea0c871a6ca999ea7ae9d0a49998a9c9a72ea0aae1dfa79ddb87142dc04ca930351733ccfef0c53130d372ea810421b146e8695407986e8666edbb6ada029761dd7c534ba2102a5089e27aa8e1fb7aa230a8aab109853e8f390b29c63c7091d7d9cc7c14a8ffb3c3554d7d423437115d75749aaeb4672a1aa9a6a8a7261b7aef24b4f72dd4e553dd1b3fb3c232c425cb7ad12fd407053d7f4e35455ddce50a47245686821508228b8a1287a82237aa62b19724171784089a6a81a7aa6077ea10aaae223a5b9a2eaf6799f8a8e9cd981df90c81da9ea2ac47158d601e5a9a6630a729d6792eb09a2de90c88d462a11d1942c5d8aacd4308494e506202020204e8b6d40f96d9d998e20697a1d1a9a9fe9481986e286721bb9799da79ee637247240a8409440405db88ccb80721cb92e04377305c34ffd8ec68d6ba43c3f7043532f3d43f4133d5365a41c3df5134f521c537035cf714a0a1d1dbb80b203bd4d25d1901c491214d34d01250886e0998edec8811f388adc19bb48b972e6d96d204792201aa226aa48d999e9fa6d643a76a219825c2804c60869e3c2d8444a931b411524494f245173e35264b47eddf2b1092851904bd71335d1955341724587442e35f27244a4821cc985352c2265178a5be775aa899a21787ee496330bea25558dc4ad8b3da4e4427244c5f30cd5735b512fedc22b6b48f9ad9f3a9ee7d68923d7795f382472e46784d4c55b0494dcb6aadd279ee8c9851db76d43221744d19623d22885904389c01c7d03d8424af53357d213c1ee43cd7135c9ad8d3d40997aa6daa5620aa6deba75dcd791425c77c51252722ac7a964ca7d2777ae28970e5282a7f7a124ba8ee24776ebc92119993280498b54f03c41ca50553bce54c973f4d2f51357d18c3540496ea9a872a4279ae7789a62d875819427da815c387a6998aaaa88729e074839a6e446aedcf68123c88da729c4795c57c7fe51865e78aee6487e648a86646a825b15eb47a9aae989a229078aaaa776e2e7797d94a0d7adeab785dd0992e066aa1bd7c1b17c949e17aae2b975a0da7ddec7895a8c8a824052a9ec519e9bf8a1a9a87aa8f9a9a037020179a410e7e9519a20986e28da759c987aa7099279942bf88163aa86a637aaa4fa711e29c48dde970e8dc5a3e4c6ee0bbd5115c36f0b436fef284f73fcbc1444bd730ca95c114ad78e521dcf10f43c92dbb8d3344fae0a10101050c70971a8fa85e348046c1d25f7791d6a8a22a876e2b87ade4647397a20b79ae96982e2d9792b3a4739a664caa521b992eab8aaa72747d985e866725b38a6e83676eb070484a41e1d99601ce5ea91eb796ee4f98128d9812b29c4c1517ede9876de0a8a9e29a69b78be518a5f67a6a2ba71a8798ee7086ed9624211e2ca58374a4fddbe7433d1334439d0dc46928d6da3e4c26d45bf1304b76e4341d463a3f43ad2ebd0703dc995fbb8cd5ba33cc92de4c25045d7144453f153a34cbdb13b49931b539114cd8e4ba3ecd6f1ec4050fcb64e0d3d9544f2d205c94b0c54ae08112d1aa5b7ad22b8919b79aaab9a926017fab067945b7a8edb7672e096a66a87a643220704d479a319e5aaaa63687e6beaadaaba859f0c5b46d9ade61a9a1db9795c9aa6a039247232d0f0f323031816a0e4d475dd4e313d37d0ec48743b0202028d90ba905186619776a49aaaaae9ad9b778e519e274a72ebba85600aa6a3391aa95a8a8c4e4e8c5224d35524cd314451500c576e5569800244c586517e9d8772a6c7811b18aedb8a0a913f56a470421cd8bdb16094e8e6a5e8989a609a72a9f9a140401c27c48d80ec1725f87d21f79da3ba9a1eb9ae1d16eb45999aab1aa2e038762308a6dbe77d9e18db45c989dd4a8e21b879e1b77aa267c572517a2a9776a62992a6479eebaa72294291bcc4f07d01020a4921eefbd27164ec16a57a9222fa711e79725f7a9a27101010d01129c4914760ac16a5378e1cd8752b89a29d8aaae428c46d526c16e5e771e429aade3882617a92e890c8211165192a5a81808080b88d13e24635168b92433774144f8f24c58de47eac482152bf94e338c45e517eea896ee0c78d5e67726467aa5eb69836a85eb6985fc02cd68ad2533bee235714edb6ad53d3cde3b64e684b172e593c0ee4a85c11cab8d82acacd0b439224d1943bd78d0cd5b408252a431055d3225448080c521623b3cbc851b92274c252517ee1988aa93882e419a69ca762f152222f23093b45b99adea99260989aa399a2e24a51a2a3277e9b29769bba9e26d80d891cf96345cacf1423a328a8daa50a10101010aa7e21a95c11da26368a12ec506ffc3a6e53bb535c557448e47eac4881f2d379508cd421a4188c6250b998558080d0a32c5c54d4dcd24588288b509722a3b4fb4823a42e1ecd230b45c97de61a9e21e99de490c8a5aa9a7220e8659f28b971fd387135416fe34653f586444e28e4364e1cc3105d274ad0db44131dbbf324d734fdc21f2b527ea4d8f003e5c7851f2b2ffc0001010189cbc536518a67179aa4a8a25b88862b990e891c18558b5017930bd76d5c17938b6754448a1e47e58a90d03251a65c687a9ea9a25ca8ae66fa0d891c16a22c4842a6d0e9e548a80889288bd8a5538cd825ca3424b730e5b8711bc9ce14c1219113222ae2ba1f2b527e904e7505a42ca96a04a54b91d10a4431a800e5274522f25284a2298021c292a50a10d08f15293f36000121a16a9723a22940403200010101fd5879618aba65b5f2c24f0d443100117911222a0202e2920508e88848d552844483ca250b9291171580808080448ea37245c8a69155a244d374f34e151cbdd503d333ccb24994e01a82ddfaaddf18aae6a77a2a2c1265a8aee8c69da0489eebe77de258854b9151142020202023a4a3908b17a22c042837ee1b4995f442af23c72e25855059305283287aa1ebe188b203b773333b74f4b8504dcfee0b4f234a325d415134d7d413c7edf4d221910b4225f28cd421d4c854ab182155e13822231509c9ec221a211d15516edf976e22ca715eeaa9297772112252b7889c10982e4528f893825e7eacfc9ca0971fb25389284772544f312453541cb90e553d1686283b30fd3e3545bd93fb3a8f0ccda128604a72db4412f4ce0d34cd6f535721d2293f565ef851b11099340001a958884c2320a0a32e459c48e58a502a44298ee2a7aee6169ea2e9a9ea2844724648476010e5376eebaa9aa779a6ab79a229488a5f4af43c5194dccc753445f32457ce08e9c8ebbc94eb36a2a6298ea6f86deada09a10a85dc8f15293f46e6507794160d21a95c44237308fdaa2095eb26ae67ca71aa6772646a8a484747a56aa2e4ba9de2ca81a4ba72df96a2a2f3a372458805a3721dbf0d54c3d124d35414577148e4843a21754b172f484258bc9c6a0a762510a528929d979ea7688a1dc98de290c81521116511fa422f42ea9191d9e5e3be2e2549aaa2488a67a79aa3f791e2d8715cbc10794245a5c98d2a08925b2a82a908221d657981242a41744c3d54ddb63025c931dcd42d5dbc58d948e0527e9d48aa9ea86e1b8882640822a16a1721d42da5f78527d9912adaa960f7719e96522449905bc7f41b3b70e440ef871214538e23bf2f14cf73fcbc354b9672153ff25cd1d34b3b8e53475145ea62a629a4a60c40406348e58a1096a17215b951354593fb4e6f43c17148e4c02a1c7764a48e485cd612b094a1b9792989a69fd875a7b9824322c7a5c868258f904623a423cf5b4b7423d56d33bbf353371335cd219113dabc14d492fbb6d11341f45443b1f3d66f48e484c0a45db7658b8a7e645a7a64f771a94a86ebd6ad247822798121a472456807b4443b74e54eaf23c971db40530502dab24545b790ca15a11cce120c5530543d7555c30e44cf6fdbc1f43aa0524c371235bb2dec3af2ec56d2f4a11c5571f55675e3528f13cd30fcb122e5e7c7ca0b3f4546ea15a2222a40404546ea1044454440405cbc108922952b423f089521498a20f98129b76eebd89120952b4238f4508e9c487a1d7a865eca85dbd77d1f0fa56aa6dd99a6e8489e63a76e5c141a7ea4ec50a2220a7a1c97821d8aaea6fa9124e8506ee6c891278aa2a9189aa2f80d895c6a76f180ae04940ca8f4edef94402009410cc60000035074ce981999b301c31340502020140f88a43289a4473b3e148009327ea28626362389c210419003310c82200681200618438c31c628a418546600d0351d347048af7265740980022dbe7b53505a6db898b2c90867ac693de346640f80ce160c16bdf46cce72f4ab026223a5694ffb5587c29bd926eb1975632e2eac9c29b00020799cd5a3daca41604b6330011fca0150cc003ce58707a2a6b88a6eedf238039323fc8856c310d8a2397c22a53681fbc5527861289217c094a7afb223485748bc1781fe8569475bae391a43fa1deaa0c6c82adc5ae8f442dac8b11b4c0516c8217be50a5c1b483b224fc91cdde77bd0092a591ecdcf704bb26a77a594ea404f421c87bed74f1e1c54a12a46ac5da733b2a74a2c8204639b5f49c9d57bd181e6f0147f540ac442b5e07a9403f495d551c251313c05d90de741f853af41671f3be868d94ad9d015b5213226efc6f95e2c1269531674c0a5b5bac0616d7a670bf610c3a771be5587de7e1e4870b932edf53fc35c54c06e6abeeecefe57fe41dc118c9d12759cdc92c605812405f7c8495c5cdd8ce56080f2928975055e30d0857c0d279c50c2adc29850f11b70b256467b3ddee394720cc7682f4f0fe4644714aacc680fc8d354d00785e17df1bc8bc8f9d2bda088e9ef3a3940a243c28b02480858324756c61eb243176a269cb676af206575e2e4a3290ceefde8fe77930ff81f347e27721dbeb4e62b9260ecd9c941cc327ac0107833f8137f039c085741729edc00d8189d8f57360cc00000fc6d805ff17c1c1487f0dd02532018ea0d5195f02cb141dff614f6b1d111a0084a2b2bb4660dc4c0aa8f54331f24976f98722bab5856711b48e8612c5c254e121b8616f5fd1bb4ae6869a29c685dd6ffb804e02e6ae4c39a90472990bb573b23ac564b340bf5970459249d9513deb4e4e21a2231bc1b696045c7cdf2ce842060d23574a275b0d0456707f41515679021f7c74e9db38b148f16107ee73c19aad19d57430461fa676229bb2eae645dac0fd99674daf8eb6d8f85b5984eb718e0eb0e8967b7f104b54b8b1adebaf49596d4e01050228570449ceeeb21d4d76933c48943bf3b9c06426e356074a575d2920ee0e382a22c221d5e93cee57819099e12520717815e30577068f8533a1769c859e0b3fa0cba8e8292fad1eae7972577e8f74275992f820e2b37f14b0cfbc0a35e62f36b43d037a60d9249c9d114e182b052574aed9dce905278335593b32b3c7101a84dde773d66b870333fc18cc22303f975063ff228dc3bf76bdd9e08d0440419cfca73962bcb52b509ee5c1acdce1ea31dde2ec7b47346716cd14b9928038c849031f47e609cc5bb4234daefc55afd603d9227a456c4523096968425e808a6f8ac50b6279823a2fd5b3e70a5e102f9c8cb573ec3e203bde5de33ac5b0784afb01eefdb02f71c7d8a96b17d1bda61d99caf1bbd2e68c62ef333ca15995b6ea9b29c576ea58c651bf22f86e0551c68fca43b16c6cd8e215e34c3c3eba9856a0e07bf8d290012ed78c36f1a10a6f1c65cc00be31008e3b6aad145071397d4a017f3475b312c647131dc1b39090ee756fec06a903991b945930a9d69c3ed228597d0ef6d6acf981cdcfb33fbaae321a6d06fc3e2d53632f4c4156a026876a6ff10d849318cdc90edac3d5c906b01e994dd6190fa8ac257a2d69d13e33b40ec698cf6bc98322e3fc0b9ed4225eb701ceed684a5a2adca01370bca5649c40ec98de0fe1db6548a3c3632e7f103a96200301b0b05f80e37d524bdae5b88978d2827fe29469a01262eca2401ffa0443607383e30f04e2739d0bfd0ee574cee8f6ac9df3b13431153fd176459069cb4565fd6f2f339aa52ed68b2c1a08378abba594a486bace575b006de299c209547d690ca4200ed68da626b1c071a1d1bb9f0341f3dc24edadbfb04befc0d1fca1aaeca951a25caa205ed7cde8209907d74a8e4354a52de50c3bac15c519d4e4eface24a9661d44ec1931da103eab47ea5423c4471286aa050bf13bcce49b41b2a616f4c5f5b10582249c2ad0a51e50e0e18c150969faaf8f1c59e61b7d3e96a1839efd3e73bf00494f2bba304b28a12420bbdca3ee222b747d99f0d5bbc95f275b5d63895bd2d7b8777045c2c80075b994d35aea3f6d92292df5ceb6ba055381017e298c10f384105e595ee8a93a1e4c374b05e9e8a247efd2b45a3ee3c5aebcb4524715c851c5af83c7bc32a696e758484a030d315c1fdc775fb797bb26dd0e8c21231597c0cb7e8098943cd8e2ec231b4c74404acbe43abb3fa5b6ee94703195e7d994247ef9403ec96f088a510523a27a002547bfbdd8e5b8f21194699c322476c8ff250d4ceda120413f2cdaccb14237d386a493e9f4faa933b609e08e968cac8a36932cf0c55951beb5c8b80345e9c49c39a07d48b24791e17ec5f5c776e6a66d3d90c91029af158fae1694b7217286439abc0dc375971ff80cc1df399635403f73ce82233a74e472354093ee13f387872207116eac1b4662d67220a67016a219805092524be8251fac0dde99f5d637f5501da2ed2ba59571a007a7b3e3b9c0c6490a92c4a40cdaf97b226f60952e0988d641d0dde5e9f2d42ee06328173394eadf6f7afa845875fddc8f495373acc3e57f9a5c46f1ac549b70f031721882d4b846a03a0a519e0dddceb3f5f16f507c1ea5a75a7f3fbaa944c7ff19660d346e1dffb2a7d0524e02ab7dceeb8f25168ebb54a5800564a0b0ab82be2cf9f0c65110d04c61d30c628d6b421f809d5bcbcd1e9424b5cc21ab30e489dd885b07119f2fa0f40defe155eb2b002068b48ced2c40b5b7793eba6596c84990c756511e283784597325e332cd1cd91a48495c67805ea01baddff6a240ad4966e62e79c5d8eb166c00351962898a73c325c9c69e9fbe8080a670429fcba250a67c9c085a770194f024bcd92154092db295772c6b59988b37268bfd49624793002e442c47cc04509933d49985a382183e3e8ea098eb407a647b3d8a2a2792aae192b11c29c57032ce0a8d4a28041b007decc0e08db19989851b06fc0b8b804946042955a9088ef85be5c523ccb20eb8da9c76b0d7c93b0cd5f927e3cdc2cb1d1e4c5d7e100770a466303dc9e743940bd3fee46b8ac02b518e269ad55754b91d0d8781e45fd6a5b6cafc68d412858621b34e03d28f7e5ad7fd92fb72d53a45bfbb3ade309c6c442131e1825783d6a4100437a12308ea9724b2c4ec6c78f34175ddd438487da3db2b514d9910952e0b7d29c484b2e536eaf7583afc4e676b2a26ca85cdef6e7dc188a7fdd0661fa77110d2463799f2af0b9bce3961bf1187e3ec68336dd340e37f039d7f8acd4c67d7c49ece6045e979dc9e42214e2841b8a0efa4327d1d9e37c7033fcce2e472f52a793a1f87f842207d2148b0a67574f39e1c932e663386aa93ea56ef9f1a64affb6f4b0bf8c295c34cf3d3a5b5d555ef6dd0438319652c71ffda573304dfa5fefc8cc0ee1acb0c0e8ac5d55c061f4020b5f814d4f659217b20613629bf8bca79cb200e78228d3b5906647e92b36b6c36648811230858df148449a6c9693a4aaef18945980cea75e7148fd7fddcef1855616752a35ed56ad64b14d9c67063a7c94077fbc304bddfb2020439190266b698e29f4b24f281c7fba950aa730a079dc89f08dc9e5b80f690479b5872e6fdd11d3d832f18d11ef6c3030ca533dd92b1559da2a15b48a64900656be3572b0d96f087b45fc73ba3a3695d66609b28ea00b7d1b48f567841cb63090520c371ff000c5a4678fd27a9b083f7e0e1b39ce20a9fc7f2ac0a723e495484085d98d5f42e06033d41887738652bc8d846927c28cbf890051943e060fd8f357b23f9b7fbe484030e0a18ea53dba96a5ab83f0c4761b70fbb5147e1254bf61c804c7bc15fcfbe7c0c7abac274950c9217ded8d1f09e6c40e0910fd5d41ec8ccfb367effde77e45bd2b28b6918d4371840114c06bf33b302c8049123e953ea461537ed750eb9457ac14126a9d6814fa13c4427f2ff457273f38f72161fe3c860276ab84e58ea09ebb4b81451228660282dcc0cf97c3715cefa55e34160d35eda1651e2c5dee41df5e28312cdd1940f28bfc12bf1ea2d40b1479005a4c90f2630eec877391124e706f73d937ed855f355bd7fc3b1005564fa1cc3ad31bd13244a727d1b8d7e789e869dabbe9cba37f96f3b8bb01c86f7fd0a28d2df6cae65fee841c42cb0e3a63ffe6df8cddff84891b0af76c7e855c2f2a2320132a7340b6ec2580835c08b24cbc96f47ab44b10fffc020b811e82cd4b98d1f4583508f1162f68280d668188fad9399f44554a284b895b446d34046700fd7acc564932a4a2db2492af26a4b16037b6f12280f54811c94109c799564ccdb17a5d477a7528d22794bd31a10c21f45ca407ba00de8acb1b7b0a420c5a9da1c4b7f26bd4594dc421a4314fde56bf7a5d45f3d8719371a47f1b3f21a483650c567df12c2b78a90bcc263e3660ca58198fcc65326fb6a98d4f9f7f9974ddf89425d82db88f26d8427fc1fb86464b6b1fcd0f6246420cb152f774bcac9a9eae42ed12aa95e3bf68174048c743e35daf0895906f585cfa351df5924d6cd29282bf6f3fd5569b3a4b8a9d3e630888171f6ebbdc46e6d8590fc8e7e2b05024871ca24cbb335c20a4beda11a39628ef3965558c94d723597d22a0dd8e77d5278bb24f8a66f56ae8a834e8135bb8d31e4cb64a9594689fa6122be3aff6a56e83fb3bdca2e6c532b0c6963b129b336f1ed9d0c43f867f7c682409c970b33b3b1f9e8a160ecdb796b319eb32329c86f18827d7b4e3142d3c2504a4bc7e22e4512fb33a8613d39cd75d70326f155340957c059b3ee323d96e78901f02c874c6ff70b1ed9f92e439780e930c6323241de4cb092fc95d64b8d82484b53603dcc1d8a4e04a64d93c144ab7683341845024dcc83538996ef2af076d7c7fb15c661697d436694d7cfb4c40392a9622ba0efcf49afaa682f018f5afdc6818eed63b870ade3d3801587f319f1a7bc778aac4f08855de6e2eb4216b64b1338080d541c4e6b9493c121a40c3977b91f968d197501c3d4b897eb85ec998962ab02c009a6fa613154a8a3b9350673918c0fea1364e59767b20607d9e716e4f74fda84914a7e023481aa8f8cebfec2328f2cad198ddb8461262ce724a3d9851b61524d2d77a015facc1e66c5884af21480ba83828d3359692155165f9b1816d5aafb0ea5dd2e4923433a89eb39f07a9ff18e96ce36238453011709eac688791814e0a80938dc3af443ff8e5c8ab5cb73dbdc200781d7a069089235cd589fdedf839d6134e85268e523f718b8b842f387825ff5955457f0f8ff5aa7c50e88892dbe2e1ddef11bb79eea89d5af3d534dbd5242ac6877aa5d7b7e689b9a2226ae86c78792b4ed362de7a1e0674767734080f09f66edab87bb4a284a54ed7184acdd72875d0513df2ec5d607e751e4f7b4d6d0f951dc6beacd1ff28328ca33f38f5ca2fc07c0a77344a36c4a5ae9cece73f7f0c5d0912d40f27345c6dafb2c0f4ad062932a85422927db64b1c1af8604f95e42c1b8daad6a1dc66242aa195f978ef0e6e110caea9d107dbad89e4e75a76f9082d2bdd497f2d148d5898e6ac9dc7fb41cdd7df1ed1351dd3c8e05db4a3517b2d5ccd924db31a3317012493db9fa40866b29206a5e49d9ad99027a499bb1e58c287835c606e58e0922d59a7f8887e0a629286f391dc492968917a437cc45ce055f5dabd1e9a5bd9bf3bc01d08106b3195fb6a2827d292a114911b67390d375bb01bb8c9e9f8e0cc172777f3739f9fc28b0fe7054a02162a6bb00ce5992d79ea2c372e2d615a8061a37b5b3972390b071bd277dcd35f9756cc52847f72340fcc2c20be717d256e4f263607b5e471fce7233d99316f5ca5f21ed0f3a51517be4dd348b8fae5d22da8280d26a89dda9dd2fc44e1b16eb49a4e9dd0833a9d29c3f32368f856e7c2572b3760eb69b27481e74a01392804bef98821273ade2cdf370105268b82b68bb279889eb83367abb62edf7b7b2cb896970bef7b6131159c6a5b89b4a4e39d7f64113a9b71a84cf36010f0c5c46351acf7325015ac807098aa7f78086060f292cbb3ea31932b94238f23da00862847619f7533d42d53451d53cfa1d7a7c216bf0525b9423dce082235aa84eccd33df0b34b0f6a1326e0e4af1b5f69fd72d26b93b296d62adbcda39a103a63375b666ef20a5804247f1b0c0929c9bbe47f6da6e4fa3804125270e391a7f013c35d4e0ae964463c22585708c759ff7025953fdfa5252c855c4f627ac9c247894dba0e0b7c74ac1e15502d28ee5662c24bc3d4dd012a991be1a67ea471b805b44c8675a241628e8cc57880dc9ac30821f06458b873b526623fb5bfa965d6ecb6ae16192ff64495d577222aeeedb0a38c3290028c4f4f9cf91b49f1cbe140bb6d42a82e16f762daf0fa4becb7a00098e7a0de3d9673fadcca2bbf0eb2c009fd4334b97894abc63df91de9e3453e0f7f2ac69dd428e235e022d6dbf340e3533e94a40f507540e6e310edf01c91caad88098752eb15267ed468655dd6d34b6e26f30e56728938331f2a5ff2174e36cbcf4d449668dcbe4fd9d54fd2776f98df2b13fec9e11e255cb3866b223ed0e9e2be11d1f9cd5cd13c9d5f2680043e96e11801f0783838cb462374c88e2255815bccf9946d0028cdc5b4e3c52c17286ea230bbe894d089f1022bf7774926d22a208d110cf5adba993eb9afbe82170b25747db09acf738a963510e88c8b00f46af48f353838c66a362835d3457fa496c823060b4f6fe64f8d6c803c87f1e270be475c06d88e02af94491f6667d450a102f3aa4e1b047ab0893f2f52caaa711aaa8c91a98e82a9b531a1868cda627e9e0fca1724c73bc645efc23668fb2653036a151aa9f0371230a119fe0d9bbc6f61c9ccd0a3acc08d288e3eb1106a499b678dc3f4be7d60d286b37da86bf6d2af3f75fc2f2fd4fb3cd20154aba1300886144848a3ec3fec0f51d02fa1ff3e31750c59f07491125e1642bf318844f201ac22879a19b2ed538884fefca3f86491e16df958d3ef3f0fd00ed35fb0b7f75e22207bc7ed20bd0bb72a88b2424a2f8c5eb1dd2230ad2a5cef5b6a69a44c280b4c347b8f92811d20fe0a42ccc2e5a92ee00b0b5f10bbc49b0ba9f280dc5bdadd863c72e7ff85d887e0dab346f2dc4035f0b1b971289bcca5833cca5506680226216a6d63019cc4dd14c33cf8bf37b02115a6e620469dddfda2c119ee44329cb06641efbc14482cebdf22e5043391465b933b6118680a1a9784dd25e5033fce98fffa42129320eb5eceb75d62b483152736009b46902f1cb2533e69c260447900052b66dd102c1c397ca70402c7263c0e1c4597eeec864af4ad614c1f41b37436d83986e026a748ccf122e88c7353e76ec805061ca8624b781ee97fe7a0c40f1454c84ff893bb2bf363d0baf6d083bfcb30a007a65f19aad06a9b30751af8243173cd53dc6f9c2cc058ed29c33906e0cfa468496dba9be87dbfa56619ae24f48abaa3088a399d7d60a9bfcea6f7862c8f5a9d6d02c03f58a4b52f5355e34bb35a83c57657d9ca504a501ec036f393df41d68d9a33ad7acf2f40df285cd0094abf907e7ec6ae2b23cfaca5cb44322cfdff77d49c74fe75056fc164dcf01c1cb0b8dd97e0565f03e209c8639120dea542225ad43aba4b18dd9f929038af91c02a78b89b1000826987e837ec1ba6256619deade8b5fbb1da2baa78eb8fdcb43414e03f6e6e191f00b6172fce9bb64815f3c65115b5faec49dd49e07e960fdce6b9448fd7b294bd4e7eb1cdb841c82e493e8a81f57fc3e592b30b2fa8b0df3030f9cc7348f594c2de094764f52972a4ad672ba8e9ffef398ebf6917591cb020a62971e368c04490c894b78e7f24ec71fd43f4042386bf1f34266d8bac678923948937d947ad111f10bd5f009fe6055d7c16ebf50bfed207e8837c9d114eb0c50e09699cd4779d24f5564e92d2fe254db625191266b052affde1ab9404eef817d345e25277ec830753c720425fb54ff45eb56ddb3bcbb6635d0c128d4b3c8690c917f41dc1ddc6f21a862bcf186b7ccf543ab5a92d2978506d8c40909a6a2cccf2ac65508df4f86e3a3340007fa1217193e26f7b84b3cb14dce83c13b53c707df793560176242069cbbe01743dcbcf9001c4a67bd6b50e2f14a703b910857159e1689ed0d4dedb73efcc7c47aa01f710a1e5c4772239ff9bd255c5fd1c1a15d05bc8c935f2f0facad29acc00c3ef961aebc9e06a2a657b4fdd436bfffb26b0783bf5c1948f678de06df0bab717e9361e562b092ef2d5431f6e88f983c89b0e0e836fe1a3ba1ed8b8b497632bd49b5cbe26b4a3b9a9d429d379cc8a31add1d24d901fecbc0b4e77afcdd0d344aeaeff0384356af7e84c91f0f0d70b43d38d8059de88f3e5feac81f854b0f25d469c11a7df71e26071b464771494114c6ac2f1d82054a45b3386847fc0da9bb8d81235b22d3ec1203efac68d76326b91fc45bdf589fa4bf4eb88e828060ef9dd40883bbf7e5b6e634854db2a7498a55bd81dd381a1669a95b6b96ffa2977396e7aa12624cc142927e1001a7b98478528071b5cf362aac608cd6ce7155cd0d5ab48775c1eb7bcbd9d7749332edb3c72214d2cce20ab4225bd2c9f72b383fa91d3f2af394b5e7f10b89ce327f66e005ba3d1d0576e288122a14f0eb59b524afaf39738afaec936b93fedd863c338fc3d0a5aa31bf0933c87972b701111383ab16f95b9550a38bdd998952b0b00a64cc0518d6030b7a94c572b50e22085f9d4c998e3270f2d4d31d1474186272cef3167e8dde81e34f70423f6dc0d1d1e5d9b81af972f01e1fc4d42a3791b297409975e05351a9a897c61bfbcc26654176df2785bfd8ab79f58c64e4ee1c4978f82acc228ec4c14252994b35035dfd93eee97480aaf1ef9f8e66772a6f4eed5af2a92232b6da04b040a93e90ba8d1486d76ff43500b3fa2a6f67cdae7fb0228b978f84c3d1d6380cf7d840cb8cde0e7af9b417780efa63d00496fe837f4920a30355e62f52434520636034a4ff081ee6aa538081f2c1860a611d43d0c06349cf08aaedf2d81a43c3945ecbf003468343a91b40bd4a6c44fca95a66ea253c5e0ae2bcf187c2ac5f5d650f9a8c64bbd0c77c2e915b4f1dc4711afb9c69bc63757bd1e3958196099cc355887178d10fbda9ec7f02be66c5434f01e5f6bbd1cd786767ceb5053d83c6861bcae6274f3ca1d68a4c2af466193d97a5d5f3c85ebcc12125ff041b699a856c715adb512fff7e06b79433282a623aafa9325c9093490f8d5287882311df180a50194e30b6f2031f7153eb3065346e1db19cb70345021e415cb60a48bf44cc68d00d6afd765987395d83571ae4bbb88f701bce2380515ca181c5f8b166145067a6a30957a3c6fe063928f13ef6e2b2c485bd910fad1bb915bea8be7227d69d28d57298618d98680c5225d6fcb5360f8ec376f67688e79eb9ebb2fe1d413ee7270a3408fde6b472f04302d68703c26f7e15f42693e2684733d3af957e94f71cf91eeac90a9ed44f7dde2eb16249b5e0f6fe0087c9aadbbf627e835f5b87d4aeafa72e68851749a7feca03bc1d6dbfe1e7eec1b1b13deaceb1f643348cca099326e7d919625fa4892a01ce270599247b1398c7cdf2669ea3516fe8cefe9545f1a33ae833cc3f4ec256db50c45a45f50660d9725d195529e21a55a6f061f49a9eb2e35740099a1df75757a312100fd69bffe3a0082af1fa271e157ae822dad3a51983632369d63ffd7472d754edd74f5c2f4c5062e8c992183450334baf131cd928f3a247e70c6399eeb823a3813d424faf67523af29a764f60a59ae1bc522a34447ca71d1392522a6cf36ffbdb90f56a3b4051cf1d5c7f3992cbdd2bdbea7a8661954e72a8ffb6648d482a18b5824dbdb359decb58df5c579e5a0d03f73e4166365c7821d721d12b27f6f80929140d8c3fabccb0fd77de5d9d6b4cfffc000df19e76b168c184765abb48847464076dc8c4d52a19063830a0b27e27cf2d630c0ee194e7c17e6c0f0d555aa9cbe49096be8f141e239b706debd0033a265e9d8710e456538948d09956e6dc1a472bedb8dca15ab61e66335118250cec5796ad93ea5c7b32ab3ca403d67d25f49d366469ed328879296e9fe1a411207b2d160ef47218ff81ad554dcf03ca900e049ebd297c1479264484461931c0c8fef1e2cb9674bcbd2593a6b5c646a84f43f897286e250139550d839b4367d31ef1e7332dc1813070290d7544f5341852e0a6a87141746fe8730859f373d64143993a1d33a79ecdfaa7c1a94610fa5a968cde73758298154697333eec86a1cec9e626fcc14ebfcfe8a2457737d623ce4b89389a21d6dde26537e3285791fba518ab54ab45d5376500b32d6cb7f089846ae23719c59f89b7f92c747671ddf2d57215c9055de4cb4eef0811290be08a439e17c51b4059fda18d626256c39cce2e2d65677fb3fe33cfc229b44a39617d2e992c3098e65074f24458c2cc73f4ca4a45609033dafdea263f84cd323b6fc988b0418ab3ccefa4cc9d7f2c7bc117935e4585d34365c45afd247084372c28598f0a3df8437bab18a88dba94a7c5198791a9f040b2fdd2dd5558ee31a45c2373033683a0e2a27b53dcef8958f089a1c40794b423bff96b0d7a68001ba412d2d0681a744509e0bd851f7777a0fc59aedb6890b84ec02f2e3db040cbcb108ee7dfb74b0fbbf6831c436a8385b051b5e5bb6f0b27fbc85a0b56b610f8f23a23331236126f280080e3c9fcd5979708d31c5b47d0e0e9efd1abc2681a718c0fadd765252485b4773748078251014dcc2565cfbd70e1dd8261b9b70eb51b1a0d283b0d6153243573b974044c97d7172f4cd7b3f982ac560795e5a77262e39d937132e7241fc7e12ff4dc9f789cf746d6ff22dd0808cb6bfddadd63f78f0f667b9006129cd185d0cb7682cc6277ce51c29d3f8775eb0d77e36d5f002edd352ef0a031772ea4d12d8c6a1b6afe29fd177f4dea51c72fb2636effe37625cf2e5283cebfc0320efef9ff038735bc2beb5cf9a6a684ba557c3b7e23bd07735f32a6a25082fb4dc81cc1b13987e904ec397bdfd86e625c1824ac655c8f4ed6b83f58c26ad006e99bc48e92754fef5f682e40d107774ecd36e2ded20f307e4a7e7731608c0c0ee0f2ac412b673af6512a19bdbace86545f297ac37e813847ce64ddb5207b5517cf0714bda73f9ccdf6089935c811b779e29547786e6d7a0c0f036f90d9c6add9b77ea2fd72df72dbb9aeeb0731c1feed348f0e17122e8e69b3d7e0b9bc9f2aeda3b4684f4c49f5a5eefd60a98d3ee0976fa669ba27e711c3b1aaa08331f8bcded1d9fb6c51b3b3ccfe5b666dbf1d70f5ee94ded4e227e2ab3ad333e63e557f757dabd589e8b0c6d25de2e11bb669a992a47066303fb2ed5ddf8c7162458087633fb20dde4ee9702770398e95e6f080a1b485794c83cdabb2e84f873e4adb69a6eab334c318c46a6fd3352f4d416c569aada566907c73a8b847ab50a45d2117ce28c82b923c429105585ab22961c6dea61dbc22af5c40fb1f0d64b159d79c95b7ea477b2e07bb65636d3efb1cf4b3771df7be3b8521273650663dd602760d400c87ed3cbfbde5f5f76f2f908103790b8fd14207d4701250dca78dfab091a74623735b70b95e3f696f5d9c7563b015c6de19c29df80744a8fb3d00aa80e5b039fb4daca60b75e446fe78381876231cb720af3b5d07c5379dc5deb790b1899bb3a98b5cee51e1a98ba38ca58eaf814b85742d8bdce0b6c72cb9aa5c7d6fabf30641b8b201ca7433f0758cfd170cf826edb4c616b96efdaf9cc9725a4732e020f3cba07b5b42ecf02333f9c5fe9ff50b612cf17b69985fb25056f8536bfa227f2a2b49df355b2661b874e954f32c669275fd74c98dee866c73f29c87b5f3c2aa2b7794b9c9b3ff3e93f30dae707d95d7f4089ee26b8a3e589bcd1d3848f70130c4cf0ca7e2672c2fc84e5bb35c1d9bc3e91393deb6385f78297edf3faf7cded8f397fc2dfe91b4ffeb19cc8bcbfe74b8c36adf6b3c2a85ad256fb7e3f30eab74ab2ade91d81b3c22df08149fc5e5cd96983e63c7e687102907ad4601aaf7982a7e7a8f5d738bf4c5da71b7c12e31f0e575d6cfcea10bb87a19d8018da61a7830541b7f616b4e7bef2e1c81b26a6fc0b7a1054eb1abd2bfd0672457190ff740ff9ea4406fd6738fe259a3cecb4cd2fdc818167d2ebf196d2c0347a7dd8b2cdd0ac7335b35e0cda4859c0424a7ac687992ed387df14f9d4c0ba9f281db5d3b422b6c613645e76f97b974d2fb9a284d4be6c94fa63d0c8dde717d5041964bb7c0f268b17403a4ef206027bf6f2c7d009c76be27c7a0fb7eda3c05d8bc4eb42567a31c17f40586bedc90b8b71d0bcec7d0af90a3b1e19355bb42da50ce9d0a9cb25bee8d36cbae95f4ecce5810cf35177631e33f3d99335f739e8ddb30c272bf01e4e3de0e93ed0287b0976e6c49913cf9c3761fac4a973a7ce9dba55a67622aa57ac59b7703c1df119229eb69ff13dff179f48d3d013df82fa88ff981f44f27aa2d38d8e272547c6c8c853be48cf2573fb78d7e3d0c9fdb5c2fd9fbccccf985bf3ca447d116fc44cd99dfc7231cfb6cddafe6ce0773696cd212477ff0cd9abd494c7739574bb9db15a1e45ffe8c9f6335e8befc7f60607f812e109c4fe722f2f05d2446c7f50e9f53b32bdc79f05fd671fd6fbea3a9b733fd56451f8673f3826cf6b7687458bdee3276e8f8c557d6e8f983db5ceb161c5775b54f95b2ff1bf3ca1b35d0df7f7905f5e60ee624a7d17f5aa6da4defd56ff2dd9b5729a269678ce88993555b382f6401c76f46c3bb2fe36e073f29615291c9b3490e8ab18e7255c6c60e66a48407018193f81f18f8bbd49789a1e7b1eedcdf8f54f0a56bf2bc7fc4e0fb65eeced8bf2b0b29c5fe2549b415a1feb342950a847cffec9dd70a28007065f0f9f22ba189174c5d712b4c3cfa35760f81296453e70cb12ab75af7daddbbee0f82ce130f6b290ff9561aace7fedc013f854ae55bd3b92ff2ab44d993c54f5945833130bd39fde2af9364da7e5483f77f4449ae05f705ce3bd8397fa0614f78e777865d4cc065e071431d30cedc7e4450a0a5f0e6fb62c1e1c15e16b6ad789e7e34cbeea0972657cd23b1b117d17eb5e09a823d7021ea3d0df335b1f557e517dbcb083a712d5d91a231faa42ea287d194f451565fe322bb880d738fe2a35c18439ca1ccff5b2a5c2df7cf5006aec8685553326dd035c15bc3188dd519aff783e8fc88c50c42f79be4f91c22f219594e01a4bdb33d9f59ab5cb1c3edce8dca388fb67a4f541d67ab688acf7324cd01ed7dfb883db5890f26ce4796ae246ec8f9b32e0d03926fa9871b5b4ee1e5f9a1669b3b551732324a323726106750c8f5b6f137ccaf654afe906f246d3733989e7f76f3f38bb5db8e0ada73edbdcd2102fab8c6af3fd662ae08940df371d1e41db039cdd0dfab7f178bf7c813b7785f7c25fad353808756eb25ba89dd2f22aff74d62d2e76b6363303988f098dd99bc1e2c164262c5671b4a5e9a2ce0644fab1612f49dae6b7ffe5b43cae7e703d8cb667452cd379551c6d69081d3ac5c07f3cf361c2e6df9bd63775da2fbdc2449f6b27d7b535d3e4635cbb22cde263cebb0ea3b56fd146f96adecb3ef0da241ca735e4086fd0203c909eee011d3993ed9af56af49f6eb24b4a6cd970cb462dda376cdab069dbb4fee1b740298e17fb4e8cd457c66879030cd159398669cd8f83b895d21cfb9ce1d95f9b1fd5073085d10e6106da7f29fad93dc92874e3e7eebac4ac8595ae4a38b8b62cddfcf3250f7b22f4adb6a6ae6ce625f0fdbfabf970054e8370d7c4213bf1fdbd77cfd4015d49f3d08bfaed3bcbedcfe335c59e45b75fc7cd507fd0cb4ffe0db10ada45b7feecb3239cb9b2bdb927cf5cd06ae4e3a898f4efa7f458e949f3f242b887ec1a92cbc45b2dc6b677f23d6cdbe929dc350c8ee33ef4536139b8e1c1f228fc5bebb6db819d1e2f0e1120fa87eb7601ed4fef715e7a86b65ec60b09ec593cfbcc363ff73791fa8b5fc569e21eb9487d63313b8579f27a23e20e4a42f7c33e4f3c0c50770f47b2dad13916f03a5f1be51cc0ed8d185d1462ac759fd6d28679678487fa172ce58d8ef28d794473ee246ff0d04e5d999ca52d04770a8f3f8fb33f80e64cf9547d9c04b4779a6038a607d9cb10e978a269c652c160ed356d0b86711e36375669f3ea82b468fbeedf9db7c38c7b5f296edd947343c73fa138fef4ef48e227f0d6e55909bf3b1d16d9a00f679de161e8a16fe4cdd47433bfcd7ea31326776e2cf09f31bbd8017a66dcc271b729e55657f7f08f3e491ba8738157f6bd864f0577c60684bbe6cd3f1dfc2bfb543ae8e308f2c992e3189d1be275516a3746f646f47f7ccac3cb673707530fed1991e0688b4bcbb806cf1357babe8e131ff6812ce439f699a87909be67156b3065609248f644415b98fd2ab07532a7c02ec23fec9a57fa297e88b8b310d439188f9c7f82f5953cc7c1ee874196e80eab5c4b3da3fe1b1268e1d6b87c64cbdd7544d7eb7682fd3ba4b3e6c2930610fcc28fa59ee9ddf51e709c8a0d0e376da4f3d74f864bead734cd9093de3343838e622bc3b95a1c589eb1ef2c3b8c6f2863c731059e2fe56999c8509e73c0c2968df5110566e29ff08d1dba8be9037137c8769e0d7fb685da8e12ab8f314b41ee0da682155df356703bc682716f41f7bb20a174129dff3188efbd6c77dd21123fa3a4ef7068cfc9ee9e7724cf922061c6983907874e970d60fa42871bd7c78b51f78ee2829f0ec484234fcd75ab65c9295f40f20654f20704d66745f78054ed07755f3cfec690ff1c5d1eed349ebfbf249c7d0ce4dc354de8f5c1dd92fb282a7d9c2d0e7564187997d04de89f6f47f19e06ba0aff15ee2cfc5bd0bb90fe021c067d0c7919f633280fd6e6b0972793395bf903af4ec31269f968c885d5717a5cbf3ac141ecb655eef377f2348fbbfb40f8ea0b520dd223b4e38cb87594621eadcda8a63b74af2888354a8bd08e33e2b6a394f2603da31aefd839f635e46029778707e061655aa95dae9c4bb514345088f001860c1b3460d0b035389617e87526d679444cba4b318b466657d315ea942037799c5ab7a8ef46f6ac17a17de690c86a284f3de91d3760a339dc900e06fb359ad7338d5cacf84f4c6df061ca1ffdd466421f9c2df6540b180cdc7ad635539ccdca0ca0f22a9bd1b20d5994b6565f25f47b4f05d02b2a9bc518994d6765a12c348f2477132a79ae9f79ab1efa0096960d807d89122fae382c62687f8b0ea9c9826ebdd1ef9f569870a0a30f361a559ee0faf9ec112fe58d869ad19a5bce9b2f9291b59e28c2ad16634b9b80d8c8dccdd99c751ef7c8d8ccc549c6afaece59fd5edf56b2519f7cee41b14393568649ccfb53726f92e6aac1ea782de58563f77f46aefab062b3aa8b564f50791042ba88c47672f3ef2069d4166a89db7aa3a9a12938d556bea60132fb0f64fc0d9a7b1b8f770bc94902e3ad7efd713e57be24d9c7ffd7ec3ae94768a9efd8633c301fee14327aea10877d8f437de36625dada0db4c369cca3c7f4f772149f9eefd1d149ab1d23cca3ece25b4ed2880f02eb5b21069f320e3dff32ff0dc335e2fd6ad7ddbee0c41fe07da436632277c794f0c0f0d4d914bd6ed40d74e774ff080bfb73b8631f67081d9c53299bd3cdf475e0a6e738e6bda684d1a2f67c1787b1a3327233e3491e9d1ad513da067834cb6d4d01f96dc4601fa9f792cd0b37e7b9e60da4ba67fcbd798307407ece950e5ac262c734f60671b898a749bd068edefa26e8a984bfd4b770e82aad1c16bc3a125f3d86948c9ea236e832be5dbdaa70f4d3f914fef773b13fb2ff5d184795d591dd9e1c4e207af7a8add6544341fad4300a5f0d11e962c38fff61c36fe7b2e1dfd1e5dc9f750364f3e6486de178977313564e4f78e1eb45030d091dbb99b81fbd89de11c0f31ef02fe6067d9653566f7fe1ad97f16b7e17a72121f4532b8bcd5e9eedbb4fc6f31e34aee558c5b16fdd343f980bebda0096d20530fe36927ecd39d77436e8253a8d4199905b32876f2b5a929b9ba0d7732b74d2c791271eb47e8bded1d5f83617fe61f95ff8a0ee36176462614c6776c20f2fa4f2d8749c5d4586b076fc2680d6e4c1af3d19253349e36ce356685217eab41a16de49cd0152e305a45d696d77f1cc61a368da0de2728dd156ad6d6ad9f1a7705b9b4287c749a241c9c44cbe775694f3ccd2ae3a29ff75ed0dd67c966d94b96d28a3dba356f47c03e9a8973eb1996f64643858e96c5ac2bd363baccd480602487ab5c9a57bef8c9cd9cc1d79d2954726685237ed9175df532c22e37be86d3dd2d3dee14f6799fd4a7227b236079a33f1652293b88df19cd9eb9690bef170378ce69ae97232d46e3bb2744b0c2c522f5825b7a0eb3f560a19adf7f1a7d33655ca9a3b7a20962bccf9a245e6442fb7d74c7a75c6e5b06cdb4af2b1f7618c99e8576d86eeb3af8cd36cd1129a1d887aa0235bcab2a3c67067794e405a87925065b8172cf5a56c99a9b98f4ef4b487b22807cd8b423e9b0adcba3971b51206abc927ae490ddc2ceded86f3ff4bcab34f143096d02d2a024fb72fdefcb76811c7afd45d58fe89f3223737a5732f023fdc9dde5a2a75706465465813da78736dca7a68ed17d3d00ece005d92ce0139c28c42e7ce0e053dba4821ee6d93e3d8be2adbe71c99c5ab7d9955473d3ba8d9818557c66caaf51db1fef99a75993d73a054596bc414ee762a19bbc69aa64aefc309d6bab9a33326ec24c9d21d9d84991ff0b89524579a8c5eba4589bb5e5f6a9c6afc93d277527642192acdbc888942d3254e6929039350ac2f318d1f5aab8d0dc524317823b3c8c68bc848ba6f7a96893bd91a3e779496887b5c40f2defa7031e5169874597bb91cfa8df21f046443fecc2b43ae6d60288a19cbb868a9cc19aad6aa8536c57d8bab54f73f94a5d90145b1063b3f97fec19d65a969465cf6aa5d14c73a9f16ab672916e1cb8b98a2d4a61fe14cbbedc740ff5ddd182e799f448bcdab964e447ba7c04d52dcba368d2232c2d2225c56f5ade410333cf2511bcf95c6f39c98f6709752df7831df5c0518ee336257fa638cc94a7261916da3463734cda09491462a7ad7f69d312d370ae8ff072e876875105c296606855bc6261f7a430680ac7739409ae75d937f457f40bc09d9b053133aa3b7474f6d2645e8e5c546fc83feb91faa8fefef920b0c820f5df018ea6d92d25271a563bcdd81d000ee9b024459f64d853536c09906e7fc76a3e5ad4c4fe9ecad20fb7461643ae95a03c284662dd53544ca9abd9b7aaf9f58348d8994e23186c94d6c4433694b2511a2512a57d2168b2b5d8b067f77fa4c14f1ab83757dc523a198cc99f0c4fd816b2d1ef78762fc92882dcd1bc76f253fcfa07a61a612c9d07b9da12c3f159e99ccc2a1a7d7db24091d4dd829390140fac86ed9345314e6ec789a64df91de8bae3b92e0eb094ceb69bd6cb1f3ba3b06b27922838d62fc4b2c4cc9a155eb27c417511b05c9aaaf91919f91d8893162b3373e4c62c122f14261af6d4a325d86fae1c9fe05e53c32d1f9cecec798a37e28988a53175b831a81d41761b60cbc96deda0e509d45777c3ff3070083cf9f667739b8a2763abaa59120f8487f4ee54e2e397e3a057801d7ce87489bcbbee3c64f8fe6999e56b665629dcdfa4fb211fb4ecc745e6b454b5173fbdae5e86e652f1edeac05ea3c92537f568624f1d0feae19ccc60200e04b20d9a80ccd9a9d4c4f30d52fe0dc43ee500cf3b09c765d4b78de4fcb4f0c47f9d2f740e449e0d558e75c46b4593cee428835c0651ce4518df24193d4f4e3d485e8079a5a5b95421fb0fdc2ea9cb689cccb7674978ddccd83135dba9c71e610355d2b701e5392b2ee67929fbe90e71787258bbaea596f2e32d23933b756fc101e4aecd7137af1d7e6243d406ebd5324462d40129a9b80becac4d9c375f130aa665d8e4808be7a5f6e8b55a3ee8cfad26b7696b6685d1a64d82d9f44a500ff4b8b2326badd96e104c8b0fc4de6b6f6a72b94310e1dbc5d77f98fa44784ec2250253e17bf5b5d559b0ca9a06dc31f50969764ae97825880d595677858bfcffd7387bf62eef8ef843e3ccfdbfb8f0d93f19085873805b481f3751ccc755c19d14a29f793977562a1fef4f3fb6434926c2390038cb62b30ba6a2324122366519297bbe07b75ec0f99ba2473dc8e7b1cc83f4d2de58154f84fa5bbbb102671e631f214b605e4f3020ea2841c0c3c60b46439330f0f0f0f0f0f0f0f8b036e24596413d22ad94d2629c970b335dcb65369c4cd24534a29a5247607efc2eee01dbc8377f08a209becbdefdf3f0c010cb60be711a9b5e517cc276a8557ece46b2af582c94b25a17290bf0b26155f2da71551a74c870b86f124f673be249e62be05d397db87935366976d2d18b353ee967f993a979805e3df2bad7527cba2f6c28259bade8688ac6d71cb154c5ba6da673f0aa52e6405a33ce5eafce4cc6e3c5530b8db79fd0a7d2ec3a48261a4860cf95abf821a794cc13052a687efa465a689a5600a42d75b5cae3826f62898eef5ddf694502533858229a9ced676e923f27a8229d72b573baf35373f24d709070f279873d4499a8e5814192f748247134cb6b2d24b9cc9d1156582f9821491a69349957fd2259852963a05f76e5309c6ef509dcc95a9602ef48147128ca22d6d65f6f9040f249872abf152622c2821a48e60d2f2df565dfda5eda487118c755a54d645a55bbaf62882f9eccc638ae7a0a7c41e44307bf42ca4922144baa80cc1a473d433a48a3aebd196f4c88896427808c1bc6e41568a7d10ccf69ea2b9f6179add7900c1d85a43b9ce9f4c9abb3c7e60707d957715ea936c95870fccd2e3a2493bad1ff7cd2069a4e1d103b3ecbd0aada3b00b5aeec10363b87c9bb455ff5099c70eccbde5a273a7ef26521d982c9b86659da32d3bca7ee09103f3ad7cdc92cafb7b4f0f1c986b47aea7289f1b984c69a83d39ad83ce52363086d6ce0731dfd2c3af81290a5de16d4777060f1a982b990e25c5f4eb67cf0ccc6c2957e8fd2a038349a54e4356aa60f93682470cccd9d392fcba510b6a1190e8ddb871705cc0051e30300a0ff15e734267d9b2c70b8cf1ab525d07153b6b210f1798b55b141e46b9e58f751b3c5a60564aa8399523f36081495b4646a494f244655678acc01c3652e4c34e54334e8a023c5460d6c29490a127f79d5ba930aed4496d954e273ac90c5418572b5151bed6a62c770af39bd653f3d11095f31da630fab7281b3b3331657794c2a42aaaa82a29f59c528e490e1b0fb8d1410aa3c8b938327a334e748cc2a0dce5e870d2a59fced0091da230bac9a6ae450b32f2224304c78c93c59f9ce4f89313244547284cfa4ea950556bde31280c42691342a8a936f1c925747c42157755181b4f7bc2f41adfe1f9b459ae64278cf28354faae79b1834a72e3eea48313c6f9915df53f6fc26ce1c7a2de11b929571d9a3028f3cba7464de851c14c9893f2f8bc26674ecd0913e6189dcf7d74f6b88a5fc2a895847aa79dbc00c1af0244b6b02201d96109e36db7d8d5eae91ae238376a98a4fe3483060512a095302511963f29113a77669430875a95bbaecd8267a592be1b3552e2c0822761f63c26f7a5676562250983a5cfe1225554f2cf48464678d01109f3c9ae15fa3aeafc9acb043a2061924a3fef7fd63fc2785e59dc9b7ca94d2839c21ce54917d3d9a216b5f9a0a31166d3726496ade843e860847163565b16d7734a7e42e3497098f45ec7228c264c46c7e71ea9d456d287c384a46f0b8ecf3143657428c2ac832c792df5b2bf1a25a218df4823c2e4bd2ac44b57897d9fc3c649c702056f9c1a7908a316a9f315d745e52a49eed8c081c5dff8620b1198e0c061a20006c01f3a0c61baecfaad72cfbf78a18e4218bf452953ad6f51c52684316ca4ef892d0d25bc931a690483306ca7a8defff00e211484499dd69ff1f23a9be94bfa484c72d8161d8130a85ce679d9363fa80c011d80306f99ab7e7edd4bb3fa83f1d5cc09adecdca316ea03bc28a14003d80f667bb114b4ea85116d25c064d978c0c9c971d4c951eeeeeeeebc34e7151a2011c0133afa6056ae2a6b28bbf041cbea021d7c309d8a5f3321db3d68a93d98d2a9c8987d4efa8457d27b397098dc18c15bd1a107d30aad742e579deff5dc9107834e42fade9f18cfa6462634b820490874e0c1f062b24bbcf8ec204a9206dca0e30ee6bdecb2294abe5bf0edaed00089093aec60b4a483ecfef78debc9840676914cace822999c9c2b344022828e3a986db5ec78a76f5abea783e994e5952b4c2cab94e21c8c5abaaf95b93861af2f699683b9557bfa96afe48e854746ec74d0110763be4aebdd1efa1d5ab8703005d7a77ee282d29655de6058addec2f6c2f977b2041d6e30ebcb5c2547deff5e8ac6c1d106a3f9e894f561e4cd8d3ad860d4f2514635557d3ee91acc3116f684ca42865a4e49a7a0430dc6f8cae29ecafb3ddd92bec542471a8c2955d4aea1ab957794e344afd08106c39d4b1d74dfdbc9703bce608ab7162b6f9810a2c20e3318dffe94cdeb747172ec59a0a30c26214fff84caf25955470683ca1f5dc839e9adf753d2e7c50949768cc12c6d45c79099154dec2c3ac4609413a65cc58c23c78d8e30982bbeac57d915a46fc5111d60300b8ba2e3ad48cfb24dbbe8f882d995e7d7494a0bea73f010f534d0e105a3adcb8aba4a27253afb27dad105f3bcc751e9937cade22824e8e08251e924e3c2e8b0132e43945e58a1630b263fa13b89d616796154d297a7a9430be6babca7bfea727736b01819b16142858e2c98c467c92d3d3ba3eac182d9e5a8242f7dd2422b47848e2bfcffa71694593aac605a17aef55392de328d53e3e060c095a1a30ae6d75b97a488f3a49c417272f2890a4619ddd51ba232d49d5a818e2918f566bcb2ae731d6d96f4dd69d1210593cebf341d73c9fea54047144cda83b61c75aca8df5fff6b64646484e4040aa6242efabeccc495e9559f60d61815d54a27e9abb5994e30d9aca99696f2d2e86047134c51448dfa14cc2c7f8e09e6e4aef5ecbefa25b1e61a3a9660d2d17a5b285579bd6549e3e5f9073a94601639f55245f582c60d1c5e9c90281b5eacc7a187818e2418d34ee46a21df2c6bdf8b363a90605233e579a23fbdcbb4a42f470d1c58d8781c270b921a69042c878e23183f78a715a52bec5436be140d0132a0c308069b137add2b9636d1318e1c373a8a600ef3fc38beca6cee238239fe270f1d2a423cc5390493dc76153d28132ade47219855c79cb29c933a8260542a677f6e3be5df4240307efeef574aa9e41efa8e1f98e3c8553e42def8bc4e870f0cff42c8d0c2c7b2574a491f89892a1ce8e881f1f5e5fd53572bc5b73a78604c9d841ef1a6337d2ed4736163c6e20e0c17b4cfeaa45fefc3a84307c64fb18465a166eacdcc81f147997af8aa16db5f070ecc292a3b15efd3a23abba42f045d90acf33448d4021158e7ad902192c30b098c8ce0f82e709c1aaf4cae1819796562030370878e1b18c4c9d54137742a2d8f0d2e669cbca0c30626e51d3e58d0a59e4d1c19b91f74d4c0d81d35fffe835ef5621ae7739cec896cd10013195cd898e1f8cac4867e070d8ca92dba8a0be3eaccb166949c90d83099b1d8310353961d2be6ebdc5298b4430626574fe929aae5d022150373bbae53d2a2d659346d134374c0c028365cb4d5475d2e53257dbb828e17185f48372d3fdc2913a792be1c336acca08123c7898d198999261d2e305f586d32948ab72afa46e868815985fef55013cba1f5383e87175a88c810b1e204c78760c602161638be24c70c563174b0c0f8e53bbf6a313b95540e111922225b582172c549023095aa44c70a0c7bda4e8b1a1dc5aa53870a4ca67d173b4ad395334a853956ee0edad5b66b6f5161caa12f5cd651a99c423f85b94ca71e8f59a7c46e728324c749394c6c1c8a6cd1801be71d202243e449308302278bc65385290c32f4c5128fd11b6f6415a530ad694a0ff33522457232c34ffc115590c25cca2c5afcacb592da3061f55cd838255850c0640b131c38ce152323364c6ad430492427558cc2fc36db79447b96228bc2a89397a9cbd794e25aa130ad4eb6b1ad464b295450184ea80b4284cadf513d9f30ad6a099de2a97e3eea09a38ed3d2c45ece4a7fb0130629434c77e4a28a25e68441dd2bcf22f46751dd69d038242529c74123238b882a36615057adba29b7f28585015568c29cbfe4cb9ad4f19c5a15993059965a895613b19863c2ecf16a51e5be945ef5258c2a5c5eddde9676334eec401596308cdeee278fa55045250cd2f35dccf7153aa7b2a48f29612e694adde794c47727d915aa9884410a55391129a45a9957210983107316a5eaa49256ea8c8ce865000e5145248c1f376a94ea5c22643a038da00a48984ceb84e5a4c509b5aa47986545876ad5b0a42f4fb95c04aa7084599e88accfa95f95fed89871723c0e0c54d108d3cab4972e9f3d33b42a1861562adea212f922ef4b3aa86211a6d515ad4eca9129d58585c8165688e43031d182ab5084f1cc72f672215ba2748930aba8173d4733f5d3458459bc4ceda9cf93adfd210c2e773bc84e51c5c7cc10060f2fc654f4b4f37faa2884517baa453152a5dfb6364c48ec8a9b2a08617629d2bb4c8eb66fe920ccfa65ff8bfd8230767239df279e5dda2510e638a95e0abd1555b7632309d9a24d5c0208128c97f3aa5acf954f8714821cc1205bfbe3d3a5e58e006204d305e1b99229b33c17562b82e9c755846fafa9930b110cfab12f4ae17ef3b9dd00640866114a0965da3d6b6d219834ee4babce7ecac91204c32af962f6a7ae6d424030c90fb92585769ef9b4a80f407e6050dbb1a9ffab773bb301880f0c5a48bdfbdab29bec9c1f80f4c0302b5369cfba5ce854391540786054f7b1f2c688d888d20e8ca6dce58d7f166762b702880ecc1d5f6f34b6c402480e8cd23f5816cab3bf833e89008203b34ada2cdb7f52c13be70626a5ecf2e90c7d424f2d02880dcc2fc2f76d4bdf493162442780d4c020e7857e76ad742eca6fd438394ebec68c1302080d8cab4e866efa7e874e6e184066603e55ed526ebd568c2e03b3d0e231ab4ce81d211303480c4cf1546ae1af4d69955fd601080c8cf7972b9afe3b31957f81d1e26d78abd1a182bb031017985f74d393129e73579ee45000d202734575714e25a53f68e1165688a8018405060f72ff4e5bd83addda14405660d67697da4548e9f15d0e405460ce7fe1cd538ea142fb51f84885f1b427fd9539131b5a5498d3fe64ea4a42ab522aa730870f5acc6a791435a9620ad398bc655d27c5c505abf0510a53ac20669530e525949814664b5be31e4a2bc52d0b9bc2c7284ce2e7f5996b2b230b1fa2308e4e69a5785928cc4a077f25fed427f320509894c90dd5d174c647a54f98c7435caf5e4eab6247307c78c2e029ea0f235a76c228b255ca9f7a5db81c4e98ec5f978e8fd838d1da84e9d5a8b113f23aac14e262c609c995e143136633d1d2d6c2ca5eb965c2b842ad788eeae5abef31618c17426b88370f17fa91914b18352c278dd3df59d29368098392972a972c6172ff8a6c6185c8c8c847250cb633aa2c9c76e17d213995c107254cd9b5d84ea6bf43786912066f537bba535239179284297b6b559e75d87ea913912182e367e4b5e12312c66db1551ef2734ba5b938392860c364860f3e2061aed75a1d649b8591e9238c3fa693d7df99504d86880c768449591297cf65e6a78a8d3087cae9adaa839d698f11a6d723c3b28d6d29911de16311e6539ab11ddd83cc17528451a97eb1f2737b6aa824c23c7e2695d0d0f89223224cde991ff651a5ac750ec9f51b3e0e61b27cab4b5c12d3163d8630c95cd0f64994be284334283032b226344e8a0b3e0a61d0f9fa44c4a71c634c84309ee9aefd55ab024464882440240b2b44d88b8f4118c34e29aff53c32845210e617723da6c77931e705c29c1d2a8abd6535ad5c4018e33feb2ba9a62fe9fc1fcc5b42bd4aabdf4c29ad1f0cdae6e5315bab145cd507e378d65f91f18be1ca0f7cf0c130a273a450fb56775a275a8c8ce4b0f135be3837481885f0b107c3760b2d173deb984ad683d9b3f8b2adbb245cb43c18e577941ec5d3af765ccc0b1f7828fdf76aa91e5e2323232324c71f7730cae9e7eced0f774a680b2b98f06107b3d21d3379ba7f3ef53a98d743659db6ecd257490793744b2fb58abdbd2edaa1163ee660d4902b7264d8ebf8580d3ee460d09f3aa5d54f4abac9e2600aab3d69279d72b3da427243f88083514fc8fcdcda7469e939f97883299e56fbfde2d9ad633798a50e2df7171d2d556983b9e79508e11b3ae5206a13c3071bccd95c2b1d4346885be51a8c5a3fa9b111cbed42c8858d19a806a3e64a37f16fa7b3b34c4c70649177e3a47bf09106c3eaeaa496579bf2180d0615d256a7de5b2197543ece60105a84eabb089d524a3a2c7c98c1583953eb8c8f7557e93298544af39ef5aa71532a32186ed545c898b07c523606b369e1492f2bcba245c52706e30b153d69c9abb82b2609835146a5eb5367ea659a9ae44e52f0010673a96826d685d6aaa54771f0f105d3d6c512d3975fb6de0573f0e105b390b1cf2a7d94ea32ee824946db5c97947d379b123eb860986df7fca4eb53f5fb838f2d98b7e2afb29f719ddcfb935392457ff1a10593947bbe666b1ef54a838f2c98858ad253bad2a53b4ab1606e15ed5b3945215b9eae605c7d21435a5a0f622392e3c601840f2b18f4d89790aa171b4f8324473a74c047154cc964fc4ba524e5dea682416deb20d5d86ccd8b171d3032d22477b2370583b0a8bfeea2540a26cf32be3cf5e5aadb3ea260f04ba3df95942beb23281847cdf5a3eb14af35a4616282f6090615f5a9ff1cbd57697ba1dbc587134caf665dcd2dabac433dc04713cc725b9e3ce92ad4954b722df8608279df73485d1b61e6dd271b265714e16309c659ad822cd793cf2b2518b5541461ea3283e4ae9360d62e94f417efa8a3ce21c1a8f696d3a4fa50f07104f3ab27e92d75f4c30826bf74faee430b731116c128574ba9f7729a970811c1a4a4d0290b95a4e8c7ec63086691afdb5a79a6a310af5ea10112087c08c1e84aabe5a0bbfd08023726abb4dfe21f4030896727a9e3c892f2b99293e3001922bc6822822d14b0802f7098d478098c8c2045c247878f1f183b6cb8f4d341c707edc307c6cc5897ea6941ed453f7a60d47a5fc91261a3f46debb538792e66a8919134636444051f3c30abd737dd74d579fab7437cecc0e8655a29214d968df498244017f1a10353f0d1d7325fcbf3515bf1910353ced1c5c377fdc58692101f3830aeb2202ec2b3cfed5cd2473263ad1b351ef085024e4e4e162348f8b881d93f8a7019bd1cbf943e6c60ee15fafbf5a7f9f0aec6127cd4c03c5f41ece6ddb3ad94b3e0830606dfdbd529a5fd9455b632b0109121626ae030f998813947e68954f2fe908129b69273a56a84e8942ae9d7c3470c8c2a4735bd4ab7e2770e068693a3cb82109e17185329aff75851cc48f9870bccf257bb70293d2e97ca470b8c3afdbc9f162b3df98b64c6c8887b7bb0e18305a67c5acba898253fc7b102390e0d068c8cd8f81c390e8d1d7cacc020647da796ce426d941f2a30ff8e25a1b6b3948f5cd267e359e0910a935211dff69febc40aa1c21cf425532e75927aaf750ab3c7502a87a6d232e71a1921394a7898c2a8b5da78ef97972fa74adaa314c611d57efdabcab556521845bbacc925757e73de62e1310a63ddda25d3979d063c44613859166564abf55f7128cc6fba5afe8828754f1fc0031426d5b9214d9e0e91d57e17e9e0d882027a24572e121e9f30a9a0b3abec31fc5f7a3c3c612c0fb22fc5aba04eea3c3a6178199d54482ff5b41c4e9853258fb66a7e1677b50993ebafbee0db31b4d7257d232324ca861788f0d084f992cb3b3127f75d2a65c2a0f92b5669a595fe620f4c182eae16f25a659955a24b18bbf3767ccd197d5797b4031c20224344013f03c7162323232324374ed0c3120655fe76d95c9f47ad37342cf1a88449ea381bfaf4a7d8e77b50c264e2d674daccb7acf54998a57d52ba656344ebffb9f09084e1540a9d852a5f21b416071e9130f6de5b7e991647cd08ef7ae00109737493de292be6694b7984d1e3abcfe79fede97384719452714bacd2a31106576aab837b8b9552278706498e19346e9c9807234c5f72e567517b51289592be2c5ec81011c9428b2cac10c9c20af4588459f7e3be54e95f85be4bfae49f012323fd858722ccc1dd92aad0a743b94a224cc2a4dab1db4ccbed418451f3a33275358f52bccae310e6942f225bc507f90f7918c2f0299a5c71ba97f4d90d8f4298a54bbbe0adde2d9837bec851238d209110a61fd93a27a4b8961a478e1b09028f4198e45edaeed43d8bc261b248f24e70fc4a18e85b1e82306ec58bf6e172d495f21a0883d0babe9576a975769580308a16d3dfafdd6486897f30e7f3dbd2ea65f68cce0fc67f65aa63399dfc677d30aaca5572da423e60de8851df7b7a954287e86ead073cf660d2b93f846cef90f1427a306d9897ec5341cedf9807e388be5cdf2bda73943cf06094e24bf6e7adf8207bdd80c71dcc4157b09896e58793d30e46213b5d5b98bf9d7a3dea60f0ac0f52cea3c5fccc830ee6ac6d5cd5761e35cae5310793a5d5624ecfaba724f49083516967f175f75a6e8d10e360ee8f6d1d5d76555c25b782071c8c95ff44d484d4d1dace7c835169fdf1be3e280fadda81871b0c2774f62ef9524ad8db06b3ccc7bf2773d5e7a2071b0c3f6a9536d5dd1acc1b27ed3fbf6bd3545583415f165ddac4cacaea330d06d392c2830c1d2ee720a2c1f4af72cd5cca239ec194c543bd565e4bfa4f33983ea70c5b7996cc3218b4baa0636aa15daad749287890c11cafe39fb23c42ceca3cc660123aca0a35e7c2550e4a31185e2eadabec327514157a84c174a6765d7e4e3afb3c3018e795526b7f0f04b3a7b99cb3c5b99cf2873808f981f9e3c717a7553ad7a286f8c0f897d54a9553a82c3fbf4908e98151cbf90a9e82369db287f0c0a0f58be5d7e97d85663b30aad6a7f45e0cdfae5507a67071a4d459e3b63f290706255bac749bd55fb775108203a3d2e9166577d4ab7e1c197142c80dcc314d8b6765522d688f0d4ce97b5c43fc43aa14cc42480d8c6a2efedeef6ca7b66960deb5b9fd9c55ce123a928b4208fb233a4a05219581795f56d0b1b5ce93150b8981f983d2d5ce7a4b40b9101898578a49bd2a8dbafdfe05667d79b2b30e2fc405e62cef3bc999fd18ff3f03720c212d309ab05c7aa72b8bc53b8405867b5f5d5be797cf43ab82901518e4e8f7d22dff54ad8e21401784a8c07ca73a3bef05fbff12622a8cd973fa39f6a70e1e23a2c258c9e5854ab942bb3e84710a831c7df6a6434634854108715fd3a73653b9104b61d29442a9a82d4597f2472485b995a9adf4ec4cf55288a330e76fad9a13fb21ef228ac2205c5be564f2e93c55324361922d2b4b37e34d970a0ae3f927252e9e2af313a613ebef2e5c56506a677ac22452a9ba2c6d2952290d46274cf9dde6e49eeb2d29c54b80c10963ca11e293c678be2894b909f388baebaa88ec97a90993e9fccb5dd99309a37add5acb351313a67cc1d3aaeebc07b9332f6132efb4f7d7490919b719018625ccabb7b3ceb27e0acaac84f1e3c9aab49f7fc5a59430e8603ac4985e17df0e8c4918dbf6be820c4f12463d42f7ca95a274bc5f482e6300231246d941b83c9bcd0b1f42c2686feeae83d4d2c95c188f309d54faf757afd6c25f3b80e108a39c1cef7c426bf92ba4018c461874bb106a5df4ae593808301861f64b7acfef5f949a6d0a3016613e5991b352c592685714617aa9e5e715fa55894b521dc04884416a9d54e547bde65a8908c3fbe9acc22e7608a378e5902a2e27af1c1ac2a0a221ebb37811132f8c42986407178f1fd9a36d3f31c962083008619026ef54562fdfddc741987c5cc7bffdaee897250883cab22a6e6a199ffb0b8461e75356aab796980c0883ce799f9e53e54e1efc8351c7be9a52e2b1b458fc605e59dd9d3f7f4b47b70fc6cba2c2ecabcd078334fbdf96f3a0d43e2c7b30c891afbc724e798f3ab92b020c3d18bb449c54e74908755501461e0ca63bfc2b71eed2ddc3835929ed9e5a47f9cbf512d9c20a919191be8361947855ed29b594b7aadac1b45eaed56bdd69448f7530a7eb8eb25de185ee9f0ea6a8559e0bb99694f69c19261620b91a30e6607cb1dd49e87b78adf4723066fe6ccb8b511c0cb2e655e925b51360c0c1245a7df4549f5d37a59393ffa5fc06b3c717964ecfb4fca66e30ae4e1685d6fd12d776ca00461bcc9e26abc26c7a865eb1c1982a9b96eb165b5c34d7e0e928746d4b3d3a393464b81a0cb32aa7ee94f5bf52370d66d73ae72f0feae453478349b4b57a871cf57ef12fc03883c9b52a9da2458751752b0c3398e2ddae5629e7c7fdac91913218a5ce0e52996c0b9d3e0787191939387e060c32187c476eb375e954d78fc1e0269e4b6b695ab25262309ea7beea581febb90a83694d25fdac2bcc77b06dcc60000c30184f56d5e9cba58b9323812f98f5be7608792a28254a2f985f55eb182677950bad2e986694875ea5dff2cd30b8603c9d7d1d4febb76014d53a532fa75b27d482d956b8d6593ca67e27b360363de1b9b2ea28bb2a160ceb6a4dbfd62623a3362bc0b88249e774f8a720758eebf20630ac60ec3265fe2a3aa78eb90d6054c1f8ba2afb45dd496fe8a860d4db7549a720afb2ce4dc19cc2afc8932fbd4cbc5600430ae694cf50da72a85739c28882c9cca4fc9eaf7200030a26a99459cad1c5b98efe04738c0771f12443c8879c60ce6642869637a69df335665891216817c06882e1657e14f75e519b1033c1a89d53ceed17ca63664b30e9d54af16f85cea24a2598c5c98baaee65b6cf97048358a1562eea57cb42878104f3a538ba8248cf15601cc1ac22e4b854a2b2c5ba8900c3082695e3af8f16b1537917c1ec61e9c3eb58ad469e3088607c9dffef598588db118c2198b2e957e6e6d25bb7c92780210473e76c2a5e25e923564130a59cea45be36d1d22b4030eb2864caaf6c7a36ff81f1454dd58893a9c557bc07307c606cd7f1f4a07e295a12460fccae55c5b73479e27306830706f9325e0bd7f19e6f547800630766f768e63f1f8490c2d381c1a59b669bfa4afa74726076e9d147b6694a171f83810373f44a6f7add1b18f354ee18a32cb8c86a03937d6a399e524b79188100a3060695b44c9aac37d38b5dd12880410353f28fd195f3c79ace6660da6f7fd5909d465c5c06e6b4523ea91effd0f363600c5dff1ec48de89f0b03f3aab5ac6a332a086d798139769454a55caf52cf61b8c09465b7c98a2aec6d5a60b4c058b759ee3682c1027396ed7b196fd970b905305660ccdd55b1b35dc548a517c0508129cb9f692584bbd24f2a8cbaa56a687aca7edda1c2ac3b7876b0a0aebe544f0039853969ac6ef7b1afa8575398c2a59caad38589709506298559b7ad0c53e976552c496196a69d5df9c54527f928cc1aabc25fd4332e36cb201185512993bb22ea518b564361dad650cb1784129643a0308e9faabca8f76e4dfc84f1855d0e2b75678b174c27336a289b01c413465953b9d2e788c56d75c2f8daa4f6fdcab94fce6000e1842945ade5cfb57e13c655d17527f9c1b50e441326132e367664f9092dcc84c175645dae17312da39111c584598a91f2773b35ffd427209730a8926fe73f1f966e75be2401260d304b98544e97f47818a41226a1b5efe6a952fff62b40095394277b85c8f10f3a57d237e36446b9131a33727861009049184e8e89d0a3b142255b12e61caadea59c7ea1a456244cf63aeba4b744e9a8363d8040c2a45b5a8757f37ea9553f803cc29c7f5ba8a87f940ea742b2208e305db6a042b7b61f0d0da411a6b09f5ee75c76ea9fc208c3c8ce650ff92c4e4b8b30e91db542ee53c6a84b11c6ce42c8f9a7b6b93c11c612d5a9c27938d14c3da001223244182022630122321061cefac5784ad333214e12003984b9bd4d5ed0e5f57bfe18400c61104fda5ae5f953736521cca7626448317ec9728b10a6d36622af55b4f0d4066196a93f859252aab03a250893bea84c9584091dfc0b84f1b3aae55c52a5ede90061f6ffac85560a96e4bc3f98d53dec6a997d722de607e3e99c42b547f59684b40fa6bc9f222f7d98acacf1c1a45d0857a63f8ec7f7604e26bfa55679e356957a305ffa512f375689d3511ecc5a7f4daa79794aa5762d00c183d1a4799a9e8c94d52e0b40ee6014b79bd5a25cbc5451103b18b41ea15ac7558c6d790820753009bdb735a3b4b58bd7710e2074306aa14a9db8e7e660ce62b792c9552a272a39183df5f96e6ca75a90c5c11c5bf4d7c85afff0351c4ca7fae9c3ee9998ee241c5e9850c086c90cc337184eabcbb9ae4bc8f4789901881b0c272bbc7f4365b7ae510d2fae10d9c28a9191126e83c95b7918b7642f95b8b0c1a8d32d8be52cf399a90c40d660164a8adc5739aa4a8e2323a8066329bdd71f9e3c223264546064e46d78d105903498b4eb741eddb29c555204206830eb931d5b9aee0ce6109784d29a9faf98c300c40c66af13294d7798015ad400298341aa75cf5aabbb5fdfea3c5a3218b5bb5a19a1c3920c7d016330777df65ef9293cee4705206230d6e9d0b39fa6a5ce522527c701242527a7c4710b206130ea97acead322b7e5f50210309883899eeea05c57f63b32c205902f18cc645db858adad375e00cf00e20553ca275674f46cb2716400e98259cb8c6f7fce3152c912f822c78c850308170c2b44a4eeab72a9e91ab923c92d18fb447e2ed339aa6a492e07cb01440b266dd7162f5de8fcce96c3034816ccfe297cb4c57ce99a8e8c8c8c608e11bc1d2040b0600ea354fa3eade4c8996e805cc1b0a9d753745f67874ff300c40ae6205e666a294dc8716d154cf2b27f5015945411a6d203102a98fbc49f9b149d53af3205534c91f7e9750755539282797dcfc395f8abb03b0ac6cf1432332dc907b50001040a061d5b32e3745510293ec1e0795de7ef2046944e2d08204e30e764e9dc54bbae985b138cabca558eb987972b3e49d77fda47cb7509e691e1db2fd5c90fbea9124094609affacdac4c66b11a92498774d8c6903a6ef91adf3898a290c675aa7cb95b37deeac1466194a4ab916f6a4de9b1466219f628aae68ea2e3e0ab396a1725041ec8577164314e6ae57eef5d93332427272f238b87c8183a4ecc949c60885593baf12db0fa7ea6d415c400c509865a6eb2844aad295fa8459084f530c4f18b58f4b51ddafa5aaac923e04c4e88429b6f6959582fcea396196625aa6d0eb2bd6d26881189b30bad23d672b65e99c865881189a306b28e129aad9eab88fe3dc505e9930b70a42a60b25575b0e3191e7f52c43bb72cc389961812c46464646f61246d54baf5b89bf9c3c9af7312c61acbda8b7b2547fa54e2e6c98d0a884e172f257a6f5f44ec5c6db30b931825709c4a084f1b3a2ab28a47b8d5763467a3509754e6e41ee7d3b99118225400c49183cdfb93a0f6d254a2b6244c2fc398970e9e1e4c9f89434c977716ad841c230fa75b4fd9b58bc0d2cb688f108a3d459ef97146b731e3bc2b04aa7d6dfb62fe676238c3ad52acbe9e1b4728d11c6bea0e3ebc6a7d21f0f044f012f3116614eaacdc7b5872f912f4598728aabee723717549f08832751975551c5e7558830cad2427374e80b0d621cc2dcae4677ec8a14751f4318dcd2ac56754dd5d94bfa30bd20d9ab21c2002d440a611ca95f9ee3630ec42084b1bea3691b51a782d0c51884e9f27d49cff22cd35479366208c23c2a459d525ed1098e93059e026204c2b0264ce57e6c907c61e330163100618e23c4a64b1dd45bc35331fe60cec136f62cb79079a75212c30f06a9e7c48486e79c1bc7e883d9c249ef4ce152bc8a49463063e11920061fccba3db68f16dddb2977b207f3beebbfc96cf9528a9563e8c1ac5a2b7ec9cd867069492b1bdf26461e0c9f941457c2531463ae0d1198d8e8c2861778308bf3a4bed2e7d82afa9319357263dcc11c7ba4be8eebe1839876409ba997d7c1ac93aaf421d65be67e3a18c4b52e2dd47f756e770e8695d7bbe5af75eea096832925954fc98f7437558983e995a893254a9569d5c1c11cc464afe9da7ebcd11b8c7af5827491aa7ec56e3077cc4551f14c5dba701b4cba2973eaf1b45cd53d0546465e8d8cd430c961e394642182186c30eaeaefbc2993aaa3bea4cf86090d93449223fd07904999408c35984cd8fe9cae4c1d5d4ab2b7aff0158eef186a306813722a09fdaefe212dce6b11230d069d717fee417dd536669074f17fa34f8b051cbec22c62a0c11c4bf57c6489331d544aface02389e467a454272e3c4066083186730a9edb9be0d97198cad4628552d62b49062198ca393f878d2f5b9f28c0c46bd23bc93163d331b8dc1ac9ff2ef8f7b5b90153198ef557c7fd912f623f2186130dd69dc2a79630c3098455d2bf1202a2a71717ec1a43e4ad379a35eea98c5f08259c7ca5adee2f3e8aa5958215284185d30fb6c9716619fc5bcd3410c2e182ceaad894fafbcfc82b805e3b9694fd9a42a86160c9f3d748ae87dabcbc6c8826153f9b6f8e70ea9322c1845ba8e23e4eac751638c2b18f4a557d97eef54523931ac6054fe1d5c7f9815ff2f46158cbdca54eb67cbd3af23210615cc222ad72a2d55ce4b360583489551520791da394ac1f4f1e24b78ca146af32818b5b691974c09db783d148c16b45b94bbe88ea12798dd2ebfd6d0dbabe939c1a83fc996a946f683184d30293dfafa9307e5e1d54c309a6e9552ae7d4f615e4b30eb16e6e23eccec694809265f75af5fa726c1a0b208a1a5c2994aab4782593fe91cbba4795d501ec1f44aba54a12f7cea798c60ca2a478dba56276f4a45309ac7cae1292d99782911ccc9bccea279967c960fc1e8263ee508b50bc1bca9c5e5a0948260f4d3af4a674556a610104c9762e914199d6abefe8139deee05315a5aac6cf18149a7e8175cadc89642db0393892ad9bb4a29b16df1c024b588cc16aab22c69d9815943e98856e562f95b33c4d08171c57e102e2b64cb28330746dda9b5ad367d9e298c8103a30a2a2b8ba346be974e1062dcc06ce7794cb63fe9be7c0c1b986d3c3f7a9b0a39425d0363c9c98a1f549eda3bc5a081417676f1a0d57495dc316660d61ed9aa964cd5183230fd0b2174cb5019428c1818b5eedaa7fb199dc620060c0c62fea553bc2fc60b4c4a3e5c709553af6e15c305062d65cd78d0e1b512718c16982ca71cadb6d2d87b198305a6eda85d6becfcbfd4791c2ab24503727871c3c60362acc0acfa1f6adcb538fe699ca484182a300a296b59aa5bb8b0da905418f363de69db892dcf4bfa2850e33c8e1c2730084185d94b86142f4eff684fe1290c96f6b5d66676318549f58dd0ec8b2a7fc410520af3df5c927a59a4307650325596b9c09b42c828cc1dd492dd472d947ece03ae509fe3e46d905c0f42446134f7381ea5e9a130fde9c5baa872a030cbd05aeb4ba5d38a7395b4046478d1783872dcc81026847cc2a8abbdfda59dab7515e209e3aa5d34af755f8f5a9d306e05cf346d1546ab16274cf25475e45912b7a80e81498dd7c206162232b208d984b15e87beac73be8f3fc78cbba7811c0c219a30ddb7d4df2fed0a0d90e008c98479851625daa1840973b9d41d37dc4fa9472f61d459bbcaa6936b09d3adcb89527aefa2d055c234ff4abdca15cb598bb1c710420973695579849f9330c910f16059376ef39584c1c67b4367dd4ae7c48891307b5442b7cac13fc8974afa4a727061e3699018244cca3cefd2b9cab93597f424421e611615bfb63c6fc70e2aa685880c119b80880c11cb4244868861212243c4ae1091216212109121625688c810b10888c8103108886461051f00d810e20883ab893d1db58434c29cb22e7795dd97a29a6684800c218c308c9a7b94adf128b72f6708598429b8fcf4d86fe2553cdd851045985d0ad51f5e2a8aff9d0883d6c9575c68931f2d8620c2acd5652d8492b6aa6ef5001b58e0151a2009001b420e61d8eea83584106af4d3104318d4aa88d314cb494a6121cccaa3d0caf93cae894a08210cabf2a37379d8a9bfb40021833077a7b4e2946957216e411853ed3c4b4b580aaf12085374f1dcaab2722faa94f48560860a418e1938cec8c809204cae83e9d6a3ae3a0993c8160d1039e9c2c4c40101b043c81f0c2a6829bf531017343f242439d2c19163047fc880103fa064e48ff872bdfb60b8ff913afb699dee89c98d344708e183399deaae94a3ba0c89e0e020493508d98361545512a66d2e5ce6257dbf67b842f460344bc1b43d9d8c5a561ecc9f6bebe26e970a3ae886175d989858d17508c183613b4f0a3959f9b55e4bfa38e40ee6b06e3185bdcdad4ced605e95a5c5d6f9d2b81092d830c907d81842ea60ce292915b466eddc761f08a18349bed0beee61e4c7d3903918a5d0977467a8f78b1f1b1f8227c9f1278f23440e66f16aa4e8eca1ed2d1d07637c5cda3a2594f8150a818349aaa9d44a6ca89cd3172e42de60d052cb4244abb87d4f021b587c21c40d06171b22fe46ebe7d329e9ebc2c4a484e469a81924364c7009216d3069d5df42fc7aa98aea0842d8605a79cba3ddd999f1cf682884acc11c5ef7aa54b04f255f6a30084d65c1e4aadb9c5cd2f7c5a1a16898a4130e44481a8c5a05f13ef2dee67f6486103498828aa1c3987e957bcf601e35e29229312a754e33189550612f69cb2d5d25f909216530bb12512fb4b6edf4a092beb481858c921c332e704283460244648888c810912162d4ee433023840c06d52ac991b32937db2de9fb1c2743c66052f39e2f7c580d9542257d8d23c78d1031185b95e9e0512b058b10120684885e2954564a216030abd96d8b9732c43d5fd2a71008f982b94fa8e67c655befd40b26bd164b4a796a53aad41787c63b6064e48b43e3c6c90c35822f09e98241caf6e82fdba1e3bb42b860d26f17722b887525a442b660181df5f2767d0e959e164cefae5268f5a0b4cc3a0b86afb4fade3b7736b162c1a063bb05afb41dbb735730eea93bf55539a75db782395e72e9a1a192da13f11707078e5323a40a267dd2555ca998ba2e8b0aa6cfd2ff54964cb14a680a06d7aa739f4e2905f30bad614998ce4fb58444c16cc9bc944bedf9ce567c8506483610020573f0dc7379fe4bcf74257d24666676225b582122e30e214f3029954fbc47bfb09e3c1ce204c3aaaa47755aab09267d419f85d349374b0f618251b43069675b6157052dc1782673e92feab6f59560f6b01f47cbe8778ae28c1c244014d9a2017b214930b9d2ddbf42dfcbd3291908418261b4a4b66e952b8745f036ee44b668c0851cc1fcda766fc6822146309fa97cd27d84214530efc5df12abb7f7511e4204a3eed52928312ae7772de9533466788156648b068c8c9c74616252e283902198d4658bdb172e7752a6100cdaf326737e413bb49020982c6abb6cf2d3c58ffda874b285b248200c05a21886610080e3e71100631200000010168e8503129950b0afdb0714800443362e463432262e221a168f48238150180c85430141201408c241100561188d7434466b001708dd705e364364e817cf1b98360d18d6d8c42d0c1f897e559809560eabf1b27fc43b8d85e27e90d44a21157ddbe961f0d245ebb7e344860a99a9484b4e094d13290449689f8ea8129adc251e0215ea148ab7c0504691152a37188e984a940ace33d5ec356ec460c3bb5e6354e02a2d02101ec63541eafd7f3b20ac62be7c58c3266a32fb93ccbfc63e57b4a5b435b99ec27bf21b63a0b94242677465dcf7dd82c00e0d04041d9fc0cc402726b98de0ef8e27b49f37370f31d4b36e3831612060e4138f11cbe07aeea821dd52c5ae978ed07a8ee595494b001272874ca5339ddce801ba569a2a4bce59ba4154bebe193cc89950d7572883833a2d55262d78aa37134250970ab216a431ec47b9dd7bf2484dc9c82b2152d748941fc2ada9a7c6a251056430e78a5fd154a53f362ab8d29b36e35172c187c0852b0ae8367881547c943e602b6b6d5dd71ef4f377c77e7d49a80515101a88ad9805b6b30696a5247c0de170d86dded7ffe2de6038af671fc7dda7b878704b9fa042508c78873d5bd305755fc3e8d87c029463d3f83c5c4436bb442e92e034052a96e02f64ac6e5ea1f2e3296eb86bd8aaff016a7092eed23d2b8734fa8a6b37e1d09b08ac6b4452b35a26a004fbb1a477ca79f11e20b4a963b1bb18bac546e3607ba91eb4f07a0513a9b934512d3eced3c57722604b56988dcae73c0cb4fbb8561d3c05be8f8303f6e7e6169eab555ca1e72cb034517fff5dd5534b58ae90a82057c1a784a91054b9ad8b7b264194d263a13e0ea6fc3e1c3b5add3f5b716a4d2aec14243351344fdc6496b28c189fda22ae9de4730a3cbb5c9ee1874b97f54255af08e544cb95e6a3cea3a94bd555d7d2bb0df7f6933343384827bea24e55fbcc0468c773014a5003b26108907158d725c7ae29d9894a250758d86a44c39b52b81e40aecf0bbc1d9793d0a2cbf04ebca5538458038f2815235289a86c206478a347ece271101611bf35efda3a4e0d67e50fd54899da50aa279322ffe08df84647c29d8a77c206003a00d600a4320018de33d489317e2bc6505f838e006aa79625faa7624f8e31820e56ab686ef16b7fa69e7afc2b747cf9110c4186b1ad3e8ae4c5c1f8660988d9f0e97b8a2a3007d035003a2472c9d78de8913959f19c6e8161433c394fa9ff9375b3d53bf959c68e8d7446fd1abf018b6173ae708fdd5d5b8d35df630c922e4fda4885fdfb206bb3830584be4220732d448e245221774517d5a6d361eb185c46d1a98b9b2937ff9f9dce934fa76ceff3eaea88945f3a422684a252f3d946bd1d5887f838181b2e8c2cf63a14031072e0fe395be6d014a06645dfb0836b057d6fcd6af07544df6b241fc2bf27cf10dbc1f4a8b255685a1157ab21a3eb15d7635989b2e216ff7002fbf135cb3c05eb987c4116db09c1d7ec87f4a1b715625a212d57438195e90c40d2aaf1ad40b3158e56416bc575565fd2ac86b87386f76235942a96eadb0896bdac7667659d56e965d59dd5d663356c4f4e315219ae419c2c06e5a30b83b5c6c403a19bffa11f40ef7f27738d80583645176f4632523b737af06d1c516da2cff2d7315301a61780506042154882140550a5b638cc46bd27e1ed0da7410a7f84050ec54b7f8864dda0fd8a0b4e8a986a100eb9e26321eb65d485b6c3f2fc0a697fb199513ce57389da81981301862a46d4e984252619f2f117291fc0abc265b01e9cb95bb388116516044caee5ffe9b4728151c575de087dc106cac201f772d0350cae16ee1de053d8b7c8abbac924765025136d3644e7e18127ec40440169f075e21f46b5cd22dd195309a9139de37805fe9aa3028fd17cb0fafba4a82212819a9f9d37e0c293c24e3958eea42eff4a808a257a732e5a71a5ede01b292f14548da85fc7000ed5b4c42678223e468f2226122a1615da40d8c8a91116f5e8a646497c692a502554df4ef21aa2304291f0ad7cce9d175b43d85b35dbccecda2e789486d3c675ba87822841c03cd16a86012397e43ed638a38376c90618931a2cf3ce3ec3246cf6481f30c5206a5fe243310bc44d80964b37aa0be2128a363da505fe56af7fb92e70404f2929f3549dd64539c7cf298a8c7f0c334e66e4a0b80e544570f4ae9e574c358a39e3b63b2ce0b522b74518afed81ecb471e8448c3a9645612d749b8b1b514bc19791eb062bd7f6e5ac6d2a7fbe73c898ebb5ffb521d2616ef6d23201622d55cea92bb878e786122c7863aead1e879df3940c6d0587c536dc3ba7250c6135ff09ea540546f25d62aa2fd84cb4f0a005f645c3f364d8279147d18ee7a9a031049d3fc400b646818fd653aa63010beba1a2db30a5daf7849c246538fed01a0d79a15057aba6e6829530ce45841f9387391243564ecb142a8c1b88886ad73bb38d122852cc9951688063f646c1a310c2f3fcca5970cf11fcbf2946722b1938c955461379516f29eb61f073d8d6d2e53b7839c326e268a28c0ee962dba7652da518cda1c25f807977243a19525bef860684b50b5b56ae29e4851c4a058fbb57d8e1161b9be1642cd7850a6240bb9a60f0ed34fea17f8ddbd0716578ad80ba7782c73e9119f22f718c7dd47a9d6ccbe848016ffcbcb774e56162f9f6cd02711474965a3b61003a28fd5b3fe3f2fedb52abf85f2aff799c1efea5fe21f92361000e7b06538fcce6da9bcb589216fae989fb230f4def43d11c9e14eaa46fb8928ad695b5bac3854f1088fc67b2220a10e8249368e4dc24453939cab50077198397fa1715c5b8b049ccab92966bbc8b3ee7a60790114e3f6efc6342f4fb88350db1a46e914f71869020ffc65d602e3b8272aacd286a9195f3539da92c962d483a87d830b80132441991305bdbdb4c68c05620628cc06ab22900c662a4bbc1826e96d7089a4ede615474662f8778b4d31cf9863bf9879b4e50203a2530b5cc4b6d35ddcb17b7eb73bd76ed81dd928980bf194ce56e8b378e9b38365a985367d85f88f5f91e79e835f173be1bbbe89a548cdafc117d504019f7e2df9c3b4a8453f9b1f90fd6dc52f9f0498d3432c2fd4626f26c941c721ce20c17c893faf9032623d92d60dd6f6bd070838e3bf55d5817cea118e6de0b1c17f43f57291187517bbd504c92b824bf388528e31b55cf66828f50e45118430b88d1d1bbdd4f9314b437b66912d8d4320bbbe945b43e38619733fa73724a10517645c8295799ff9ed55c69690269d141fcb86e5a92dad3e49f47448c3f4f747c2b252669081ccc037789fdb6a1698fbeebcecdacdd79ac30f458d8eab1581ac6da766a1b09ecb65c79aa5c033b266f2de7ecb2c49320513efd055997a1a3c62a40c5fc4863da29d161a87169348ffac3b02d7ca802ed945d0e3b6b511e76dcbea93576db6642272809f926332cc283e67069b904affbde0d6583aa55f0db03c0c0b5b53d7b6494c436faa2bfae9fbccbec6b251c7e56f2162c0f59dcbdc07f4049f0a9510eb1cdea2de377501d6528577519e629792d4520eed5a31e9893064c229a3222c32321db098dc64bf2db848c19e15f5e1f4c09fed951d401df9dd5e7605365804fdcb57390ccc661f11cf505bafa54949451c32d83db16f15e98fa620a29014001e032a5496b1cdffc53ebc83db92ab0dc6bff2570fec3bd04312c2c1e5ad65c2b37c51bbae059a7b55c4dab412771137cf50c22a16d09a7a6649a31d0f9143fbb5a1956cb2adc4a160080470232dfafeca1aa951b6d109129584ae672a9214e63c9ba2e773bfb40395b5604c948574b63344cdb1db6ff4732318dc34403a4de54301aefc3501dc850a387790045fd64f02a44130ccea03c5483c5282201b4333b50224d7f75e5af7df9d99cfe081bedcc7a895d39831e305ecdc85075d8707fae215e89298a51486b7992aa3b6af37bc02c766df2930ae9818b63a18309e393963197f676bbbdeb7724bc3a3f6f96eb5820464a806247f727061454df3164d83e0dae9bc28df47dd0b10493e21d39341b85c8e493a0858a1907c0ea68651ab2de2d005c97426b5ead014b88e49ccfe0b808d939c2abeed4fb6ae4bd0c628ed2d645c560e8f9d838de692ec90b2d9bcd6b8b9842dafba6fc9374b60078b45217f72c2d52981e719059a8b362146d1da96da12714b76794217590cf664291ed33502f91e6807a58d03ba4a3e9acfbad25302784dd045fac07df2e8a80386b8e8cc1aa529c5ed210be58013426198f058ccfa333efc629af2d40c96d292f5017f2ee2664fed26d7ce4eb76d7a595f897c5fcac51130e3271deac743a155fde611cb875b769fc49ca93bb37a303eacc6421573eb8851927540bc76e935174890faad62b773d48337aa61f0b5146b00bae790a4a0792b7f63035e0edee3879cbf68e262d432a147af2600ffa031e14ce752f1a1c69dbc902ff80f208c71f746953a3b46002f33750ae05207b566691c162a106d5c04019376d24293a73c3ff7592961c6e73a41b4710b2e8c4ea25c5ff8b984f1efb180e0011486026993923a782c558f2bf8a02a82e4860342f8d173fa40446436fb0a3c4def1899e442d8492ccc9f5489d4a76418e6b1434722202526644c9b99f6458483c29e0f7d0f326ea0df5d648826b8f377b5410728eebb2ed59041b505ddb541fc1311d41f4f0574835386475080ab950b41dc9962e9c7ee18daa82dbb0f8205f7f6c898c7cf1c3c0be5ce0c118a450d290836eb303f4a549846076c9a7fe47cf6358400218c37eee8faabeaf2a2bd812f64c8d9550076d5a703d21d2bb00777ed23ce00efcdb2ebb8d70e8b5935e67bd26931ad3d8a7a17383ebd6f2e73706d0e155666378d556d190d9e56b32f3f2cbbc5787f1056699bba11b82fdb6f9387fccf84c699bb05f6aaa47ee006b407f4508a4e5c9dc954d8fa95202143afb184ebd607805ecb4f8c18adb5e86a921809470df84a85e4e09afc11c4903590546bd5846763b4f57b082695c5e074cf40005896fa3873cbdba22e00ac7dcbf317a1d56a139211d205100060180519d8d7e4da02aedc6026316beace70b8d07869d50158ef617f2701bd30c438141adbbdb99d05d15bea3f032e0b7784c48bcdf618b996cc9dd3bd80c8f9a7bcac355974aef0c7dc2912a0595d83a2046f86811a2c740a4d46f1be315919339ac89e15994295265d48c703cc5cafb239c0112d9f10ebff244427061184914ea754c8520f482c474b98e8cd4a4e00b20ded42b1afa38a25a9d4ec75613a0e064e402843e7f1024e35b0479388dd6c19fbbcb102fa42e659a6c2385ac60f605c88720386effbe7d31bef5235da12df9c04856a5d3affab80c1eeb0419c918f27484c64f35b7337938c68b9554a6e83998757364b6da65851cb2085033e948706cc0f4eb8fe50d7ebf1070593f0030954e86aa9393656c6899045d94b3cc9fc6ac4c2899e6eab52b9bf24287262dae1fd33ece6a80d02e9231f3d9364e32b49055401fd8ea253bbdc663acf392ed6ab5fb32f495f487d3e1e5d70778ab0795afe1363454c893cc3e4a430d89422982e88dae8ef5341ba7125fe13d02b81bef17c0b66a9584e9b60dc793e9044dcccd293065720181b7e85a3ca49967fe4a81f2470a4c54c65143446653dee20a589839e575b493c4ce4c445cd004497259066cc13c79e8c220ebeb8387bba02c071031441f3aebd336d0e74ca827dfe9d061fb681471aa5cd5bec0710234dd2d36eff703f7c7ce5a0975fb4571622840022a15d13514b64802298a4444d82eae2ecdf8bbb0234d0f5f483d84ca219de88fa2885bf244dbf4ffa7d458a1588f5a17616cacadf770b222a76250e47dcc620e4b676e2d2a580855cc76b26720efc82d7a730d750e7c262c10298e81968054ddb4b90a2f827728d3fe45843e7ad188882d263977975414aae3312166d43b025381cc6467f03220f1def61cf2efd2fc4ca13995e5601706312056e04b881d25865e1cb58f6aa4a83a69055c1fd9ae47109426089d2d779ba1e5792f822b922626a398202ad05a3493448eec6999a390a69672a04345f16d39f459eb925ca8db3a9d9dc8de9a85e0452ff470089caea0441965d44bf8fafe9006d35500dfeddc2a0efb842905145cceb8139303c8c83fce1cf48518e417e43cd8560041f1c9bf4f2ed7911cf52587ff9801dc74c451cddce9f9187888c70f06945047e4715aed13724c2920609a3bc8ce2f3a80b8a60ec4e13cca90cea919968791b68760f60d29467b75ad124543de9c6bd9ac50794e52885b841d59a2ba8a52a1fb40e87389492e236348bb28dec8bafa3c71bfea4089d64281879e7612de501866c128202e004091d5409c7d20faa0dda3443322760be81179f076b8e18584719be745da96899f03eb8e0d5f2d7e24c8cc4a587ed9d9027475793592613d902b7cf041aa72b1e585ea2425236ee4ea6848c2835ccde8751746de9814200e6dc58306c7512cd994497b2138ef89902df96bf91cb3759ab8a294bd008bf56d983103b519bdb557e9af9d504c7e9ca260e542a4bd00524fba984b3c4ec66ae4a412398f38ad48f23ef49131db76c6acea54ce022c596047c0a851e25c9bd8a5518c2134ea75241f8d7941a0418128984bc92ae9a37db7433b3ca4c3335ea0a46a88e5e619118530e439e134d8bca7864b70b6285fbaa08ff387ca86f69930baebbd5aac0d3109254b0aa617ceed3daa26032cc5f17ae5e0cef826a0994ae5648f7bb1f4119f12be98a28c7e6e7d4a5a96263746c1b10c53b120299e24361fb52f5e732c26c4dcd8432fa65f5a80ee85180891a72932a185fa580529a3e3dc4c4bb3c7ef1352e2dca47d4073c45f24359c213a45bb175b11768442afb57a08780b4741e144ee008d1271d7f41deefeb4ddb28785a3b52fd771d27a4b27287715aa618efce77730e51a058e33df2cc4170fd9ab5cf28de71da517331a444905efded3370367d801c6be150a0532eb72840b719ec36d5a85f8ced0181a906168a8188a071eff3d1cb47656b674c70aff9bec67ae7291cdcc769258e1d4b99bceb469e458fa89fa862576ab136639ad41d1ae012960504978701a40c8e8e6d1be9b1c3e0e554091568e35caf083387b6a19f7c75b489af15054a9820905dcabaf59b049811060dcf6bc06ae76515e81f060a3ac08eda4e83287c50e77f37e8070ef883069a5fd1c57da35dba119a70a59868d43ab0986c80e4eab43215a2f166f13f9279686bf6cfccc4d46b50286160ccd8c1414bac7e73a2e8affdafac79f28588a6241282bc94acabfc636cd727e406d7ad6404529315e961e39706f20592f087272b1ab81ddd4d4e3a51de72300790d1d3eda9c5a3cebd358ab4514654f5095f5b73576991c36a49fe647195b479b2b7a58a8efb5dc7ee24aa6cdebbc23fa4229092316dc1177d556f3b1d2748c23b1143946838138b66b57d63c00879b0fc9666cec650690c7b6a740d2924a6d8278f3f1f0fe272ded871afe17afbcc6faafdc96f4f9b1a6d22cf40e44a96bd9117cf83c10fa1d04452992e69bf7f601c26c1b99ca6c6c1645629c1ad944907852921e3495b9040b6d51e333608d19ec4e5d6e4531fbce66b7ace6e90244c75e064d20cacf73ccfd815129d272e64a3c3329363580eb1abaf1eede839e198b4a4818a5b712713ec0deb2d476e0a440da3c5a84f4879a824796785a7ce68edabc62432d0948844f9ee5f1400547a230f5e2f53cebe3cd830c18aec21845ed9f5de358be809cfdb32b5abb217b34969c57c75950f9f5dfb3b9bb979a1104f279a919407b56ce2e07aa300eb128a628d1863d255a9461ce7e0263b3e1193d6200b4faa02921e587d253261125b5884c8737a83428cf4255a7b47ffc05b2a757d91323f7a1e8d747ed65d960a89f9b8b9b51ca7b762a049c02bac18f418ca6f8c04b96e204f74852829973bf7b10fd912bc61e7344df6fca6aaecb97c68cd9500ab102b269cfadd5c2f3b845f9444532590da2158490e0d443f9d32d673d25c829043d9183705b15c247b2c45ded1a8375ed0fe1d88249920a8aad4c13bf5d21996e22441e19c89a59e04d209f2876460dcc8ad48a434bce781b3cd7ce2b68b73c656a8b8a8bb1fd57fc342f1576f553f122169638b0201a7a56a3550ad2d91ea0980dc2ec8f3624d189eae3273de62ded9303ff52628c302c48265f6114b6112c4103b0caf3f498efbb8febcf0400783619e451f6abe4256768352e538f8258881734903c03c47d4fca8c42bb448c8fb30044e5a440d4f6dc318ec2858ac11eb340a4f8575416f9b0e9b3c79690a86041867d48058fda26c9f8f8db13e0bedeba7e7c95c8d7c87f939a52b317246f5887d30c7e57e1079c09c3bd7f9c037ac6e1ab04a92c71c2106c315cd1b414e9148e8e01940cf814d5044ab6000d5ff88b3db2641d75dfa71ea6a9eb13f8db8f3986b1e9b533a4f7362c21e62de376d20d80f84c89902cace39f8dfdf27e40ef76c12c671f40037d302e096c667a6bab339187ffd8fb5420acedb156090d755957418af750c764a3d3b7eda50dccacd8849c1e8217735dbef4e8663a23fa67c833b0a4da5e5540da2a86e107973347fe6caece97e1422f304688c841435056bbb7f7e679908f443bd6edfb6f22834f043aaed9cc2712eabdb3c06ce830f274b52e264ce17ea97031c4e4a2443d239261cbf043ffc296cd1254bc1020a1203e2efb4f10d3a818de24c98619a8969a2cece9419096f8b1387490dac576b90b51a01e6004058b4fc0e09b2cd67e0e281172d2000d0898832505a61a5cebdc9a20c69b64ebbe0a58f50f604b5bf769e7b263d4cd200797ac90fff5e0b14e8285b39ae123e05eca18d38432356212f3fad7982ea23b90621b453474d523c23299b1685f0fb944a095eed65344ad84da8a71f7dac192b271b0f9399a2d0e2eb40935fc63127e11e373e46c5448ba82928f056db87d89bbdcbd34577e5855e1c695df5e55620ce27cacb0ffbcccb22b497aeeda3338051665058c854f4f7f5f52fade323e2bc13fd9d4b0bbc7a6aa9ab3abb277d1cb435f04ff3e651da617cf16b06752602a7ada0a1512ad596854bfd8955a411eb76014115f619415bb1fa8972286fbc59494be58ac156200825aa84eb07d31d8c8d8e7cde843d6166ecc308fdecdb1ed6118782f7dc90809c81734dc2ca78251007faadfa563747da68e06f5418db4377b43c579ee5a36761ca0ea95057ffbaa57cfe04e3c39ac7e86cff5367b0014bf1209053f2111361b007760819a7d01a581e86402696485f0788a50124a0ee6a3156526c6fa62d59f5e1528a5402c7bd7d5a48e19258763e927d80d4f16d9a2f3b61efdc53a2aaef65ec81bba39adf59f6eda1e754137ca328e78f4a2ebadc385a070b552fd1d05e401855e6bb9a376a958376d550e43e87a2273d9ee1b910ddfb21384f8788f4916a6f86b8e20e2fb9dce2dc5142f0dbc48902f361295192a382f43a4b65a7ad4cada67a821a291b2e8262f19c206bc58ba4cc328dd2cde76860aa8e4888f19fa4e1703685b3f890cdb5dfeb398a3d018f28f3b832de9e6a78d5d6349ca6b41e0ac965a23235bb3924a234a589acb4677bb5689d7c70a0bec12c0b775c7b45c13b2eac9cf3b2cc75bd25319612f23bbbd82614060085b93515751c414d824a20314220912bb59a1f803e6c05bd617a3c4360a6a37793ba6dcc13e2492836fa4f0a48f19ce35690a28e85d0038a806d3023a079952a8963bb25592d1421660e43132293498c19cd94fdd90b562584abc6c83164dc18d6ae00348fe8bd6b6a34eea9c9f0a23d12b3aa90e820000ebbb1893937288b977dcc5fdb60a02eb0154644a3fae27836ddde1f81222d137d01ea5027bf426e32c31e89d423d57c78280489da1232d44c0d571138299cfaf1ff086c378be100cc697044d1b7d5524da1ebaea4b202c544152fd0c1f46b125ee63be605d5bf8598f006fca8c0fdded79e2e82e31a922a89afbd3b0daab8ba9b8fbd979ddfd901cd5d73abbb7ae0dc0e32159cf077cf3055aa6e9810de4ca0c7414cfb4e22201e3b7383546394ce24ce72e6baf8e39a93e94ca400dea757f012327d012a95d640554a8b3903acf4728774bf94c20a8b90706a78adf2d7ef0a77a58d0342baf0cea8567739eabb0d0900194dcc9f809849e69030a5030d79c044dbc30f321d3ff6714612f506cb59383da4b818d4a5e67a144a5c2b27928230e49f8ef9bb81e195c8f548cae54b5aa1552f7cd02df00a8a0c3e1cca05b0690c710d329f34f16da68309db9900327852506412d81436cc698c25fef9b225646e1e5a48a9340da50316b9adb63d7194266a7a127fd5fcc13602d200e416a2633347fd169322dc1e3e183f5e06ba4074f9387241b4c2d6cd124dcc787bfbe50f6a8302257077aa5857d25f1fb245e14d7ed3d727c946affba13e46fdf05821347a2c4d69d2963946251c93f3493d7e7d991573305d88e1dee9009ea18eaca43a51a770d100ac3095193b33749e30bc4b828af8a14ade32ed02f34ec65e95029491389d89452d31c90e3f5cb053ab166336696c464a6cadaaff29b9485adb39521a0349c2a4be70ad9690bc640046534abccb653eca4496809bc3f70450f491f980610a500150eb01a4bb01add9713b15bd8531d533eec4ba66a76a06d6c24cb0cb010b29bacfed6c09b5dc18eb970e76c1c9261616a544758fa2b6abb7833d618741859bdd089fe1fcd76fbdea616e2338a686dd460216580379097640844613f71753963b9d7051f4f4208e84310a4db7e397ab1f501da2a9af7f784eaefbe1298754d2412326cbbce5c7af1d344ff796af2d1363f657882fa2721f6a23c93d56f9d47930d1a5665d85e6f5607167404205648ddf964087d95989da38571278d268a8c8864bc04134901a838b48121e22a74084f1327fd55da837b82fd3870e2be86f5fe03089885e359284dd08bc545b8a04d514e84adb7ac00d09d625a92b3f486c703b3c2c47466efa4c7a778098ea80f0bbdec5c6e99ceee9df2bd98a8d08cead5aa1cdf3e0ca47b363a6b61066ce3d037f7e9043023ad223540605f63d8706251418d415b68661e35f6b69d7f1ffc09440ddcad13aedfe158d502371e9227da9462802953569efc0e889f3d608fda3c40747b578388a3326fc8adc25cbb14a45034a10de348aef853391b36cb0178ec4263d253bbe51b014db24368a3ccc7e07294cd767680515f1e58a0264e60c18c0f07de58396a85ffaa15463094c687f85fb176c110f017d176528b31bc4ffb5eccc40378c25d66688b3d0ce89b2278b8b4f0802d00030f2eb6475c843ca4f54e01e88cc4250ba094a18098382e8484d4ff58d566f454b411f2c297d39cc774504e76cad97a3ed657671642edc9830c19c70b9f67d6803e697e45d567bf1d3cca5695abe26ca789603e9ecdc35c245e62d562aa32fcec3b9140a12345fdbefc70a9220be0e2cbefaf5c9e7dc9d038cfa7009def7973558ef8f8e8cb5bcda43415222a50c80111f0629d4e8158c8782707beffb593d21815dfd679aaf552813a95b406735d192d67f89a4010b7053a01c1a465ce2e39374e77c29a27406580903a4f5af34f0d5068155cd5baba9862a8a350fa81fe2e8689de9222a2de4c2ff484a817fa4a085aa949838b2cc26b961ef7d14fd038225868b8a42de498531fa4eb07bee0ef6439ea37a7a9064e275437447e3e45d5e7af7649ca104a9f9342dbc6c37b41ce68b28119c672e315e0a36b0471d2efc19848356a5ba6156a6f837ae7386273e4f12b285c001127c29b40c63d6108f24ca93ada9220c00d8f216f794512aa57e653bd594fd57e7de85d8c00a8ff7d3e1a155237b42b926ee07dd61078617fdccff9ce6b8dcc442676a10b55f520ecaf0bceaaff338bef3b4c05e9eb62b259dfc396bc648e000e307efbe8a5861d98ea04356782195d1219b858542a2c04cb8b60540682551c12e6e22221e206b2c7fd9308e78ebafc0b1598726d2a8d99e1dc034e9c069df0fc019ed8732494184a7ddee070648ec7d360de8045fa77458076036a9b274a6244acb2ed9d4b9e88815106d9f4d5f59c4c992d9bb543b38e3a3a856ba2d7f5d9db001fa9cf92b7c28ae6e5f1065590f511b736b58769e30bbe11cc48a9c721f84ad87527c89a3b0c31ba7ed8dbddb72ac017c21de01398606d529837ca7f71dcd879e9d5c5cc1a7d1c749c93093b44be532cc3ff4b33ace0c54986cceb9d69e04b36625a6a460e6448a6c10300303edada4a8ee76ff05364780d36588bcb6ece501090bf6e82c3c10a29d3841c0ef2853d7ca99d28c495f3d66d707620403e018574626813fd0f86f3c22ab546cfdbe8f9149e7b353edd54670fe8195fbb14239296288100a6856e796f73eff26bcb37e40e1b9017a2c4084a11f469d85103815873e8b3d3e264d68a6265519b87ed6009a10f102ce30584430883798511816fc9010b22b0de1f0bf7714934ee1876fa588da1cca650d39bfb222eec2f6bcc00e315c61f705f68049f3023a5a3334e603273eef03f8aa7ed522ff182c1d4d61f339110aa1899291052884d1248c30e72d4616d08191c467064079b2d91e7e99eb2146a92864238c2b421696f41009a31a2449261de4088989130ab9e2242a1dab22449aa45d08ab68bb2dc3fa000cb024ae7672db6b59f24179092e48271dc16b97f4068f039c8716edea5d9c24a160cc1b3543c340a6368db1cdb6c1634113be6e07663e55bdbc6252472a4359e1d258100d7a3a9e21c663f806206972936889064c9aec64490281c60dfb02db48aab46f23b421245380ee2e320321c05dcfc498558d6cbb18879ea586fff804ef2db12894fc96f1ad66cb6eae7d593c8580e7ac8ac68410b5a572772ad00adcd130ce64a99855d9f8e323404c30a5212c7b4b08cfa2b6de94e61cfa92bb8d9cde6a87f73d609703f73b5f1bba5b00e9cbb176760123ed2201592464614ade90182713440ef68765f3c5b1fc7f944e3cec50b1b41c1b27805a823b08826889e2871deea55e80593daf0d9b372e4da74682fdced606a8f3d94d3ceb38571cc7fea6a17b2998902bdc7b7c4e0bc9e278ab7daafe7dde6c7b4c73c8de01be8c8355d033ff1b37fdea07df5fc9ff1ef5eb9fa01ba8e7d5068729652bc86ef28dd9be7b893f614868c092ef32a727ba073bf9807e29ca58aaffa58883098adf98b9a08e7ae1493af1200b2903c43cbe251af85a1b0189408cc910012300778317d64f5a12d68dde58ab084afd6897f5892e4903a483f5c2d2117336528aec277649ecfd52afb3581512bf3ec11d2626c9cba5311b8a3064e2cd756326bb91324308c4baff979d47384bb24e50545ad6598fe44c62135f5a6297e64c5ca0bedfdba8e22f80af97d8861ca700a0541f602e3938cb530832f382800bab23b46b2c9d7995ffdc4f47bda097a735e14a20bc1d03bd0189e65fe17b1656f191b5f4272bf1fefc73c1656bf9d6321f9743fb1b7afa68d9593f8a1cb4ea05792cad0f7ef951a6f01358202a99a4ce6ab5a7c920ed7bd41579d2e1478ed75fcad3bdf952f0803beae06799c3e08ec5e40dbfdbdf5620e7d91810055884f7f58926814ac9d836293d6a1cca86003e389f36429d21c2ea90d956cae21cc1e8760a79b0cb23a5cc896c344e763866f9a6996037585567939811dc19fb317c28498c695312ae5677fd24d7014b58d2d9b6505bed7b679cba0559419e9ca5c68d52c7df75fd5808b697afae5672888f64e672d1b712c7e9c808824c93c632a91c0a1d4270b3af3f266037b33336995021895736896dc0c9fa5a3e90611fd58ef4b877313e0fdb521a7b41d633cd8b585984c5fed95aae84d390512cbd8f95be489388010c34822001f266ac0f2d385e7de823c3636ff57a08c558b6ebae4d402359c21bf7b2fa2d0a902813ea25ee966c0ea865b55e963c84cc882f1f47fe8244d181d9f09956325c8126927092a4aa7dcfb1f1170e5cd627206169786effe08fc2ebe6586327a1db26bf1760aab0fc80d4c4ce5fb52405c829df98bc7ebef4208f8eedefa064309c5b738b7597677193c4e96df63e2aecf3de2e6e32aeff2aa84b770fa3168c6901cb39b0da5c2ac4b8c018528534c78f5d26b468275866922356ac86c14286784cab5f13d1e4ce7afe76453be18bca97b3d2fde20c3692e606e4d64acc140eeb931fd32c7848b9b89c2924d8e883dc7c2b37e421b2b00aa4c8bb82125bde88356c89b36998edec167c290d094cc476495203dbd1fa6e08e0a11a074c822f5d4877aeb7916307175dcd815d7860a9f3db28c6e41879b7071ccb8a1992ac601d033d3bdb200514b84b6bd3ce247a5902118fd914e971ad7397823e35803ade9ca892647e331c0fe3a0be96d9e3bde23fd35518caa8ff078f829b35ea40e24357c3a15f488e595f67f6b1886248ec3e63d6f3f572f3f9bac4e69d6a00738baeefbc462de113a850c2dc744478d5f67e7e1ef22be0594a9fa961c4994a5ce83248276cb6ff6d27570543c53df17bda6187f16abd5a783d7898d08877f421111304476752cff8f08060532409df17a3ec103826d489861dfaa9666a6d585c009ef85eec08975b22ee6367e5bbb8c17164089fb932fc3ff43de8a82033150c60fcd1cccbb9c07cf0384071f815b0403d7147ab9eddb45456a626999195ae27c17eb2576ed31154abfd8d697ff2264fe06f42bc725ddecd371ab17f47ae481ef774083c6c63411b24f9b2854bb849eb32393721a041330d778fa91325ada96aee6ab5e874b7927fe087500477c58b9d2c45134963267c981284aa64809ebb2770f1c23c03cd66b5331da7d64a3d3dd3cdb181e2b25bc1e9ad50c5161a0cf16e1ca823a9af5566998b729fd55554d51e64b083829c37db1aa0e97c489cb69aba5d7d90d2d45399fefb12bca0825dcc64c921d66129a6f6812dcb011f769462b9f97b7958d8c1a89b6fa67bd3389e80d215e4e87ad747d391e01654f904da1a00bbad1100474442fd817db0a4e81bcbfcaf0b14b0081f6429fcb7d13742db44e299703f7a09673891585a63f43958cfbffacf02d8a8af7384937b3b53ad32d3f6d4509088bf86c5116e1349658d99013de41415704f78e19485a0b261f00fc5292b0f4a0abc0c1ccc4abab0f2cc2a074bd8c90f26727c2708e6d139a961a05d11c1b3411084501ef9c1a8f6d2176dcb1ea2124a25bf222f4ee91b01e3b242e5357097e233d98da6fd9f683f4a10ee652e24486806f1d0189af76ee34cd9d0073c4115802c81740e859463be596162d5273d4473559a1cd2e41750c0f20363b3db8e35a7022cd6327b7aadb1c33ba7d1428a90f1ada973e605b4ee0a0c242bae7271716a8ed375039c6922fe4ae2ea2fc7493911d0b178284f7bc45a4da283dd8c34d6fb62e0a00608c2bd244e5ce1ecb3d81150cbd32c331c750e78f5c50b3ae322b4c3c0adab6ecc3040b219110c0779c10f4211585d5cb4db7053bb8ba0234f2bce1b77c8697ac46816ccdc4b06d9de1437cce8a50af7dc2e997b7d83ae22dfb0ae0e8a24a8d18a574c8baf2a94a667cf9f4164fd28332b0874307fea846136f75dde0388bf2ec9f0cb513e41d98bd426e650b35c88f9b5ebe52371d808f07ad0b56ead6917d59eb311c2297e54110e41691cd874ac9432d10ee0d15c20d2f5571b3728d2bb90cd59abfb2781b4e833281c88a212f87e05f29da95dd132c65a37d95763d47aab2604c7fd3bf370ea5a2452b719da96a25b622d9a6f099ea4dfa20e81e88db4196bb666d4d129a6960c93988065c13f558a095e3853df7e2901ebce1f276d8cac4ab9e2e37e7f6104a2416048d40ca87d5e86634264df8257b0f291c351dc5a6e7abf140321240d709f46462454b9739da35e30678a76661c9dab336094fee25f1d06491414af1d8a4669509b600ee2270b02e393b38945aa33590ee8cd8b2f64ceccf43a1d6a856ff72b54133a70723e9a64692e7c6ad400591500d50b9b820f8700e2801815c08d2c85eb331fc38a287cb77b6e14520c9f93ed1469985b8586aab016293cd42db48fb11404cc7d21a3a5feb5a0f534cc7d06b55f1fe9000245eee94034222bfc63769e11f5b2e2e9a84b9abe67f914e63264fb62b73b037c105348b94458fa52c3940a801ffd8b00a2dfc0450e19ae382ab114fed956244a38332a102a579f65aca379d82fb93f8e88163b09b5d81e1c05f72b7e3cfa5d18dcc8624c4dfbab5bc3b64f0a23fc5835599ddd1ce0f2d946cdfc872e93498d2d7b56010e500a11274c9d17baaedec77d557e7e4c0cccb815097efb2b0694816cc9d02b1c2737b2089f4360e1127d01b6c9ec6245c0a063f6d95b1a8eb89dfabe2dd19056604775f4693af0efceab3a8334864497cbe391932c4419028d8ab2f42bdd5e51d3a59764dfb7db5cb7ea693ddd487a3a449bdbd76ef43aedeb1c7f49f64031449b060aa0c76c60f8bcd0f3a77781af7a8c2703b769aa53f523aa5f9dde1614043e041356b023acb4ba33730ee34c3013cc3333cc3333cc3a39f35fd68948491b6a76e68a72425b5ee2042782cfebfa04899644a296552f830f8c70363d444a4d90c670af509250a09195deae8b78eb95bee1f8d48fe8978fa0c5a2ad7b3dce5a50f4624ef36e81bb1a32ab5b8e5d3f8939533707c2c22d9ab73bcbed8a3cba2292f616973263845a446cad8a721babad165d7f291887431dbbc8cedf832f60fc2072252faffb662558dbe7fd8f07188c4277bed71dee5c310e920a38edaa3720fedab7346f828445a85569ae5df3b6245844876761d17b29bb5bde201e1631069f5e89ecfc588f0f031e1f3942e4194861f8248e7286c47cb2ff6894a3e0291cc2e8c902d84383f5d99071f804899f9bebabfce6d2f223e737f482a5f0de7c53af58ca367b18490f0e18764cc39afb5eac1bb68bff721351ad4eb728b2b97d9251f52afb634ba2877a8125c516931e34b72b08ca025078984a425076f858f3da4737c763bd79f0b3a3d8b870f3da4bfb4e331bfa77eac3b0f49d1aa5e3ca4cbe56a5c8b6e2db43eee902e7c39f6e665f47d59b3433a5e9ab96ad830fa551364d2f8a843528e54d97bdb2b85cb7c4b7b0e17d8071dd251888bb697d934cad1c71cd252eaacb27e273a7ffe0d1f7248ff6bf6dd56975f2d0ec1471cd2e55aa9214f8467213f296cb000e5030e6915b3599d4ec3174765c4c71b9279b370d9e7baaafd32613254f0451b3edc907ead736c1522eb348a484832cdf0d186848aebb2c9668edf2265435aa7155ae8f17231c8bc3ed6902e765973701d4a737d1b06b3243ed4901022f5766aaf15ff5226ec5b0609c95a39988674cadc2e17b597644c33d040e917e41a3ed090d0c9efe4e54e63fe2d573ece90f8c2f81784ab7df6c6b0000a1f6648db6f8b32f3cdca65aa0cc91c5d8ee905b51e83504a56544ad8e8953d147c9021e9254ffdd3f8c584cfee543ec690b4bd4f5b5d19575c837af021867416d2ebbe98eb57cb921595963e76828f3024be2cc49dcb67dd7a4f140c0911bb5a55adf2da91052658f9cb97e1282726619064e1e30b69d99a5ab37393eb1ce385b4d4b229c3b5e85c5ecedda18f2e24fc3ea4d0cf258d72f70717d2653f59b53e5e7ef1390c1f5b48eab2de24a72237a5a98594ae06d1992d6417eeffc84232f9ea9c936f9acdfac3426a43bdb4ad66cc55f457488ff0317b6126baadfbb04272d5777f8c0daa32721512dae9cb31a78c15b5171552da5ad5a8f5e419b3d0123ea690d621428967dfeef2d852488a95eb31b8f072b98e4521e1e59139632e6b792fcb0f28245dc57941ca67d6d7e113d2390b259ae1457e9688081f4e488bcfd9542ad7f4fe054de1a30929d92cd5bbf8b7da5d9609c98d26373322aaa6dfc712923a6eac780bd5234323840f25a475b8fa3c176abba4f6471292a3eaa2551774b8923f9090b06da9ba2037986f673b3e8e90def7f4f4e5c2767851e7c308c9d1a52f8fb4d9fc5ada0e3e8a90f47c1b26f205edd8d13a7c1021395eec4f23e3e5626a8976858f2124446818a9c163264cf226abc320217993d52b2d01c4f02184b417de4cc54b9d66f4ca8485052424791f41487c177c34a667ed3c0d0889d13269d857cdc528633e7e90982fbca767faca1cb309dfee404961c1ebe0c48ccc6ce3a38db48b4ba54285b6cbcc32e1eb45427b47291ec6e745facb1db3b6cf9b715c8938f0d84532f6baaacedf5317097fd1b1496fe367b07391d6df19b5ce49ed460617e972fced0c1aedcb3983b748ee76bc3dcd71b31e94133550fec0c316e9a065e8cc2cec3dc88d3af0a8455a672ee766c8ffcb74596891f41aedb859bee4d19d454297bb9b7751288b64cc6776f8f8a75ab209dfedf088454265b356f76ad4db8d4cf872071eb048bd5a1311fa0b3b5abe22a14abb3fcb8e9af01daa78b822a9a516fd85771fad37d38a8418cd85f7ce31ba5f2eb278b022e571fb2373cbb0f99955a4c7b5f4a232d11d140f55a0ef99eef9a742383c5291d423a3c8973d74d6bca7818ab4b78ef2dfabf383dee61409339379254abcbd34b3a898b01a1ea64886dff45954de08d598097f71860afe64a5e5c4a314092feb976c46113ab64d8aa4ba6b7e47a9338ae4eafcd8967ab251b71ea24869525de5b2be1dafc66289472892e541ed37a4e8d2a7c7840fc7b79c94b0d12b79667880223922d3557fffa9ce3f8b9ec0e31376177597563db654c04697310212fcc9ca19068f2712afc3e81ca3b92cb3beb034cafd0bc62021d1f56ba04751ea24c5dcc0a31369991d63438516196d9bf0957c8aca211828288dc30c3e1478702299a93674daccf3a3719594db44ba45694ba1553497556d8230be1023055f84f18509d6f0d0443a779459bfd8c16647c72313e92ca42eb8dabc136dbd4a4b0926921b1a1a5c7674f99d9af0a15b316847038f4ba473876dfb1cb3521a858c7795310e8f8146497a5822f19bbc33e4767f41d7267c298d9243b909de55c62069c3a312698d652722e651177438c5831209cf519fc67531df8e1283c72412ef055d1fd733ca7df590443a43577a517fcead0b72203c2291fa2fcad81c36aeca9723820724d25247ddb7215cc62e968f48e8face60eb72af636ec27a7a1e8e48ff68ecb2d3cd05295f133e944e59f9164696151d613cc1a311201e8c60791c9d5212018f45a4367deeec6ab3dfcfd26260e0a188b488d741c897b9b96761c9b1c71e89788007224a20e0718892931c60a89c81010f4370c0a31038560e18e04188b4074d5fce1521f6b72983c7204a1ae0210896ff4bc9053c0241010f40fc5f4a32e0f187f40b1f9151b5acf8a229e90a1e7ee880471fcc830f2a97941314940e630f7ad880471e1242cb155eb28d61930ae121031e7748c6d0fe9cdafb41f7ab24e5528283a5d928f9965e6961c9b443dae3cbf2d587d0aafbacc30e0b78d061021e73a880871c92f26ba5f4911bc4bd748ebc34fa0c334abe65c708c2202161f9961daaa42445e524141e7160d9f16a5cc0030e17f078435297b763fadc233586f4534e140e958567dc90461a7d06073cdac08674a87fa185a84dab6ab386b4accb1632a3c20ba622c1430d16f04843043cd0905c399b53b50be7b24b40789ca144021e668080471992217331a8d5fb9863220949143cc8902e7aafffd7c71e6348f7ca54ed2f6bd15fda3cc490962e9bf57e86cfcc5d3cc290d0450d593e1e939735ed0186a4fb06b939fbdde56290093cbe90d6b2cf7c33857ee9bdbc1d2b87041e5e486a217755cbd8baf75e4cf872241947f0e8424ae37fb8d1b26ff35f0d1e5c48ae3c97e5fd9e77f74507e1b185b4a99c7799353e67565a727868a125e091856446f9a59f17195db4c7030ba915a1b5b6f1e2a6aaf9ea718564ec7241aee61832fea3de0bf4d6202139c14989de1ab972529283453dac30e25185b4dc6aadd098f5476a4df850ba84b1e504c7094a89494b8ef71c785081011e5348eaa031b3e872d1c5ecc1430a094d113a6574d6d4dbd0c1230a490d1ab450d5b71d3ca070c3e3091e1e4e484a4fcf29ef3f695adba3092519f060c2023c96507292038c5ed10106033c94f09f6bddd5d47e12d22fa46b0d19d367f5e2945e2c1e48488af996f9ff357cd0f01192b5ba2fd4abcc08e9723173f98b5aba3c8ac07924cb8308268fa43d8690bcdde41dbaacfc3dac2b2d3954e02104cd23496ccd6695c2566af68f1aa31e4148ec8c10bdd26e20a48b5964e3da98c6d61e8f1f244b4fa44b5d2e2651ee8de1d146625ec5baeebc52ece714d08bb4dc93dbf19cf20b3a2f526fb7c2de6c5c68195e514949e3c4d450c02e029828d55ca972a78e1028243ceaf9763957ea7dc19027a4343de6e26df038afb18438212d5a978554cd6d32e2e0110d214d48e617451763bd85862e6765a59cb01afd30600f6142ea379dabeb4f5fd4e179aba09815842c21994767f11a4f934347891a2a2a28a7055f84812e4409c9f0e263ee15ab0bab83429290ceb221527717d3e7592121e95e546523b39f5f9c51821d9f821c214748ae8fc75721f44648cd77615537486d2fcb22a4f5458a5a9551a98c22212142ba658c511fd35baf78703cb3113284c40bd5d632964acf7934438810529af368cd9e7572a93c214148d7e7ee5ff1b91c8400215dde34ef961bca3765945e791c6f922391906c5ec80fd29963eacfeee66f1a9690369222737e8d6a73685a9d1e905e24cf73e1cfcd44b41095099ff21296b66301630424f05f9dc38c1d9f821e80f022251e5af75b9727f3281306d9454a363d261b4d57542201a28bb49757e3288fef9f63169d2440729174ffe2bc8bd610951dc335b848173db47e979f3fe7f0dc22ad579cca38dd6e19ae09df4a1f455ba40b9a5d2abcbc4bc506a9453a7f31e868ab356669ce195301428b74b963ab79912fd1dd9a453acd973dc8e4eb22527964916e1973a7cdd423bda88f455a56e47a86ce79d5e8608109fe64e58c4980c02271df597ec8117f5e5e9178dd7621753975b88c72455a17766372ffb91dbd1569fbf7283d7b78c72ec88a7469a4bb8ac48bf792cdd947fd7953457aa3f61eb1d9db73414e45ead55fc6f5242374494345f2562a352fca185d5ecf299265b2195e6ece6d9a31455a5c7ee9be3eb8ff079522dd6aa397938d67f93d7b5fcc59bcb347661a06192af8428c32be3848808c22a94cbd5e6bd4a248eb2f672134eb3c14495d9bb5cb79c3cbc5121449ff7266316db539be984fa48ba923eba39427d2e24b22a56be77422599ad2bda0398a13094fd5d62c5bc7e0a93d01d944eaeda59049b3b68dfd494848485872a86822f951ec878c314af5aace4452b9508f32c7161df2020413e92dd7c1c70bfa9578614412805c2229bdfd8ba145688eba5c4cf8cc8d01c41209b9bd1ed353e7c000a944b2c3df9c467ded42eb12c69fac9c61890238020825929a8b494667eaa2bf90555af2f4804c2299bc47d5bf3b3f879624d229a3d6907a7572cd191a209148ab902f235e7a741f2d4824659cf528dee2f58f8e3d22b5a24197bba57cacd025850d9532401c918e9a21d3632ad1cdaf1d208d4887980d52a46726f50da6008411e92f67d617f3df219045a4e45f888989ac789da0b094a034a622d251e698cb31b9f8a266cc9d014944ba18e5a6d90f52991755267c3b9c1191b04df1a1836654afcb99f0d913400e91ceba858c9757a6fc3fd504c4106953f5a2b85eed1bc3cbc1c9a510e9926a1764d43f6eb39010c9d278bebbefbb397c8348c87751031144baa0e1e55e4aa12dfa719040a4b53c17765ea7a770b7e4534e50524e140410498f3777e2255921ef1f1d28207f48e8d7d99e63d681f8216177bb427dbfc6d773258501d287649637cd79dd81f021a12a6e8458a9cb391bfb00c81ed2e5513bcfcd79efcbaa00440fe911ddb19f4147f64be621d9b265ee52fc36d637615b03081ed2eb51befbbe18fd6d84a252c277486ed435c28be1a94b6f8b0e1c6980d821f9a373b54819fb12e100a9436a73355daa2ebb765968c257e2bfa2d24287a4963bde2db5175bd68e6280cc212dbe98396ae3aa7a1153d850d994435ac839aff172c1dbd5207148e6ff0c2b64bcf66584a35756c0202111a38c2f4848c000028784bfb7a62ebc4c7d57544e4a8e05206f48a8dc9e2f17c417438f2e1b06881b52de653942decab421fda1d7cb217a237331c586b4a676175d66b457a9ae2179b251081dd253435ab98d7419524d433a368950ea2af647656848a84d75a37551b72e7ee80c09315f0c21a51cf97214198819d2aea3f36dcb9851d5d9819421f5ba3aaa1ad12985a9eb2a6318261032a4349dbd47818c21f5427ace2cbfe0e582aa023d54021031a43e6ccc0ebada74850a240c292fea0d2e758ffa18bc280e9e04eb5f70f83a70f05eb27e0d4406081892e145cd5efca03c5564be9090ba20a4ee97b3bcc0abb3b70bc9ed7e9dcb9b428d1017840b4953a5bf2fc2c6c45bdb42526cc3beebfabc49935a488f4e1b625ed65c8c51b3902ea7eca2de8e25e2b6cb1440b0903e171ac3ebe8288376b94242675d521d590f55cdfa9395335a74ac00c40ac9d7f7fafb43cbac0bb22a24936ab9e63a95a6f201a1425a85102a3eaf692e6b272b2dd60190292464165f7acf2ea5503e4a216d779b75504ffa65bd45219d3526ffd779224d44298040212da5ccc5f2f845d551b50a8b4134061a252800794242eb28c3cca366962303e284b44eb3f3d2ec8dee5a419a90ece84591c16f0761423a79d45c5ed60f640949b55f325a69f8975e5e09c94c35edbdd956a91e4812d232c377e8a8ff43e612040909d9ef627ed8155ece721b801c211d346d85eb583defc584006284948b76fb38bab5f6d245487e0c5ece30ba1cbfacc54448d87679a41754de8710418690565d8e52a4fd98cb1f0d014408e982fccf396f87792d070942ea3bab501fda9af0edad800021f1a75ef6458db9ecb7a4741a8b4d00f94172533eecdf0a17dfdfda00692329467a280d1a33b588a517494f9e5d0b2147fecb2c2728df7262c28b74b144f5c85ceff2bea8925da4fda3f45c162fd5a1a28b74868d2fbfdc3ddb32978bc4d67fd97483fe393bd358f1435ca4d55b8c90d1753179613e61436f91d2dc25d14595c9b57a69363ee52f6d91103ac4c8f3919f8bafae453a8f4efa625e128d5999f019a44532c71c4397a5a7cbcba282b2d8101fb348ea502ff3e6d1ab367e8c133e64912eaa175ab37ee7c99d86b148fe6a3dad8fd2a58b2eb0486f7ce1652ea67a3df67a455aab6cc67c31724542e7626f487b7735d7c5846fb1091fad48bf561993bc0adb98ae0f56246d447ff96354afeca70746ae22699e52fe0b8f33db5e5491f2f41fd765d6b119ea1615959583858f54a48bb69db9ac2936cb175d34061a25f8818a94ca5c3a95724543954bca09c2f83845c263ce72b2da645f7b3145da57c78cb553add378ff518a74d14ebdf05e50f3f3ba902219e3ecea5ffb7241378a74bcf6cf17225b275f14097f179e212f64bcd55024352653715f72d1ab3550246c3ca716f5aed7518be65a72f089c6c72712eb9e66fe83ebae2f060f173e3c919ab5ff8cb9758d5009de191f9d4877eed477ae7f3fbadc072752f75af966e86c4ea5b0d139586e13c934b2b9689f4bfea2a09ca8a189849ad958f667b29937387c6422a13366d54328d36c1b444c2444abf32f7aae6b501913be96c661887789a4961e67e49f89ed082f5d9277ec04f16189a49ef762561532eb9c3b6af8a844f2a396f1da45d7a8596ec2b7e3d5309448fb7959e7165dd4249259d8dec8caa87551755412a9d77f997378c7a8b5432bf8884432bf2c642ee77afb0d66422229b38ad8d1ed72f218f30d3e1e91f6f2ea77dd348d1d37a7e0c31101f9c60c9145a43cb8b9af58d34ffd2822d92f1ba3d457edfa2c11470f493a173634c82e690e11e9cf1aeac5789d5fccba09df1e2221b2a30e9b44aa6bd672f49b94a8b420ee483939c3dcfa3576a49cbc40c4102971add3c6130f8c535288f4975dcf9cd6a74aa55a121142243744d7a88d26db51fa252b2a2732888498c7525d98dfd579edc5f2253978931511419c9a23496b10cdb2c57f4ca3771a880422a951bce87ad447e8ba80488988ce5d561dd37c528521f28784dea02aef959cfdeb45fc90980d3ae35e182d83585d90a2c28202913e243ecbe6361736113e24cfd3566b6f9c88d6b3ee21a95f7bfef098753ff7480fe92cd5863e0d238508ad2088e421195c7997c53bd7e39b69a49cac941394153c24b57fd62d47ebe8c7fd84848465c7ab4142c27748967a1c2ddf76bc1ac80e6921a5ccfcea97851052133ebc6561f9951c78db262c06db8445c740a3c444ea90eca84b5e7be1bf28e5299d23076f767c0a5a7e65bd192729272b48457935584c840e69fde58e22328a51109943ca5ea68856d3a8af5139247588522927fe3ab42f1287c46f58d1592b028784149bef57e897b9a328f286942bf92f978b35372bef12714352c6d85ece1ea396a3a1494b93414262d2d24242d2226d4806adbc5cf49c5b3e57cd86f4a990325367ed89c81a123247a9215478ec8c9d22a286a4e6dc17579dd385ae59584e5a521ac7d9d1d2f22b398c8e1d2b99868494b1ddf1bf30761a113424de7398f5cfbaac538a24242424bc46e241e40c691d467adab82d4df5ce30435abf94a9a9cbe5cb9074ff20bfa42a3d5dc8c9908e327cb9a4413f654ced189241bc7d14bf311131247454cb149dde95952ed9914365a5c58840240c69ef2feb6c2ea40aade3d53958bee5c404062d272839cc58696111014362a5ad8caad37e3e6d95a49cdc0a0e8a7c219d3b7e08d5ba3a973724e285640a1d3436bdcc9d8ba54817d2b99cfe994196db173c5c487baf4b3dff5cd4a9e35b486ee662f66b86f2d7e5a8a4886821a5e62adf695df5b44d912c24a4be66fa2f684a95970816525ffcb2dcd1b712b942323eedeaf8beafae61112b243f7f84178597937f525521191ff5bc487351fdb9645221a136a45c4f198a4c211d85ae797c350f196e1129a4456711db9ca5abbfe844240a89f3f6a83faafeb2969a3644a090d6713d6ebafd2de42ff28474c96572d95efc449c905ce9abebb6bc77d41dafc6af28a6915206a221d28474c154ebbdf8b22ee6dca791f228286db2b604224c48ae88ed88ba5b2f882a86c812d29e3546dbea91d7d6b9961c272beb3c20a2848412d3cf3c22912424b5a89997761fd587f21c3c33d02b95951616937282b2f238cc1868941c112424f36f75da302e639499b9cbaad16bbd1a660c344abe103942f2f5fd4ff4b5e7b296891821d926e2ba8b42434139b9d88591464a1924249ed22b65ac939426838444a408c97f2fa9d96afb1bad24426a3d3d754b3d0f14448670e9fb5d6aadbf8b8810d2a1735d3feb9769ef8b894810d2d9a6e549a3ca7c41de4b1001423a6f482dbd2cfb5ecc4de3449578ff4a5e0c447e90109a1e84f8b2a88b47c940a48d7497b310cf902eeabc642fd23217663f0adb9ced258e15153170aca85c105e00c410c28b74ac8d51dfcb23f3fb4bc82e929e32175bec26ed1b5d882ed29f5dae97d3349f852717c9d94d5ffc20de105c24d366b7ce5f7cbdb07bc82dd2a3e365d6fe5597e6242139e3597a0424240b89105ba46bf3bfa03947f5af5d6f05a920a416a91321b585cb58cf32bfc649cbfa1ba8ac90b13508a1454af63ca64dbbf18296777e71460912893b187801b845c82c52a37d23d5f676bd8e267c25ebc45354c220213951218305e54fc6f89493cb050c1212961259a4e53f83ab528d695d63c297031555a2c66a14851ea545e9603163a051a2819058a4eee37d3153d67631d8267ce60c43038b948667fcce51aa7770a1e8580109090909a60e12922fc428e30b4a84bc2299c3eb6bd4bc691d3860c15321bdaf96beeec5d5403654726c2b9231ba166a9f2e9e568aa5cfa2c1e4287f228661453ad9fbbff8ca2fae2777ac3c0e95345a525470e8d1206415c991b9eed5b6d6980be3091b2525ae82a3c49411a28a8446bd17b9217388950e964468c65969945e2c0653471a2e21232415297df9be6fa931e1632931782d2c2a2625252ca84508414552455f872e7b9659329f222de47f598cfefd0e5aa770083145d2e64447a9bd5d6ff44a915ef1a729857aae0e27299219a39651480f93b37314a95f5d2cfff3551449fdb73aa2ec5eca8d4391de243a6a7e512a8c3d848022a11ae3175eb81c9b1d8710f289c4ceea4fa9737646d72ddd628627923af996dcc62e280fe66a21a413c98f5ddccebe2527125e2ee7dcf925dff4f10bd944329497ce46bdcf96290949882692f1c6bbb45f7e9bee15928974ebfcdf7ee9adfdd6104c2456c8967175ebdcd63817422e916e196f3b5f109a193d2196486706e1abf7e556339e4a2474d8f672c7f0aad91542896450fea3ad5a7c41563689b46ed032b6345357e69144d2cb1f447b31566aadad0993900ce016219148e6e7da8c2f87171b732190488b6cd17a44c295666f905e0c5e7035c41189f5949717f7d5bc5d421a915a2dee6bb5fdbad04f0823d22722eabdb1ad31be218b48c748d5aba961b4978542149110bd69c42aef820cdb0a494452875c2fc820b3104310910c323d7739b5f010c9a8bab4efb5dc511b443444da75ec7ed9f562e4e7100b91fc6db1a34b6a239ab510ad11291f421c44625e6cdaf788d1c52e280591d0baa875d4ad19c317f7244348209241a98c994e948868901a42009196dde275306f1f8de2cc3fe4aee3bec67c34d30f682eae6fb972bf0f09edbaa94b7ebbbad47f42081f12ae32dd3e9ee66cf443f6908e9a2f745e77e51442f4902e1773946daa06a14727240fe998ba0b1bbe98cb6a552178488a8d7a856a0d2621e40e8917badc3b625e8f3acf0e29cfafbdb52175486c8afa2c65bcb8104287b406a9ba2c5f4c1a57ca750ee9f83ae6a507a194437a4606252bf7d3bb4ea21442e290b8152d7da4e77f2d35040ec9105f76e1b93cbacf3e216f48a917b3bbdcd5fa4799103724c58bedc5609aa395d88684da1c84b87fcda8b5101b92daf2bce0ab74559626640d4917eabcb5967b713d9a19a28684665d3e2f898c42d2906ea952cc8f2ed10e42d090982f08d3f9a2173b44e60c89b37bd9efa1f52d8de3a485e5757062c6454565850cd50b31435267d18c59f59817b5aa0ce9b28e1f3554ebf7a2afc890bef18ce65aba798f38640cc9d4f8059526f7c5b6590ca9794d4de227ee850f8621e99a22e5f686c6f0620e18d2df5a3bf48da99eac4c0a42be909e4f9e8b219ef9bef0c1102fa4858b67078da9bdd8cac141481752ee1f5dd7a58a0bc95c14772334a5561ee2902d24cc36ea188de92084682121f75517b5ce385fab9d21240b497b0d97ab352ca4d3e8a8fc337acc1a5f340e2157489c6673b1efa51b4fe6bc102b24ecbe6caa3f72b3507d4815525f969fb3ed6473fa62ee10428594a6d414ff70324e21f1a20b3a66539952dc2e8584260feabdba5223240a4939031be0eb405a07140d2d809d7ceedf1ab080913093dc04560b8b8e9502046087ea80470058567ee97095951c090001cb498ef608c0220010000074800421620080854591104000ddd24e528000744bbbca0ac901003000072460e54bd458ddc2525212028000dc38400e1c5e48bb4e5ffe1c5da22fbd2ea45b37b83c195555de73e10037b6902c7d995d64f0a8cd5c381e079b1b5ad89163652135775e7cddac5fc63b267c3b722c1c8fc3e0781c7b030b79d2e22a38ae90272d6b9cb88e929204dcb042ea9599b78a321fe19d091f3c1d78387ea5648d132ff1d7c109ba969493644939694139b931d02869c18d2a644909026e50214f5a564c58504a4a1270630a79d28262c2d2252509b821859667c1f1380c70230a295dd71a5c5746ede5dc8042b285101a450ad1395e18b1e45029b10fdc784232668f591daffda9bd091f4aaff17a82432565078a0a19ff621c6411e3861312baf01be2fb4519e489379a90149ef653d6db75baf20613d26557aebd9cebd273883796909cfbd474217478d4e586124ccd9ddc484242751745fb679161e47b030989134fa3fee3c3a8180d1292d7c189cacaeb608f90d62e4d85762f8f16fb1a272befc606378ca06a8ec46f14212133eeb9bc1579199a5625262c3a72a019dc2042d2cbb3aacd65a93bec299fa3a465458505594890f82c2a39709ca4e8b821a4f3bfbc5a97b74f4f3eccc19ba0bb2184f4eace972ff37b4117342d9fc68d20a465ebd605f582d452688e09df49491a8a253780902eebe720377dca848f6f6ffc20217ca4d6de05fd9f5e75a38de478d611b2b79a7b847a91bad7f6e22e3d5ea473597e8a0c62dcc3c7ec22e1a15ef66598b6df175d24458c947a5b8e6afb6c2e92e6b177e631ea9c75595ca47f3edf4517e75ba4cb2a55af8bfce671d716495d2ee9bc7f6294f1c5da2961a4162917f62dbd3d3fa50bd1221923fe445606cd2299aecb625575d57c7ab248c817bdd15506152144b148bba8d995d9bfa35d18162977df2f07ffe2af4898978cbff4f9607ebac2d0b42299d57b369bfdc28a846d9622f37db9ac22a942c6943ad6a88ab497a62eab2f69a9486697299a61350acf172a52db296643d49775a8f3146999f5f6666ee5ce698a745986fc18bb52b7e7bd1449fdb2665d969fedf66d52f0992349d7a77657192a472b7914e92f687ddb0e2251a4cb3c881023b32614c9d59abc752e9fc7389109dfabd16780221d743185bfd2d8d0a34f24eb8b71bfa85aabae1fcf13c9175dd40597596812239d48cab96bd0397eabf81127d2f5bae51edb85fda27a13c9245ff6fbe05f4eed42554d2447c6367bb14bbeebba8e6422253be26c376d3091ceccad3e6bf660afcc4b24b48b9af6346a8994b7ca2b9142ddbdc72b917c155d1be5d39bfc4989c48ecbf55c72fbccba9a444a366a8f0e9d4b22f1dab3d6050d2f1ddd4622a18b9ef37a39ea0289a446e905bf790f7ba327794452caee76a8e6173555e888a44e2bd3977e45eb34ae298c3422b5f1737bc6b05297a3cc0823d21e55674b6bb47fbfa230b288f4e772d17c356dabaa5c1189cd1874798ad3a04bee482292ed27ea11442474337aab50de9b79cb3c445a3ba667cbd1ababb30c91fcdacf6ce75a662152f69bd1c31774c18b5e2e998448b62e677deda5979b32287310c97dd14565ffaa1f4ecf1444da3bf768591b7ff3db2381488fda7b69fe2eae943120d27b9f0b769bc5976f75997f48c917c5338adcb12d35d30f697df7a34ed40b1446fa90d0a0566ea310fd71657c48beac5c2ddd34ea51e6c81ed2326a7ca99a64ce3e5fc2881ed2514b376a6e4eaeb385ba309287f4dbafcaf6ed205de677040f09d52f436a6f1d2e5dd9c6c81d92597ff2d2174584ba50238ed8213532eaf60ff5de59785618a94352dc68be33d39ceef7113a2446870bfd596f640ee92ebc4a5fa9c5237248bfcc85cdf590fa60240e692923f734eae208841138a48ba1bbd5caf4b4d25d78a430f286840c23e63e796183c879c40de95ca92d5ff2d01db65d307aa40d29adcf3faf19852ea7ce081b9873640d099dd94b1f7517a31733460de972996e99ce2a57bb8da42131f29acbaaa3949b2f5a328206e43c921b8c9c21b923b45dbc8ea6bc098b8e1c3cc6881992d9bbf05e2e8698594d4f1992befbeddd05d749bcc5644877ceea9d0baf3463487ec98b59cacc45570cc9a80be24499775851ae8621a1b6f93acf7e6976996048baae97acf8557e2139ca735ca1cb256d6def8817d2f98579a19bf754ea74a40ba9955f2e66a86c2ebcfa5c487ab9b4de397ef8a21e31b7a0984792235a486c77fee6eaebe7e792b390ce526bbedb8ca19b0bc2c4423a68e8b27b695eb367e40ae99dd7f159d363db218c58211d3e978bfa27cabc2896933d2363a40ac9a0b9a0d573d466993c292354489c867818d3d9cfb08d4c21e9e6c530df1de4d9e846a4902e7f505e982f67610d46a290fed845d9e5783f8e402169ba3a65d59e234f486fca74ad95bc469c90f820e3c4eecfd595965a1869425a173f3c0a9d5228bdc4a8008c11b0400525303bc2848497bbbcc2cbe56cdfd11b5942323e97cb52974b7f9f5b3ba284d4e7cdb9e0ba58b3eb45d950098384840d959595208c2421dd2a5e504f5d16e23f348284a4f2fcb934e6922347486b3122e3cbc58c374ace91438711d29e56367e3a11e5e2929567430505478a90f6ce723575dc4688904c59eb256af3c810d2416d0be9517bd176be1121a4f463bb5495b6229e1b09426a4f93eafe8c468090f86287f28dab8b524a7be407492fcbcd6a44cd888e37d246e263da7b596755b789bd48b95cfff89c7c115ea4633cba1ee15f522fc7a0b4c82e923a3533eb3ce28596bb2e925f924f259ffa8bb5ca453a7a327b35e6598468ed40041769d1ef2f46ce97bda37b8bb4ed8a17bcac79923688d8229dedb58f0e792a7366535e0d95334a4e0722b5488b7da9fe69ff3146a6456236c68e41b9ac1fadcb2c125ae8f2db9a4861ef6a13d680882cd259f686a6a9169dc31f8bb42e8677697a864dd350418145529736b393b7d095d2cb0a445e9196eff219e517cf658cebb9221dc4fb5cf4ab15c9122984d87bdd2ed5b2b022a17e2b5abe77c76c6a1509d5418e70a52b3d774a15c9ef726cfdec92ae5a5d2a52e6bac37ed6741f2b2a52b25206d7ca8550a144a748979febf49a2542eaa81a889822e5f14df46ebaac7aedb014a9d3e8c5ecd275d3ebe53b438414692f76aaf8cef15f16916514c96c2da5a24817748ed0bf6f9e316a30100945ba85909d3bacd4ebf9074532bdbad4ca750eba5ffc13e994327afecceb0944bdf8bbc9e74b26914e24367e647edf3861bcfc7256651a3db789a48e394347f5a258f34f13091d56b5aa0873b16215c9446acc0ba3346c2c10c14432e468abefd776b32f7b0956446289946c70ddc5cc5a5e37c6042295489fef8e4e7122f24415a14432c3efcb858777d82f229348a8176d5f545e6c2fb6940b229248a62e66975fcca5f3820e4522251ae74774419eabfc9fe506229048eb147aa4d6e834f2b5931517f023125ff40fcfb0a523ccf2f062c9f6d7083d87641991545efe64aee3dd2b8c5ea8c4302613691c1206845110c4300008cea32000c311082020301a0ec703029960aeab761400035b4c2c4c422e282c208984c2a148241608038140300462180863208a81400c2881d875635647b87ccdbe230f18701ff75e04a8ac4ea45dda0b950564eb7d84cc3f5a0ff2fd64201c5940d937664e1fe2102e0a24c1b8996d8880027a6a33ff9ba473f87fb4888679caee2d399e1ac9b3a3bcdd6e904a6e790bdbefcd298b2fa5dc169d150815c0d50a9a20f2cfed3bc8dfa766116314043773fc366f63c7437e49c3a446133a9976886dfdf6223831855fd0f003efea50866771fbc70c4ea82b73fc74a6ef5d29d23ef1496066efe3fc1c74079951c1e697818e08252041a58cc4e009975d33bff9cda9e2e5ef810f94950497bb3240054e066bdfa042373fe590971e5938f0ca2368f3592432c91b53aafb0d23306aa4e316c5464a70fa1978301d257d1b635224b21a790f7c26a036774ba7daf2931aa7abdf858358fbacbb2525127cfa653f569a87aadf0f785834e031d2f987edba9e8e6040c80ee698d6fa49f8d07785b609b287eaf907eb6694ae884c0bc08f2be8e919d5ada9cd2e176abc236dd6e0d5d8ea4b68148f9ca59f9b4c2062ddaebba436bcad6b7919debafa23edb0b6b23685acf651ec29da3010b5367dc6dc02785e0fc7311cae8d9aa357e922ebbf6be9e93368ff7d8658ad950f666cc66773994e551bc944d8421a7995eafc4b4a61e28858913e65ae30dd672e597adcc02c3eeb0f6c4b1e3e71af8502df63ca717e094b421077abf650cf6330af1f8cd4edb0e90b11790cd64be9a787bd4aae93538f0f24bcfac315b9f71cdf79d38f92e2425945c0ce8155005530c286600c9f6d5b30c90f224209cbaeaa8388ffd2bd07344307ae8804be702b2b9fb8d62d44b3602e34208588209f31b1cbf2a6811b77b128dd1cdf5fdec27059d1218a130e4dee4c03805489132f6de6c015911f944e8d15700d14989726be13f572b872373fed2fa8446e6fb2b02ad9a28fe2dc0ef9a350d55f6008986bdf39bec818c43ff1aa07d3617cc4835515cdc83fdcfbaafd72fa2c680f15112146ec23ce4b99911f82871b067d1641724b44af116d63476a44b7c98c76fd7cc2737ed103c58f63e28bb6022a01d756cdb4903a808518833d0086ca88c15675da0d1628f3c98c1069c10aec9e20320fad47881e4bcc94dba511969a63f664793f73cc4f56841722c296a6cd4fa05737bcefc91d01da6cff0c2a38fa83715bcaf411e75cd71e856759845d5b26758bce73116c56b2e1ddfded07fa5cd1fa3a78729f83ff0afae2946478a08af8d5629bb7c4fd001b0354963657e62be908c0d1021cb51be8803af215c13bf035158a4c117d0157e1a36c40a2e719d89e6aad433585518bf4a06c24c1ede01e807ac5452f28f184305132e187108098bedf2bd1dda989812a6a1bee2027db1c278b834a7787327525af28e66d1675cd1b6cd9b914d0bed84a601a265234dd3014611050f39f635320156fbc501f745321ebf6d3cbb0ed1fc5b29558f7a5898879ddac0a707c65bc30839f21539176c8bc3096241e0c696da6a07af0708559682e63c340fdf6a6ee1548822ee22279c67bfe536160bbf0994e80ab9bcde2b336d4092fc650b79369530bfe6f00c0a53e83d780980791240444dcac1b0a07ebaca7abf25fecd066f5625a6fbceb1918512e6a3477024a7c0e0c31e06cd5f36993c66d59c21b68e72de1e43f65678c3f45602a264845612ea9255fd27d82a1b08bc940cdfca3bcaa1a4e1b9c9e28af4505c0fa495be20df71bb7e90690ec8686b9906a2ffeb69932177482a10f31719bae8163d961d49e3dd83bad6ec121d4dc1e6df3ad20b4596ff4e459b11c328943dd18be8575cec50dea4056b7e3d7370d0bc80f939ed86ebb61333fb58ce2e73719526e26c8cad2e743b42caf27d241682a09a96a9828fb6446c82845c58c12658673ff89d5163b1ab01a65981a03a7410e726ffb8a32581469b0ca342713406d0de2468d54cddc3b431933e070007088d37fcb9f1d96892f6dae5ea7f93093038721e22bd982c4744d058c5379a7db688038b3f14c5a1fc75a9d290d9319b102e292df3fc6813e652cc2f48e29a0a3049917b423df93fef9698b2c469a090ffae68e8c6a7e9daecc1d62766acba3bbc2a38c12337cfc04bb52da8a57db046ddf49a9297096775bb6be05e1fbe78f023763e6c02bb8a13643351508283a95ac2bfbefa7eebb709104cc9baeba66e411e1cf51b76d9814b626bcc4fee007417e4e0022d7053ec6aa630446ec028a091509b64074f00d883138023281385b5cd81cc46f87c2b73880f62e886348b70e815a5cd8e9b9e2487641695be0aa92e6237f0b6569e962fb0e346799a13f2a9c785db815731c351818551bf1c056927002b7dc3b16992d606491b462d9bb03ad55335df11839d0d14c49c6d04cb72c0338005bab7be98dfbadda4d756666f7dc5d54d18d1137bab2ef04e48cf9a7c5acd2829b80b757f9afda71eb086ab8309aa63bb3bca675b609ef133f13210ab11b0d54f3317b49d3885414ad2a4a48ae50c3c491d45acb10ba60f30017ce16419a01d5cecaa922202ce75172a3f5cc6a1d59fc1dc1f0105d8ee72b2af027c77de94f676d5cfcce0b41d61dc28e97fc22920b0cfdfbd6ce2c958de5b2730587d800747cfcae0c311c960e3a6f29cc7383de497be0aa975dbafa84d698611bca406151e9c62f7c1ac857e9eaffbb301d5625d6a6050a0a0f00f59d7d06591115c2f54ca6f400d308dfe7156326d1bf2321af805b112c70b98378024c93bc2cd005fa77e8067967bc4dd5f6036a71a954e299bb1196023b5404c437c7333a1fc07bf276d1f5e823aa87aa22f06a3bfdeaea8411092e395d92c2520a72251b4a281bbedbc6428590cb04ed9218266551f5c6b7df09113938b440bd60f57c03527387ee8c024cb58d7d0326ac6509ec710bea7780d732091a5c465228c629185c6be7769b6b1be64f3482f269b6d4592f001012b49d6cd9383272c47a01f561a5ec7784e0c936a35604a2f794f60943fe3f32ddd66b1077879910f5153a8502aa665219953b7bbb3c38d43778b2ec7e04244f56d068e18594c423d215969d778d33ca9215d01c28678a4744984fd1127d4fa1b5580c35077d0055600229cc4d4d74150ad47447ca2178ccd07b428593caf2a3eb432b43223c41b3260e9eaff5d60d000aedc91ba944f9560c96f232c60e98a2888f39177a7db09013006a2ad016418c30c91cebfb502c7cf474c4a89d311371500b5e28c7608c8a4e9ea24c37ca7f12e8e8d1a0d54c83a49df514d781532767945238a0766a4a13c69ebadac6ffcd9c50420d904d4d2982d0159bf214b4ecf75989be8348a9d98153d10a113170c45f39e74ec6c64c6945556640b06630883db229e1f246d664eee97414999b446fedd17e5649511cc0cd99b510a2004e93f809934fbcf44fcc8ef2b405a5f02400fbe6ffcecfafd607744cc42bb3d3601c2768070551d74354e530f1d2c951d06db5718c8e74a9997f10133fad21a0a3962786f5db88af0d39181847f6d3973160744416baaf23b8a8840c84a612183a165dc5923d4493943610bfced0e0af5aa72fdd00ee435aee27312bc23165832b62eb6515838822a6d9012e850568a7938df0810cc8e8d74443476bdaaac38db0132ddcdff1d6f2c3895d3a34478d3a3a128024fe7a5b019093a7857f5e0c265ef7bda4065af1fd764c605a492039b262157fa160ddd4419ce819b7d70474c25e56aa97a679ca0d0a7d786d25cf3dab7fb76954f01aeb9cfcfcb31c849b120324e5f47a939b337c10bf4fb253d4762dcae01461ab8df66661022977924916c7bc9b6a55a4782fdd3e51eaf2ec835b1e91db1fbab52e9d54990658cfa0c5a66ab8587878157a39585bce70af906d6b3cca382975d3fc5bc6dae39e301edc59c62561501101df094a4ff853dce575d473f6d0ca7eedf721627509d1319faf9c32c49f6b28d5bb3f25f3061b09f7e340497fe17fdb2b748c2629176db3506b831876c6622128ee9616b6beb2552cbf8801947ad629b7cec7498052fcfbac9112246c5d43225d6cc907e17b07e827a28c7efa8c0821d118a39b93de134344141eec6b0942a0e4ca8373d14d4de5a06a5011ec56aca91dc33a4653948860b0e7bd455376affa517d2c9e83e60d548329505f2d8cf33f0d626bb01320ab31dd7ec00778efd756174303ece6706a9c86c937bee7a18bc9bba753c7736d9c47defed902ae71090cf0cedc4be255ee253a1f8149047a1a6bcf7f70e83c94941c32dc23e4048bba96388b5612ae32eb674096da615b18a57e021ecf0be630bce0b675a78ea759172cd11efe72e8017daad6912855f3874863fa8341e43a2d6d99a6e2ef712a40ce9de1c8a77141731a7bc31495fad18beaf2dfff1cb07f0434e804ca08eeb68534590b5022c6046e9108261a9a07416108e8c18851f317339ee91655b985b7816a72cf0a3973e4a9a3d1e2d8b92b526e09107767d7f6cd121f63b29c890444e11bbe32b32b1fe259fa7f1c2b2c98bb4e50aa08dfce23c01c3b719a26129cde71e5606ee9652403f5fe0c1e407fc3db0a5170aafc1641e6dba42838f8c6fcb63044ff59230238a6cad1642233e8de1674cf533505d520e636c713b492acbe4960b231e713183493322f5f83b9008332e087ac8b40bc726e55ff649ef645d3c045e895567c75099d36075b65c63c34ef4807ed0e23a57a8405110ab3e683176afb22bac7ea4382d80e35ff1e031b94caa7f4da11fb165ceacc01b7db53409ea17ae3e0341ff580a443bfa4b8e21ce3f079385286cb925b559944ccf2aca1e8dcd643fc4418939b9c954733c617c7a9ba5d89a4005c98d4d52a5491ad2b61470323c65778d920ed4dd7c7782f4e4732893d929a8ec75ffd6d0fc0d18eff447204c80c141856e04b2d02db4c49ddad31da43a8eb25d3ecb73861e2129734a2c4ee3ba3acd52525395c491d8bd0b3fe0c5abff44dab1574809e10d1e46a581009d586051aa1df97fefc247c0bc16b4464f8d5db09ad064901f7456eb39b0d5de69216df06e535cedff77559b52424bda36a509e8bcab5b34b42ae4614dc7a9f5cea8fd99c79de7f1253c6a631a1d98a6c516eea23173884113b819044c435f618237be93bb0c82e6995e814d2782ebdf5f5c0a138002b0afff91ade2277e828dcdcbc9c383919a3a9ceefb82f57ba8913e6bc2550eb6b1e4693d7310814074eb6208496986cd277396a884955494d9419acf1c77542e99972dc4b8e46a9095185b315f83a0f5e3b11924702c6914f2770166cce8c97830d2d3372208d4b8b57da3208ac8cd3c2ad56d3abb78aba02de82ed58d579e8825301663babdd7a66d0de1b3a427b6e911063957690aeadd47193ce03739d717300f0918492989b7074f1835663551a6c7331849452027d1dd8a2499f8e20c18581813e67f4093afcc34d6a4fd0c4c2123941e3e2c06a925fdabb8a7033d0934bf57d60725ed9dc40b97902ffe301d9fcf9c3448d88432749871f5cfe6fc549d1b75b4a76c6b69401ae21a71a5e791440a922053fe832e04bb7cd59eb08f29d8347593f28838ea5aedadf3bfb42865a7d5a6e01e68b1fa12b7a3bdbcef4fe8cf199d8225caf1987815bfb9c2ae19d0c3af7524018d95e20737961cef3f08ab7275ff9b7a61e0c99e21845ba43c5554ba39840a547027014532444fd46258bd8ab74053f6f3819ca8c2bf58ba28cbfff5088457e867900f77eb17b09652323595ddd976e31d28fe8c6ae6cb94648ba16f235d05a536ba82a4a7f6c29efc0aca00fc4c74198f26e0d37521f28f7161cba255b63b4b97458c44adb9fdbc51bae7e80267851d19b512d3d140a1b7be6001435975c75f293865f66db5e3b984d483d9300db2fadcc23b41fc19b97a4e513907c3f3d8c838bf015e5a3f384e17d4c88d53761cfec5323cd2ff5fd2eed6cbdf36488d12883466b6e232dafc88a4e6bd0ad6caf72cc1fd406138f36f461efce4f3bd9e6d23cb3d47fc4035202fa6e08189cc4daa82cc5efe856324dd069c5491be71714974312a5ab564d4b32216555633c749d5aca1f45a70a561a347777a81b026bc9d5166c1e75b6f3b34081df7dad44e25ac4632cd21303ac74ad4c6e6af94a1029a0bec6188a98b1db61d1a8526e56853dfa40055fd86e4e1d185285c1c56baaf8d03972d473ca8878756d849c6857b17cbd3e3276bf4e2e1c1ceecc5c8fc6e5e0a47745c3fd51f07a3a9c8878a781a3555ec16a0b503cbb0bb7ab73d057212f771783a6c8585ac2c5620c8a54b382c9c6d92b4515662ab5f64368a7dd696f77c4887f26508ea4c82f750caa47041291c2c1e08258af9b4e383c1f84f278d994dfc375e5ed91a8b3b03849312433e9c46c59b00acc63a23b4f049d7eb2cdde918d5a0008358952d47c4f29ae469d7eca9ba64485eed639238deee16a7d709a254a905613e9a5c4b5f4411bd0c795db11977cf8d82d8c8e4cce6920f32f93133fc9b75f20677ae9e052f90dd60ac761cae70eeb113d226eaf176311c7e8731571f8859b78cd86039b0d659d38b03ccee3968af48a8d87f3f6b5b611cd6073e41a069bce5d74a10cf909bd8e45676430e49f6ca16973dfc8d02ee60893028229045bfd8d41b1bd122096ff410bd826c2e408d9269b6960820b244822195ff74c0e2374f7c73145383ba9e4a2a66118c5943232047007e649afd67513214881996af349b81be70c0ea6d4c326b00fe05192340c2e848a43146b10cbe25c416a38d80b11edf3afd71c182694715aa89ab202756f60a2a174cea98fa6daa073ad216a26e6c40d1a1aba3ffa535ca8158ca0424b9f189056151703ecf1acbec8f068133f0a7e5259c64e3701ebbeb04c9cc4a9b0f010ae73a452ceba74c539cbd86f62562d6a3398ca06e61bb31d9bcb548a89da2d3c3cf7f7adea9926a3f1870f2da4e606a7c6564e579f83ec43fb983f0a82815027a2f4974841bca206ff32154556b5547381775595c38271aa5b8a8a0f341cde584015289ea569f515c37bd8a8cc5c5f70c2a84590e14e07974b2bd18d69d3d0b772b48416d456052f98df9c96c9324345aa4a03c55ffe4929820407e8d69eb248dedfc4753b867f7764b383ffc78d48377c7c49bcb8b67f41d55375290a0ab18a440df77d465e8cbda4d33c653a534a91064424090a7e5342819961bfe7dff5d0db3ddeaec85b1c3ace0925702ce88238be3854369f68a28513a986e0e4169b94cb852066385cb3e3e8508726e39b0285d17648f5f7ae68e61052fa2f1354ef85f832c64595d4791033cd76297799f6fc5c150be8d14154a98d5f0e558e0c70f69ef63364b9fdee2bd160356485f596abb96b1302a61a5c9398821fe3932c5aa6a91355ec0ba96960131e5a6d8acb2b3ea8c654d5c7271b06367d9193500d162957b2f1bb3eacfb048e817b985f9c8a3aed9ddadbf2cda1718658f4a25bd69c05218617b325787fd153b170a8874da3031a74330dfdf5beae7e0b09670ec48a2e0237734db4a313fec3c75b93df4f39787a09cf96e4eba2081044bbe7834da18681ba45d8f302304904c561b4f6b65851f1d6d6d318431c95a2e600844ad3ee24cfa0eba94ae93c408da23aff9090a51eb91ab91e12083b18377aa1665eaf30eb91ee8398ebc1c74e0506115d0e76b064e6b7ef186d87f388bdcbae8f365a7d2ae327f3f90c7c39d613b3445be4315e184906035ac1c22a634f8cf9ce8aea7cf8d6b1d3dba83fa018976742ff6f4372d26f3a6c023e744074ef78aeba84e38291202d254551e4726c3c5185a33292341e4222b56e629de813203c839b4e012445849b22edd3135942f4d6e473f7e2dfe752787cacd14a5bc9de32b8d83aa12eac91d39965c8843a6390ff96601bfeb9da58ab831da867693e716269a1d66c381145501196a8b13beddeb5e030a3b595f89b3c87b6b26cb310f0d36f7f0c302ab087704f5c4694e09203c993bd0ebbba399f14f5ce603f41e112466b7ebabb84d724438f8cb97f9ff1f37492a2bfa751b27c4bb5155667264b24b6bb018baf0742f1c3a74f7e5131f54f24b8ef2837fba23844c53fc52841eacf091cfc9a9ca8c93f9675ac515c717386745c681aa40a08315bc9ad8c79706948d8f21b324dea6fbf739ca120edd41be39980fe3d54fdc754ec9aff7978fbd59d63958a69e6918a7885fc3e7eb240c54afe7a0b1abef12c89122738eee1cda555730fe1188a9be0ac10a2fc88aea20ae681d619f9650edbd961d2a85cef414491fc24a9806d087901a26247fae1b572410db416f5e9268b2431b4962668a6b7143e2f59e516d02cdc29daf2ffc74a071c98bb69c445c8844a48d5c9d3bfc4fe3f2f500a937032c25d5049fb0d1742419dca3884289a7c0dd91c55a9bb23d7f7f698e236515eb52002d38399a8298eca195c22228a1d5e5f96f7121e4371f06367f1e8abfbb7aedb08d6e82cacd126dc1fd8ff7767a153a1605e880e55bd742724f99c4ebca80356403c04f6b1bba995d2d665d4562ec1299c04bd94da3a12e4bff4c02531d685ce5bb7ae88be012d79fc83779409f1aa2d71039710191f4f410d2db218c3c1ad8fb53f48bc37ae2adf8f73a7bd57b7005b4f65019d8cad44eb56e11ce9a345ee8c43d3d89d3f8ab7f68c26121bf0b5ebe68d161c10847a8cd0aa67a839def2091ae4f3f4bec7ac62967119bfa915a2a350964ce93a8e42a9780e2044a7a3d14d8cddfafeaabb74cd4f9cf8a8c89c327e74aa8606f97f49751e47687b6e1b38c8713f3868733a038287d4b0817ba56f8fc0a75c8793e9c8f13a2f4b88bfc3fa1bab0d1768c2b99313844a1bb793f5f408d745d7ed5370c48df4f1c7a18d1189516b7967645a67bed2ba23c0509aba4e73c4be9d22858f415157317b444506f4b26da3f58865ca245308229bec930a0b964058c2e78a51afc4920075479314e0b5da1dcbafdfa284add0e6dab0289b3452d8f339d806b17b7d0bc4ba8520c070c7e497b70bcc8b8997e04cd10cda89e3d72369155b1fa1f32372203947303c2b9f9ccdb0608450d7621aed73e22be22c618ebdcfafddce27728581b99f80a0563c55fa9b6a2b2ae756bd90676f2a60779d9ec773370cc14a5b1132108dced1c34b0848d922f1dda44fda6fac31a35d8a854c2d3e5eab0fdc6509ae842a2c2a4b9cfd9e41e0e226090791693bd253389d80ecbc7482867d5bd5e1e5bd6d0a96bc3762c9da38fbb0a37552f261243cd815388ec8ef00cc40c6092f574d3ca64f24de4496f6eccb0a2fd7d099b49b646225bc98d5fa305bd51994d124e677619085672893bd52d8e7b6905b155c65e5fc5aeca02d8629e19953447c8143a80359111b36fa9c4ce73eb6ac982e18f22e0ebe5ead6db1789e9b29552e0f78efb9f9ce634b478154ca8cca2a92425a48dc59a88e9499355244ac1ddb4dc0f65abaf9fbf5e00baa809766a5921f86f22f1fbd35749d34f5374ec77f268f9b3082b1be312886a85b980567165bf420fc4a12464c1b96ad37f6900101a69a00e8e3d141be16b016babaa8d7f3353a1f80c16aa5d0f955fb02c3e4fcd80d653afc8a32bdb74cce935024b42f2ad89134e5cbe6375e1907a2aab0627d56581130832a68de4225f21a95b094013e947958623b1fae7581034d3986ef6c9b9cf14c8b5d5fa4e1599da83ae79f53cab1e712f13f53a7cb7c0db3f9818dea93a936d8cfde62d6ff94ec61580b27a59757fe2eabc3947eef1b7631ba3fa07d95bdf03685604da227b03027cc94215b96cf6c7bd0ca5a84d6183e33b58360dbaf54fdf37fe16002cf0eef09eee375c385127efdb1a21d7d802319b405d3bd4b0acdcf708c4990857159c60b753c409da32886854211e6ea384bc67e0d0187f06aa703b63524ef604a63a5ae1b558a1ba892f40da8a7e869e52296586b55f8c4b0db87af1f62f53ea41d381107a7a2308e639809fdc03fdaa465643fdf95b5da99db0fa03333440cd8d918946f50f35a13b8f03f560e3d22b8cb52952146c4762da860c0f204b4afa7ec1bb4a2e15e57ef056cf546322df80b8ce696489e62c05ceb7e1b7954b9544141e90683807c751645784b2f2f363e7eb74e6e1c007e52e03b0e0fef980bca2e7033ea30d6ea26d154d52ca07c423f4caab424681751ba0db42b8e2bf5b2abc5e6c375d8e4ff5d9dd5c00f4f2f8ac2ece79acfd668ac797821748044b1de4799cc3fabf7b8ef12a83fb912235d5b0274945a175f0738a951198ea5ba13fb72ca3b87c7482484eb7aa7739430501427519498a791566e4a7178800e7b93707e00d1f2a19b01816f20f8734dc8b3ec61f48a7299fd712f2ff2eeef324a528238bb94fcc98c3d2e6a288bd641e12992200688f1d363c3766b4366c0bf1c26a857e584bd04fa3cc36a15447dfdd8c7884f14e2f0a40c0fa806854f932625cd6018b2b458526174174d36a086472a896c0ceadb38dd41f800f8666c92dc36e5761a95276f7df96d21dfa012d44ac8e9effe8cb2ec54d482f893602e5e9da95d049aa242b8b251ba87e3f8115c2b35f0a292b3dac22ab1c222f2ee950cb43be1060f72e64ea741fbd126f4329ead878904393d7599f5f7461fae09b13100103c458da4f649857e4d698a16d03d74ec23eea47fab337ca92cf7ae37f19e4c8b3517cb6b95d9ebb78b36954fe6f381232cb005e13f6a68b511682b94bd377501aefe2f165a2a0e5a1b16dec7b8ff10e4cddccd069dff3a91a55a60bd3a697e0ab17f80653c7f14c9fd98dd1bfc906a4f99703d5d36162ab3d522ef0e851a15f057b0f9ad1dbcb4f83115bb3ae333ed8cfbcd4d16f56124ff603b6d1bf249aad9475e9d95a42645627f45636d0a0d2830e90fa8087aaa9768d4dd0ce9c465e61745a215cf046d0ff72b28e3defb4617d7db17c37bf0363cb88da5b03a06dd3370697e3d8a09ba2f761f8af1a30e3b5a1cfebd4974cf20182cff940d8756841ab7007b372bae7d28bcd1c3e65cf8abaa11f05b5acd561501bdf5077a07ee458d0fa244f596eb99088309f6c93ef1ef8ad712efe75b8b5d2e8108b67c8ed1e263f1ff95c58cf7f50473d4d4824bf8ee8092583f01119e6913348919eaeb9bf626c66194ce06dec23f4ec494a461ee1a46c53f47f2be96e030715b0b9862995c95c578d2a47508d82d4fe072cd1e2152233114764f8846d0c2a933c7ace68437e650c113c084e39de628d253e72c1e5ad1ba48479ce37a5c8ca971ac5b133db5f0ed72ab26f2cb2093db10f22c2775cf747509c1225e2e63789e6582ea49090a14810e392042472a3bcfbaa072810db2887d32869951a0f041bedf0292f534a6ce3fb0ccf727d30e5a951eec112f42bfb79fe477ca4d3691c153a161d14756326053d73b4abb667532dc6609f8340ff030fbb0b274b4fd9c47fd14fc20753c4133695bfd62d3f9d5674e072b1d400705a4f73ba2a700111cc63253898f4ea270d10c9b28a4d3b53b62c4b577b9d68ec6800f1746c124a5b26f46922c2cbfc799f51f95f8cd67579199278627ace6b7d2e8b25310820ff7d553aa4a1bea47b33060583da8fdc4e048887e5fe4a9bdc39a3636301256f5c44e3b61588da6633d9526df8d21eecc0d622366b8ce095dfa85f1c1a9ec054c211c50ce0e605647629cc22df1b9424ae06371876e898e5f02e5303fc162073e5285ab1bb52393c7aa1a415722d45381c80908cb833bc3d5fbb5529dab1db8865138f3d983d2a46fb0a92afb15a1b6085adb02e00bf8cdeb3df416c71c103852f5cc0ebe8b48f1bade303169ae00fac07b7f6b731a3a94721366f3bbcd87a4a1479bf9acf12e246a5f354c5da5a136cada71389be14d77a7233a092fdbc5866787f11e676748da41572be0a35ad65f84a7cdeae24334251213fdf64d75742bfa6bd3a1c91eb8b81aa307c3bf752273dc75db2846f36d2088ddd1616ac6927cc2037cd23fd73f7a75b7d0840fff17ab6987342c14a3be9e32ee75ef4aa90d3270d5c1ac8f88d97b065ee004b69d59fe9d42eef2f60200df93015f641731e75be9d41a0e7ae1e06fadbcb8e70f6013894aa7a15505fe8713a636ead0d2939b81e9253e7c01929915040a6ca747ffb6fd4100224473b8dcd7d72ab1e9f8176be3a9a002401962545302406e6947fe63d6f36e2485afa5b116445c690a70c48f29e3efb81d117f609a7198a47a72bf1e7f04b97fd4b585d74dc18b888124cf15b701f38492e42a8cf4b4bbab9ed25d38d8ddc22fae004bf98da86bcc70d3656c12cbb91b483d06509272192251f7f89bfcdef570b21535c6f53a010d43459ce49d00668e9407446792fd0b5b5f89f358ae286d1180b1c7f8b7ff6ac14e396a628a6dac99c30ea70b029c5a8a7638a017ccb05820251212cdc9254cb391ada6907bc52505cfb54096deeca0708c4bda291817eae0e46688a2a5ce560e669f57d68a509199a0153007d5ff00a6b090a2bda6b51d4dbd29e9fc9b9f1db27c8b0bd24f7dbde59e42f065d7e916b2fbd5091c821c0a841d314a635406bda84634b183171b2e78dedcfb2aec50b2eca64676ceaefa2f58b246e69f4330380a24bb4843db0d1fa020230f0de090822bc3518d84382e148aa6691f7503ef0b8ee71c67be15fa4355cfc10090b260033ac6845ce4d04f7bee281d6fdd4b670cf2e10bcb7a81e956862c81d99baf896b3d5f48de20ae8f8a5829d5ae3e699942610a692a295ddbcecf09b03b778d19d7ba08a29271c341eb34b83197a2783d95c9847beea1ef717d7cf5fecd7250473d4195808ffddf7b870f52b8aefc42b3e89ab9baac3dd13197c1c5ad34e4884cabb1147aea4803d0019aa21f80cf73278e2906185d743f759122e9f3194b8328b8ea58a181eb3869dd187701628dbef97adb404130795b5d795541347f3b1c25eed7edfa8902228941196a9a0f2cfa345aa99bee485832ec0410a2361e44cc951f1917d06d4cbdc957f947b43f7132f5323581c2e3733830e111aea31664f19df8ea6841cb79786abb52500c01103beb827281cd5626a06f2c009c08b157128b68389160e28c09d6c819719c2083cc57f5241443173e8d2657148d07949aca1017af85937e16fe8c14666336d81b8c9b7b85db672a", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x1ce063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a46597283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a2301c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22af0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0x3f1467a096bcd71a5b6a0c8155e20810308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x3f1467a096bcd71a5b6a0c8155e208103f2edf3bdf381debe331ab7446addfdc": "0x000064a7b3b6e00d0000000000000000", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x4dcb50595177a3177648411a42aca0f54e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x1ce063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a46597283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a2301c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22af0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0500", + "0xb8753e9383841da95f7b8871e5de32694e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000000000000000000000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb31a487e6cc5c348d84c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e": "0x1c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb32d8a863b519f21b700f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127": "0xe063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a4659", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3313cc789e5100ca680b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f": "0x4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3624ad9e747f9464220d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606": "0x248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a230", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb368a58d3eb991155a689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63": "0xf0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb39f053c2746e722456610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38": "0xdcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22a", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d21fbce2a623d5a4049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c": "0x7283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507a74cdfcf05a85226175726180f0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b": "0x689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507ae4ba128e21c1c36175726180dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22a": "0x6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507c0de6df75d99ec461757261801c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a15": "0x4c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19508f578a71d93450886175726180248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a230": "0x20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a074c92c894f26b161757261807283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e914": "0x049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a273e8876df6fc8e6175726180e063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a4659": "0x00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d017874cd5fa32f261757261804a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942": "0x80b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c20d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c706064c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e6610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf6380b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x1c00f379b621bd73c45c7d155d2a1fe6a04649e3ece7c7e03b70b3a6242bc7c127e063247ca37058db551a8d99f2f15cfede61fc796acc464a9cdce4c18f6a4659049bec59fb5fe6adea4578250578e89dd7e51ad88c7c92493d6f451c6680925c7283ea6b8648673305a3e06be6dd83b7bc1840081d50d4deef1ce53eba21e91420d8c795eef2620fba2bde74dbc36461c07998ebf600ed265b746c1e05c70606248dbf89d86998772b66900d78e98980ea2afc3c8fe5b93f4b38052f3018a2304c0aa0240b2d7485675e52cdb283a87973652f6acb42c830a5a5faa80f7a707e1c346cb44aa03f8995eeee230970772d6268cd7606740f269bb4e609a01a3a156610a5024c2a5db3d02056d4344d120ec7be283100d71a6715f09275167e4f38dcaa0b4c6840028f6d4fa8c460d5a7d687d1f81c9de453ef2f5ead88767fd22a689e1a66fa33b75f66415021aacc4fa23f49306a3c21407748b8b2d39b4abf63f0d0e90c36f95605510f00a9f0821675bc0c7b70e5c8d113b0426c21d627773b80b6f570f356fef7b891afa2e1c30fca89bc7a2cddd545fd8a173106fce3a11f4a69b6ec0eda668471d806db625681a147efc35a4baeacf0bca95d12d13cd942", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x04000000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file diff --git a/cumulus/parachains/common/src/genesis_config_helpers.rs b/cumulus/parachains/common/src/genesis_config_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..d70b8d5b9c111ca6272bd733b555b34fea56168d --- /dev/null +++ b/cumulus/parachains/common/src/genesis_config_helpers.rs @@ -0,0 +1,47 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Some common helpers for declaring runtime's presets +// note: copied from: cumulus/polkadot-parachain/src/chain_spec/mod.rs + +use crate::{AccountId, Signature}; +#[cfg(not(feature = "std"))] +use alloc::format; +use sp_core::{Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// Helper function to generate a crypto pair from seed. +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account id from seed. +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> ::Public { + get_from_seed::(seed) +} diff --git a/cumulus/parachains/common/src/lib.rs b/cumulus/parachains/common/src/lib.rs index 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/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/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs index 82f86e2b32ef00b50896bbc6208bda059dd8a8b3..5b70ed490c633b7d78558e72762fb60a2f7d0635 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs @@ -70,7 +70,7 @@ pub fn genesis() -> Storage { }, assets: asset_hub_rococo_runtime::AssetsConfig { assets: vec![ - (RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), true, ED), + (RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), false, ED), (USDT_ID, AssetHubRococoAssetOwner::get(), true, ED), ], ..Default::default() @@ -81,7 +81,7 @@ pub fn genesis() -> Storage { ( PenpalTeleportableAssetLocation::get(), PenpalSiblingSovereignAccount::get(), - true, + false, ED, ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 80d2376c6811d7e8f407330dd3c2b02a5260159b..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; @@ -59,5 +59,11 @@ impl_accounts_helpers_for_parachain!(AssetHubRococo); impl_assert_events_helpers_for_parachain!(AssetHubRococo); impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); impl_assets_helpers_for_parachain!(AssetHubRococo); -impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v3::Location); +impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubRococo); +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 fd84030ed13be0b5deeb4490552f0d8c44678ad4..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,13 +20,15 @@ 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, + SAFE_XCM_VERSION, USDT_ID, }; 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"); @@ -65,7 +67,10 @@ pub fn genesis() -> Storage { ..Default::default() }, assets: asset_hub_westend_runtime::AssetsConfig { - assets: vec![(RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), true, ED)], + assets: vec![ + (RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), false, ED), + (USDT_ID, AssetHubWestendAssetOwner::get(), true, USDT_ED), + ], ..Default::default() }, foreign_assets: asset_hub_westend_runtime::ForeignAssetsConfig { @@ -74,7 +79,14 @@ pub fn genesis() -> Storage { ( PenpalTeleportableAssetLocation::get(), PenpalSiblingSovereignAccount::get(), - true, + false, + ED, + ), + // PenpalB's teleportable asset representation + ( + PenpalBTeleportableAssetLocation::get(), + PenpalBSiblingSovereignAccount::get(), + false, ED, ), ], 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 608690218d2f4c439511e508e261fe3e74d37465..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; @@ -59,5 +59,11 @@ impl_accounts_helpers_for_parachain!(AssetHubWestend); impl_assert_events_helpers_for_parachain!(AssetHubWestend); impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); impl_assets_helpers_for_parachain!(AssetHubWestend); -impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v3::Location); +impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubWestend); +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-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/people/people-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs index 43d182facdd5186eb7ad5a770d0bc50d3c1b597b..36a701d24c27e9cda44a06d033332e7f03177d88 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-rococo/src/genesis.rs @@ -18,7 +18,9 @@ use sp_core::storage::Storage; // Cumulus use cumulus_primitives_core::ParaId; -use emulated_integration_tests_common::{build_genesis_storage, collators, SAFE_XCM_VERSION}; +use emulated_integration_tests_common::{ + accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, +}; use parachains_common::Balance; pub const PARA_ID: u32 = 1004; @@ -27,6 +29,9 @@ pub const ED: Balance = testnet_parachains_constants::rococo::currency::EXISTENT pub fn genesis() -> Storage { let genesis_config = people_rococo_runtime::RuntimeGenesisConfig { system: people_rococo_runtime::SystemConfig::default(), + balances: people_rococo_runtime::BalancesConfig { + balances: accounts::init_balances().iter().cloned().map(|k| (k, ED * 4096)).collect(), + }, parachain_info: people_rococo_runtime::ParachainInfoConfig { parachain_id: ParaId::from(PARA_ID), ..Default::default() diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs index 0b99f19bc1309d49f1ae06b78d284c706443936b..942ec1b31d2b46f3b3ec9c253a1c6ee521119b4d 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/people/people-westend/src/genesis.rs @@ -18,7 +18,9 @@ use sp_core::storage::Storage; // Cumulus use cumulus_primitives_core::ParaId; -use emulated_integration_tests_common::{build_genesis_storage, collators, SAFE_XCM_VERSION}; +use emulated_integration_tests_common::{ + accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, +}; use parachains_common::Balance; pub const PARA_ID: u32 = 1004; @@ -27,6 +29,9 @@ pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTEN pub fn genesis() -> Storage { let genesis_config = people_westend_runtime::RuntimeGenesisConfig { system: people_westend_runtime::SystemConfig::default(), + balances: people_westend_runtime::BalancesConfig { + balances: accounts::init_balances().iter().cloned().map(|k| (k, ED * 4096)).collect(), + }, parachain_info: people_westend_runtime::ParachainInfoConfig { parachain_id: ParaId::from(PARA_ID), ..Default::default() diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 260875088bbcb2c68381bd2d6974f98520c5d616..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 @@ -22,11 +22,12 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, Balance}; -use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation}; +use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation, UsdtFromAssetHub}; // Penpal pub const PARA_ID_A: u32 = 2000; pub const PARA_ID_B: u32 = 2001; 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,6 +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 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/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..559a16379bb410150cbf569bfa60a4e99be45cb2 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -17,7 +17,8 @@ pub use codec::{Decode, Encode}; pub use paste; pub use crate::{ - xcm_helpers::xcm_transact_unpaid_execution, PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, + xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, + PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, }; // Substrate @@ -30,7 +31,6 @@ pub use frame_support::{ pub use pallet_assets; pub use pallet_message_queue; pub use pallet_xcm; -use sp_core::Get; // Polkadot pub use polkadot_runtime_parachains::{ @@ -38,7 +38,9 @@ pub use polkadot_runtime_parachains::{ inclusion::{AggregateMessageOrigin, UmpQueueId}, }; pub use xcm::{ - prelude::{Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion}, + prelude::{ + Asset, InteriorLocation, Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion, + }, DoubleEncoded, }; @@ -51,7 +53,7 @@ pub use cumulus_primitives_core::{ }; pub use parachains_common::{AccountId, Balance}; pub use xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, BridgeMessage, + assert_expected_events, bx, helpers::weight_within_threshold, BridgeLaneId, BridgeMessage, BridgeMessageDispatchError, BridgeMessageHandler, Chain, Network, Parachain, RelayChain, TestExt, }; @@ -61,60 +63,60 @@ use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, LaneId, MessageKey, OutboundLaneData, }; -use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; -use pallet_bridge_messages::{Config, OutboundLanes, Pallet}; +pub use bp_xcm_bridge_hub::XcmBridgeHubCall; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, OutboundLanes, Pallet}; pub use pallet_bridge_messages::{ Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2, Instance3 as BridgeMessagesInstance3, }; +use pallet_xcm_bridge_hub::XcmBlobMessageDispatchResult; pub struct BridgeHubMessageHandler { _marker: std::marker::PhantomData<(S, SI, T, TI)>, } struct LaneIdWrapper(LaneId); - -impl From for u32 { - fn from(lane_id: LaneIdWrapper) -> u32 { - u32::from_be_bytes(lane_id.0 .0) +impl From for BridgeLaneId { + fn from(lane_id: LaneIdWrapper) -> BridgeLaneId { + lane_id.0.encode() } } - -impl From for LaneIdWrapper { - fn from(id: u32) -> LaneIdWrapper { - LaneIdWrapper(LaneId(id.to_be_bytes())) +impl From for LaneIdWrapper { + fn from(id: BridgeLaneId) -> LaneIdWrapper { + LaneIdWrapper(LaneId::decode(&mut &id[..]).expect("decodable")) } } impl BridgeMessageHandler for BridgeHubMessageHandler where - S: Config, + S: BridgeMessagesConfig, SI: 'static, - T: Config, + T: BridgeMessagesConfig, TI: 'static, - >::InboundPayload: From>, - >::MessageDispatch: + >::InboundPayload: From>, + >::MessageDispatch: MessageDispatch, { fn get_source_outbound_messages() -> Vec { // get the source active outbound lanes - let active_lanes = S::ActiveOutboundLanes::get(); + let active_outbound_lanes = OutboundLanes::::iter_keys(); let mut messages: Vec = Default::default(); // collect messages from `OutboundMessages` for each active outbound lane in the source - for lane in active_lanes { - let latest_generated_nonce = OutboundLanes::::get(lane).latest_generated_nonce; - let latest_received_nonce = OutboundLanes::::get(lane).latest_received_nonce; + for lane in active_outbound_lanes { + let latest_generated_nonce = + OutboundLanes::::get(lane).unwrap().latest_generated_nonce; + let latest_received_nonce = + OutboundLanes::::get(lane).unwrap().latest_received_nonce; (latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| { - let encoded_payload: Vec = Pallet::::outbound_message_data(*lane, nonce) + let encoded_payload: Vec = Pallet::::outbound_message_data(lane, nonce) .expect("Bridge message does not exist") .into(); let payload = Vec::::decode(&mut &encoded_payload[..]) .expect("Decoding XCM message failed"); - let id: u32 = LaneIdWrapper(*lane).into(); - let message = BridgeMessage { id, nonce, payload }; + let message = BridgeMessage { lane_id: LaneIdWrapper(lane).into(), nonce, payload }; messages.push(message); }); @@ -125,10 +127,10 @@ where fn dispatch_target_inbound_message( message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError> { - type TargetMessageDispatch = >::MessageDispatch; - type InboundPayload = >::InboundPayload; + type TargetMessageDispatch = >::MessageDispatch; + type InboundPayload = >::InboundPayload; - let lane_id = LaneIdWrapper::from(message.id).0; + let lane_id = LaneIdWrapper::from(message.lane_id).0; let nonce = message.nonce; let payload = Ok(From::from(message.payload)); @@ -151,15 +153,16 @@ where result } - fn notify_source_message_delivery(lane_id: u32) { - let data = OutboundLanes::::get(LaneIdWrapper::from(lane_id).0); + fn notify_source_message_delivery(lane_id: BridgeLaneId) { + let lane_id = LaneIdWrapper::from(lane_id).0; + let data = OutboundLanes::::get(lane_id).unwrap(); let new_data = OutboundLaneData { oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1, latest_received_nonce: data.latest_received_nonce + 1, ..data }; - OutboundLanes::::insert(LaneIdWrapper::from(lane_id).0, new_data); + OutboundLanes::::insert(lane_id, new_data); } } @@ -925,3 +928,49 @@ macro_rules! impl_xcm_helpers_for_parachain { } } } + +#[macro_export] +macro_rules! impl_bridge_helpers_for_chain { + ( $chain:ident, $pallet:ident, $pallet_xcm:ident, $runtime_call_wrapper:path ) => { + $crate::impls::paste::paste! { + impl $chain { + /// Open bridge with `dest`. + pub fn open_bridge( + bridge_location: $crate::impls::Location, + bridge_destination_universal_location: $crate::impls::InteriorLocation, + maybe_paid: Option<($crate::impls::Asset, $crate::impls::AccountId)> + ) { + ::execute_with(|| { + use $crate::impls::{bx, Chain}; + use $crate::impls::XcmBridgeHubCall; + use $crate::impls::Encode; + + // important to use `root` and `OriginKind::Xcm` + let root_origin = ::RuntimeOrigin::root(); + + // construct call + let call: $crate::impls::DoubleEncoded<()> = $runtime_call_wrapper(XcmBridgeHubCall::open_bridge { + bridge_destination_universal_location: bx!( + bridge_destination_universal_location.clone().into() + ) + }).encode().into(); + + let xcm = if let Some((fee_asset, beneficiary)) = maybe_paid { + $crate::impls::xcm_transact_paid_execution(call, $crate::impls::OriginKind::Xcm, fee_asset, beneficiary) + } else { + $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Xcm) + }; + + // Send XCM `Transact` with `open_bridge` call + $crate::impls::assert_ok!(]>::$pallet_xcm::send( + root_origin, + bx!(bridge_location.into()), + bx!(xcm), + )); + Self::assert_xcm_pallet_sent(); + }); + } + } + } + } +} diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 7077fbbb0a9aa2d1b572720da448ccae2fe95f03..c6b8889730e51da2c5ec6891d7005c0dbfe3d31e 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -60,17 +60,26 @@ 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! { - pub PenpalTeleportableAssetLocation: xcm::v3::Location - = xcm::v3::Location::new(1, [ - xcm::v3::Junction::Parachain(PENPAL_ID), - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + pub PenpalTeleportableAssetLocation: xcm::v4::Location + = xcm::v4::Location::new(1, [ + xcm::v4::Junction::Parachain(PENPAL_ID), + xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); + 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 @@ -132,6 +141,7 @@ pub mod accounts { pub const EVE_STASH: &str = "Eve//stash"; pub const FERDIE_STASH: &str = "Ferdie//stash"; pub const FERDIE_BEEFY: &str = "Ferdie//stash"; + pub const DUMMY_EMPTY: &str = "JohnDoe"; pub fn init_balances() -> Vec { vec![ diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index b11adacbde5cb6ddfbdf7cf9a1c192aa20ef7584..578bca84ce5a54df1973803888e9d9d8d1cfe511 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -228,7 +228,7 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { $crate::macros::paste::paste! { // init Origin variables let sender = [<$sender_para Sender>]::get(); - let mut para_sender_balance_before = + let para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free; let origin = <$sender_para as $crate::macros::Chain>::RuntimeOrigin::signed(sender.clone()); let assets: Assets = (Parent, $amount).into(); @@ -302,9 +302,6 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after); assert!(relay_receiver_balance_after > relay_receiver_balance_before); - - // Update sender balance - para_sender_balance_before = <$sender_para as $crate::macros::Chain>::account_data_of(sender.clone()).free; } }; } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs index 76179c6d82c6e904b122a42bdc76eec86aa74a09..7a289a3f1ac62383e834645a57080ffa04c00710 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs @@ -23,16 +23,15 @@ use xcm::{prelude::*, DoubleEncoded}; pub fn xcm_transact_paid_execution( call: DoubleEncoded<()>, origin_kind: OriginKind, - native_asset: Asset, + fees: Asset, beneficiary: AccountId, ) -> VersionedXcm<()> { let weight_limit = WeightLimit::Unlimited; let require_weight_at_most = Weight::from_parts(1000000000, 200000); - let native_assets: Assets = native_asset.clone().into(); VersionedXcm::from(Xcm(vec![ - WithdrawAsset(native_assets), - BuyExecution { fees: native_asset, weight_limit }, + WithdrawAsset(fees.clone().into()), + BuyExecution { fees, weight_limit }, Transact { require_weight_at_most, origin_kind, call }, RefundSurplus, DepositAsset { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index 6309c05841078bca03969da2642588f344f5430d..f4fe1478f3ed1c4642d493a0e6ff17b98987dac9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -35,7 +35,9 @@ mod imports { // Cumulus pub use asset_test_utils::xcm_helpers; pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, + accounts::DUMMY_EMPTY, + get_account_id_from_seed, test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, @@ -64,6 +66,7 @@ mod imports { CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, ED as PENPAL_ED, @@ -90,7 +93,6 @@ mod imports { pub const ASSET_ID: u32 = 3; pub const ASSET_MIN_BALANCE: u128 = 1000; - pub type RelayToSystemParaTest = Test; pub type RelayToParaTest = Test; pub type ParaToRelayTest = Test; pub type SystemParaToRelayTest = Test; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 8b9fedcd4947cf5aaef5db0233166c6bc7cbcf21..faff5f7660c20ddcfb3b92fe512a6555bb70b374 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, @@ -493,9 +555,9 @@ fn para_to_para_through_relay_limited_reserve_transfer_assets( ) } -/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work +/// Reserve Transfers of native asset from Relay Chain to the Asset Hub shouldn't work #[test] -fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { +fn reserve_transfer_native_asset_from_relay_to_asset_hub_fails() { // Init values for Relay Chain let signed_origin = ::RuntimeOrigin::signed(RococoSender::get().into()); let destination = Rococo::child_location_of(AssetHubRococo::para_id()); @@ -526,10 +588,10 @@ fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { }); } -/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work +/// Reserve Transfers of native asset from Asset Hub to Relay Chain shouldn't work #[test] -fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_relay_fails() { + // Init values for Asset Hub let signed_origin = ::RuntimeOrigin::signed(AssetHubRococoSender::get().into()); let destination = AssetHubRococo::parent_location(); @@ -691,10 +753,10 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // ========================================================================= // ======= Reserve Transfers - Native Asset - AssetHub<>Parachain ========== // ========================================================================= -/// Reserve Transfers of native asset from System Parachain to Parachain should work +/// Reserve Transfers of native asset from Asset Hub to Parachain should work #[test] -fn reserve_transfer_native_asset_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sender = AssetHubRococoSender::get(); let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; @@ -749,9 +811,9 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } -/// Reserve Transfers of native asset from Parachain to System Parachain should work +/// Reserve Transfers of native asset from Parachain to Asset Hub should work #[test] -fn reserve_transfer_native_asset_from_para_to_system_para() { +fn reserve_transfer_native_asset_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); let sender = PenpalASender::get(); @@ -768,12 +830,12 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { amount_to_send * 2, ); - // Init values for System Parachain + // Init values for Asset Hub let receiver = AssetHubRococoReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - // fund Parachain's SA on System Parachain with the native tokens held in reserve + // fund Parachain's SA on Asset Hub with the native tokens held in reserve AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); // Init Test @@ -824,11 +886,11 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { // ================================================================================== // ======= Reserve Transfers - Native + Non-system Asset - AssetHub<>Parachain ====== // ================================================================================== -/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should +/// Reserve Transfers of a local asset and native asset from Asset Hub to Parachain should /// work #[test] -fn reserve_transfer_assets_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_multiple_assets_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(destination.clone()); let sender = AssetHubRococoSender::get(); @@ -939,10 +1001,12 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should -/// work +/// Reserve Transfers of a random asset and native asset from Parachain to Asset Hub should work +/// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. +/// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, +/// we should do equivalent test with USDT instead of DOT. #[test] -fn reserve_transfer_assets_from_para_to_system_para() { +fn reserve_transfer_multiple_assets_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); let sender = PenpalASender::get(); @@ -965,24 +1029,23 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Fund Parachain's sender account with some foreign assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer.clone(), - asset_location_on_penpal, + asset_location_on_penpal.clone(), sender.clone(), asset_amount_to_send * 2, ); // Fund Parachain's sender account with some system assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer, - system_asset_location_on_penpal, + system_asset_location_on_penpal.clone(), sender.clone(), fee_amount_to_send * 2, ); - // Init values for System Parachain - let receiver = AssetHubRococoReceiver::get(); + // Beneficiary is a new (empty) account + let receiver = get_account_id_from_seed::(DUMMY_EMPTY); + // Init values for Asset Hub let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = RelayLocation::get(); - let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubRococoAssetOwner::get(); let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); @@ -1017,11 +1080,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location.clone(), &sender) + >::balance(asset_location_on_penpal.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubRococo::execute_with(|| { @@ -1038,11 +1101,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal, &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &sender) + >::balance(asset_location_on_penpal, &sender) }); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubRococo::execute_with(|| { @@ -1135,3 +1198,353 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { // Receiver's balance is increased assert!(receiver_assets_after > receiver_assets_before); } + +// ============================================================================ +// ==== Reserve Transfers USDT - AssetHub->Parachain - pay fees using pool ==== +// ============================================================================ +#[test] +fn reserve_transfer_usdt_from_asset_hub_to_para() { + let usdt_id = 1984u32; + let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let penpal_sov_account = AssetHubRococo::sovereign_account_id_of(penpal_location.clone()); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubRococo::fund_accounts(vec![(penpal_sov_account.clone().into(), ASSET_HUB_ROCOCO_ED)]); + + let sender = AssetHubRococoSender::get(); + let receiver = PenpalAReceiver::get(); + let asset_amount_to_send = 1_000_000_000_000; + + AssetHubRococo::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &AssetHubRococoSender::get(), + asset_amount_to_send + 10_000_000_000_000, // Make sure it has enough. + )); + }); + + let relay_asset_penpal_pov = RelayLocation::get(); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. + // So we can swap the custom asset that comes from AssetHubRococo for native asset to pay for + // fees. + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalASender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov), + Box::new(usdt_from_asset_hub.clone()), + // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` + 1_000_000_000_000, + 3_000_000_000_000, + 0, + 0, + PenpalASender::get().into() + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let assets: Assets = vec![( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], + asset_amount_to_send, + ) + .into()] + .into(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + penpal_location, + receiver.clone(), + asset_amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + let sender_initial_balance = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_initial_native_balance = AssetHubRococo::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_initial_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + + test.set_assertion::(system_para_to_para_sender_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); + test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); + test.assert(); + + let sender_after_balance = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_after_native_balance = AssetHubRococo::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_after_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since + // they aren't used for fees. + assert!(sender_after_native_balance < sender_initial_native_balance); + // Sender account's balance decreases. + assert_eq!(sender_after_balance, sender_initial_balance - asset_amount_to_send); + // Receiver account's balance increases. + assert!(receiver_after_balance > receiver_initial_balance); + assert!(receiver_after_balance < receiver_initial_balance + asset_amount_to_send); +} + +// =================================================================================== +// == Reserve Transfers USDT - Parachain->AssetHub->Parachain - pay fees using pool == +// =================================================================================== +// +// Transfer USDT From Penpal A to Penpal B with AssetHub as the reserve, while paying fees using +// USDT by making use of existing USDT pools on AssetHub and destination. +#[test] +fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let asset_amount_to_send: Balance = ROCOCO_ED * 10000; + let fee_amount_to_send: Balance = ROCOCO_ED * 10000; + let sender_chain_as_seen_by_asset_hub = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubRococo::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubRococo::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubRococo::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubRococo::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_ROCOCO_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_ROCOCO_ED), + ]); + + // Give USDT to sov account of sender. + let usdt_id = 1984; + AssetHubRococo::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &sov_of_sender_on_asset_hub.clone().into(), + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let native_asset: Location = Parent.into(); + let usdt = Location::new( + 0, + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], + ); + + // set up pool with USDT <> native pair + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + usdt_id.into(), + AssetHubRococoSender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(native_asset.clone()), + Box::new(usdt.clone()), + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(native_asset), + Box::new(usdt), + 1_000_000_000_000, + 2_000_000_000_000, // usdt is worth half of `native_asset` + 0, + 0, + AssetHubRococoSender::get().into() + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // We also need a pool between WND and USDT on PenpalB. + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset = RelayLocation::get(); + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalBReceiver::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset), + Box::new(usdt_from_asset_hub.clone()), + 1_000_000_000_000, + 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` + 0, + 0, + PenpalBReceiver::get().into() + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // Prepare assets to transfer. + let assets: Assets = + (usdt_from_asset_hub.clone(), asset_amount_to_send + fee_amount_to_send).into(); + // Just to be very specific we're not including anything other than USDT. + assert_eq!(assets.len(), 1); + + // Give the sender enough Relay tokens to pay for local delivery fees. + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we support local delivery fee payment in other assets, we don't need this. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Init Test + let fee_asset_index = 0; + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + test.set_assertion::(para_to_para_through_hop_sender_assertions); + test.set_assertion::(para_to_para_asset_hub_hop_assertions); + test.set_assertion::(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::( + para_to_para_through_asset_hub_limited_reserve_transfer_assets, + ); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // Sender's balance is reduced by amount + assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); + // Receiver's balance is increased + assert!(receiver_assets_after > receiver_assets_before); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index 364fbd0d439f62ed1fce356d1935331ec8e0d90b..29eaa96946436fcafd189adcee9ec0bde6dc3548 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -18,7 +18,7 @@ use crate::imports::*; /// Relay Chain should be able to execute `Transact` instructions in System Parachain /// when `OriginKind::Superuser`. #[test] -fn send_transact_as_superuser_from_relay_to_system_para_works() { +fn send_transact_as_superuser_from_relay_to_asset_hub_works() { AssetHubRococo::force_create_asset_from_relay_as_root( ASSET_ID, ASSET_MIN_BALANCE, @@ -29,28 +29,25 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system asset in the System Parachain -/// - Parachain should be able to create a new Foreign Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using system asset +/// - Parachain should be able to create a new Foreign Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_system_asset() { let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( AssetHubRococo::sibling_location_of(PenpalA::para_id()), ); - let asset_location_on_penpal = v3::Location::new( + let asset_location_on_penpal = Location::new( 0, - [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), - ], + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())], ); let foreign_asset_at_asset_hub = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); // Encoded `create_asset` call to be executed in AssetHub let call = AssetHubRococo::create_foreign_asset_call( - foreign_asset_at_asset_hub, + foreign_asset_at_asset_hub.clone(), ASSET_MIN_BALANCE, para_sovereign_account.clone(), ); @@ -86,12 +83,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubRococo::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubRococo, vec![ @@ -115,15 +107,15 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system assets in the System Parachain -/// - Parachain should be able to create a new Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using sufficient asset +/// - Parachain should be able to create a new Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() { let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( AssetHubRococo::sibling_location_of(PenpalA::para_id()), ); - // Force create and mint assets for Parachain's sovereign account + // Force create and mint sufficient assets for Parachain's sovereign account AssetHubRococo::force_create_and_mint_asset( ASSET_ID, ASSET_MIN_BALANCE, @@ -170,12 +162,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubRococo::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubRococo, vec![ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index 16e0512da960559d192fb427ad55485fd0922172..ac0c90ba198d0542f55412a7763efc6c2f0ee199 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -17,13 +17,10 @@ use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { - let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); - let asset_one = Box::new(v3::Location::new( + let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap()); + let asset_one = Box::new(Location::new( 0, - [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), - ], + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())], )); AssetHubRococo::execute_with(|| { @@ -112,11 +109,11 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); + let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap()); let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap(); + Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap(); let foreign_asset_at_asset_hub_rococo = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); @@ -141,7 +138,7 @@ fn swap_locally_on_chain_using_foreign_assets() { // 1. Mint foreign asset (in reality this should be a teleport or some such) assert_ok!(::ForeignAssets::mint( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()), - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.clone(), sov_penpal_on_ahr.clone().into(), ASSET_HUB_ROCOCO_ED * 3_000_000_000_000, )); @@ -157,7 +154,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_rococo), + Box::new(foreign_asset_at_asset_hub_rococo.clone()), )); assert_expected_events!( @@ -171,7 +168,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_rococo), + Box::new(foreign_asset_at_asset_hub_rococo.clone()), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -189,7 +186,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 4. Swap! - let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_rococo)]; + let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_rococo.clone())]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( @@ -216,7 +213,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::remove_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_rococo), + Box::new(foreign_asset_at_asset_hub_rococo.clone()), 1414213562273 - ASSET_HUB_ROCOCO_ED * 2, // all but the 2 EDs can't be retrieved. 0, 0, @@ -252,8 +249,8 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - Box::new(v3::Location::try_from(asset_native).unwrap()), - Box::new(v3::Location::try_from(asset_one).unwrap()), + Box::new(Location::try_from(asset_native).unwrap()), + Box::new(Location::try_from(asset_one).unwrap()), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); @@ -262,12 +259,12 @@ fn cannot_create_pool_from_pool_assets() { #[test] fn pay_xcm_fee_with_some_asset_swapped_for_native() { - let asset_native = v3::Location::try_from(RelayLocation::get()).unwrap(); - let asset_one = xcm::v3::Location { + let asset_native = Location::try_from(RelayLocation::get()).unwrap(); + let asset_one = Location { parents: 0, interior: [ - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(ASSET_ID.into()), + Junction::PalletInstance(ASSETS_PALLET_ID), + Junction::GeneralIndex(ASSET_ID.into()), ] .into(), }; @@ -296,8 +293,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - Box::new(asset_native), - Box::new(asset_one), + Box::new(asset_native.clone()), + Box::new(asset_one.clone()), )); assert_expected_events!( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs index f74378d7631a610a57c61153e62aedfcb588a611..c8da801a14bff5adea1a695313672dfb9c9d8437 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs @@ -15,53 +15,6 @@ use crate::imports::*; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(631_531_000, 7_186))); - - assert_expected_events!( - Rococo, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Rococo::assert_ump_queue_processed( - true, - Some(AssetHubRococo::para_id()), - Some(Weight::from_parts(307_225_000, 7_186)), - ); - - assert_expected_events!( - Rococo, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { Rococo::assert_ump_queue_processed( false, @@ -92,22 +45,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_dmp_queue_complete(Some(Weight::from_parts(157_718_000, 3593))); - - assert_expected_events!( - AssetHubRococo, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; let system_para_native_asset_location = RelayLocation::get(); @@ -141,7 +78,6 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { ); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); AssetHubRococo::assert_xcmp_queue_success(None); @@ -159,7 +95,7 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { who: *who == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.receiver.account_id, amount: *amount == expected_foreign_asset_amount, }, @@ -173,7 +109,6 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { AssetHubRococo::assert_xcm_pallet_attempted_complete(None); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); assert_expected_events!( AssetHubRococo, vec![ @@ -189,7 +124,7 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { }, // foreign asset is burned locally as part of teleportation RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.sender.account_id, balance: *balance == expected_foreign_asset_amount, }, @@ -232,17 +167,6 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { ); } -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -276,90 +200,41 @@ fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResul ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work #[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = ROCOCO_ED * 1000; - let dest = Rococo::child_location_of(AssetHubRococo::para_id()); - let beneficiary_id = AssetHubRococoReceiver::get(); - let test_args = TestContext { - sender: RococoSender::get(), - receiver: AssetHubRococoReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; +fn teleport_to_other_system_parachains_works() { + let amount = ASSET_HUB_ROCOCO_ED * 100; + let native_asset: Assets = (Parent, amount).into(); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter!( + AssetHubRococo, // Origin + AssetHubRococoXcmConfig, // XCM Configuration + vec![BridgeHubRococo], // Destinations + (native_asset, amount) + ); } -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` #[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - // Init values for Relay Chain - let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000; - let destination = AssetHubRococo::parent_location(); - let beneficiary_id = RococoReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - let test_args = TestContext { - sender: AssetHubRococoSender::get(), - receiver: RococoReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); +fn teleport_from_and_to_relay() { + let amount = ROCOCO_ED * 100; + let native_asset: Assets = (Here, amount).into(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = AssetHubRococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); + test_relay_is_trusted_teleporter!( + Rococo, + RococoXcmConfig, + vec![AssetHubRococo], + (native_asset, amount) + ); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter_for_relay!( + AssetHubRococo, + AssetHubRococoXcmConfig, + Rococo, + amount + ); } /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain @@ -399,19 +274,6 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { assert_eq!(receiver_balance_after, receiver_balance_before); } -#[test] -fn teleport_to_other_system_parachains_works() { - let amount = ASSET_HUB_ROCOCO_ED * 100; - let native_asset: Assets = (Parent, amount).into(); - - test_parachain_is_trusted_teleporter!( - AssetHubRococo, // Origin - AssetHubRococoXcmConfig, // XCM Configuration - vec![BridgeHubRococo], // Destinations - (native_asset, amount) - ); -} - /// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying /// fees using (reserve transferred) native asset. pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs index f8190e11c51c8b202804dcee9cf689d93ef33678..3320392b495d281bf9c127ab299793535362ddcd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs @@ -14,7 +14,10 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; use frame_support::{ dispatch::RawOrigin, sp_runtime::traits::Dispatchable, @@ -161,7 +164,6 @@ fn spend_roc_on_asset_hub() { #[test] fn create_and_claim_treasury_spend_in_usdt() { - const ASSET_ID: u32 = 1984; const SPEND_AMOUNT: u128 = 10_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, PalletInstance(18)); @@ -175,7 +177,7 @@ fn create_and_claim_treasury_spend_in_usdt() { let asset_kind = VersionedLocatableAsset::V3 { location: asset_hub_location, asset_id: v3::AssetId::Concrete( - (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(ASSET_ID.into())).into(), + (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(USDT_ID.into())).into(), ), }; // treasury spend beneficiary. @@ -187,9 +189,9 @@ fn create_and_claim_treasury_spend_in_usdt() { type Assets = ::Assets; // USDT created at genesis, mint some assets to the treasury account. - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); Rococo::execute_with(|| { @@ -231,7 +233,7 @@ fn create_and_claim_treasury_spend_in_usdt() { AssetHubRococo, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -241,7 +243,7 @@ fn create_and_claim_treasury_spend_in_usdt() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); Rococo::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 060c3fb392544b5135eabdbb4f962b82f1f704a7..f568fb4101db12e84153faf08f4b67c82d180f5a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -26,16 +26,15 @@ mod imports { }; // Polkadot - pub use xcm::{ - prelude::{AccountId32 as AccountId32Junction, *}, - v3, - }; + pub use xcm::prelude::{AccountId32 as AccountId32Junction, *}; pub use xcm_executor::traits::TransferType; // Cumulus pub use asset_test_utils::xcm_helpers; pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, + accounts::DUMMY_EMPTY, + get_account_id_from_seed, test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, @@ -65,6 +64,7 @@ mod imports { CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, @@ -90,7 +90,6 @@ mod imports { pub const ASSET_ID: u32 = 3; pub const ASSET_MIN_BALANCE: u128 = 1000; - pub type RelayToSystemParaTest = Test; pub type RelayToParaTest = Test; pub type ParaToRelayTest = Test; pub type SystemParaToRelayTest = Test; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs index 15f4fe33bddc1b65f079832facc4ce495823ad84..9520659712fc625ff479da489ffdae7acbb7d185 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs @@ -14,15 +14,17 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; -use frame_support::traits::fungibles::{Create, Inspect, Mutate}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; +use frame_support::traits::fungibles::{Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; use xcm_executor::traits::ConvertLocation; #[test] fn create_and_claim_treasury_spend() { - const ASSET_ID: u32 = 1984; - const SPEND_AMOUNT: u128 = 1_000_000; + const SPEND_AMOUNT: u128 = 1_000_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, [Parachain(CollectivesWestend::para_id().into()), PalletInstance(65)]); @@ -34,7 +36,7 @@ fn create_and_claim_treasury_spend() { // asset kind to be spent from the treasury. let asset_kind = VersionedLocatableAsset::V4 { location: asset_hub_location, - asset_id: AssetId((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()), + asset_id: AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into()), }; // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); @@ -44,16 +46,10 @@ fn create_and_claim_treasury_spend() { AssetHubWestend::execute_with(|| { type Assets = ::Assets; - // create an asset class and mint some assets to the treasury account. - assert_ok!(>::create( - ASSET_ID, - treasury_account.clone(), - true, - SPEND_AMOUNT / 2 - )); - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + // USDT created at genesis, mint some assets to the fellowship treasury account. + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); CollectivesWestend::execute_with(|| { @@ -96,7 +92,7 @@ fn create_and_claim_treasury_spend() { AssetHubWestend, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -106,7 +102,7 @@ fn create_and_claim_treasury_spend() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); CollectivesWestend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs index 49dfe8d58394c9ae7b69fba75e8147e7fa94e2db..975bacea7b4fd937c33930f6e3d7fc82fe5a179f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs @@ -613,10 +613,10 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { >::balance(roc_at_westend_parachains, &receiver) }); - // Sender's balance is reduced by amount sent plus delivery fees + // Sender's balance is reduced by amount sent. assert!(sender_wnds_after < sender_wnds_before - wnd_to_send); assert_eq!(sender_rocs_after, sender_rocs_before - roc_to_send); - // Sovereign accounts on reserve are changed accordingly + // Sovereign accounts on reserve are changed accordingly. assert_eq!( wnds_in_sender_reserve_on_ah_after, wnds_in_sender_reserve_on_ah_before - wnd_to_send @@ -630,7 +630,7 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { rocs_in_receiver_reserve_on_ah_after, rocs_in_receiver_reserve_on_ah_before + roc_to_send ); - // Receiver's balance is increased + // Receiver's balance is increased by amount sent minus delivery fees. assert!(receiver_wnds_after > receiver_wnds_before); assert_eq!(receiver_rocs_after, receiver_rocs_before + roc_to_send); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 65d013a0eec40aa90b42a475f40dca9197f318e9..53b6939298da39eb9d61a8888cfdf32dd0e8f71e 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,9 +542,22 @@ fn para_to_para_through_relay_limited_reserve_transfer_assets( ) } -/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work +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_system_para_fails() { +fn reserve_transfer_native_asset_from_relay_to_asset_hub_fails() { // Init values for Relay Chain let signed_origin = ::RuntimeOrigin::signed(WestendSender::get().into()); let destination = Westend::child_location_of(AssetHubWestend::para_id()); @@ -526,10 +588,10 @@ fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { }); } -/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work +/// Reserve Transfers of native asset from Asset Hub to Relay Chain shouldn't work #[test] -fn reserve_transfer_native_asset_from_system_para_to_relay_fails() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_relay_fails() { + // Init values for Asset Hub let signed_origin = ::RuntimeOrigin::signed(AssetHubWestendSender::get().into()); let destination = AssetHubWestend::parent_location(); @@ -691,10 +753,10 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // ========================================================================= // ======= Reserve Transfers - Native Asset - AssetHub<>Parachain ========== // ========================================================================= -/// Reserve Transfers of native asset from System Parachain to Parachain should work +/// Reserve Transfers of native asset from Asset Hub to Parachain should work #[test] -fn reserve_transfer_native_asset_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_native_asset_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sender = AssetHubWestendSender::get(); let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 2000; @@ -749,9 +811,9 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } -/// Reserve Transfers of native asset from Parachain to System Parachain should work +/// Reserve Transfers of native asset from Parachain to Asset Hub should work #[test] -fn reserve_transfer_native_asset_from_para_to_system_para() { +fn reserve_transfer_native_asset_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); let sender = PenpalASender::get(); @@ -768,13 +830,13 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { amount_to_send * 2, ); - // Init values for System Parachain + // Init values for Asset Hub let receiver = AssetHubWestendReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - // fund Parachain's SA on System Parachain with the native tokens held in reserve + // fund Parachain's SA on Asset Hub with the native tokens held in reserve AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]); // Init Test @@ -825,11 +887,11 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { // ========================================================================= // ======= Reserve Transfers - Non-system Asset - AssetHub<>Parachain ====== // ========================================================================= -/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should +/// Reserve Transfers of a local asset and native asset from Asset Hub to Parachain should /// work #[test] -fn reserve_transfer_assets_from_system_para_to_para() { - // Init values for System Parachain +fn reserve_transfer_multiple_assets_from_asset_hub_to_para() { + // Init values for Asset Hub let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(destination.clone()); let sender = AssetHubWestendSender::get(); @@ -940,10 +1002,12 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should -/// work +/// Reserve Transfers of a random asset and native asset from Parachain to Asset Hub should work +/// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. +/// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, +/// we should do equivalent test with USDT instead of DOT. #[test] -fn reserve_transfer_assets_from_para_to_system_para() { +fn reserve_transfer_multiple_assets_from_para_to_asset_hub() { // Init values for Parachain let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); let sender = PenpalASender::get(); @@ -966,25 +1030,24 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Fund Parachain's sender account with some foreign assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer.clone(), - asset_location_on_penpal, + asset_location_on_penpal.clone(), sender.clone(), asset_amount_to_send * 2, ); // Fund Parachain's sender account with some system assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer, - system_asset_location_on_penpal, + system_asset_location_on_penpal.clone(), sender.clone(), fee_amount_to_send * 2, ); - // Init values for System Parachain - let receiver = AssetHubWestendReceiver::get(); + // Beneficiary is a new (empty) account + let receiver = get_account_id_from_seed::(DUMMY_EMPTY); + // Init values for Asset Hub let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = RelayLocation::get(); - let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubWestendAssetOwner::get(); let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); @@ -1019,11 +1082,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location.clone(), &sender) + >::balance(asset_location_on_penpal.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubWestend::execute_with(|| { @@ -1040,11 +1103,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location, &sender) + >::balance(system_asset_location_on_penpal, &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &sender) + >::balance(asset_location_on_penpal, &sender) }); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubWestend::execute_with(|| { @@ -1132,8 +1195,360 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { >::balance(relay_native_asset_location, &receiver) }); - // Sender's balance is reduced by amount sent plus delivery fees + // Sender's balance is reduced by amount sent plus delivery fees. assert!(sender_assets_after < sender_assets_before - amount_to_send); + // Receiver's balance is increased by `amount_to_send` minus delivery fees. + assert!(receiver_assets_after > receiver_assets_before); + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); +} + +// ============================================================================ +// ==== Reserve Transfers USDT - AssetHub->Parachain - pay fees using pool ==== +// ============================================================================ +#[test] +fn reserve_transfer_usdt_from_asset_hub_to_para() { + let usdt_id = 1984u32; + let penpal_location = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let penpal_sov_account = AssetHubWestend::sovereign_account_id_of(penpal_location.clone()); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubWestend::fund_accounts(vec![(penpal_sov_account.clone().into(), ASSET_HUB_WESTEND_ED)]); + + let sender = AssetHubWestendSender::get(); + let receiver = PenpalAReceiver::get(); + let asset_amount_to_send = 1_000_000_000_000; + + AssetHubWestend::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &AssetHubWestendSender::get(), + asset_amount_to_send + 10_000_000_000_000, // Make sure it has enough. + )); + }); + + let relay_asset_penpal_pov = RelayLocation::get(); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. + // So we can swap the custom asset that comes from AssetHubWestend for native asset to pay for + // fees. + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalASender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov), + Box::new(usdt_from_asset_hub.clone()), + // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` + 1_000_000_000_000, + 3_000_000_000_000, + 0, + 0, + PenpalASender::get().into() + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let assets: Assets = vec![( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], + asset_amount_to_send, + ) + .into()] + .into(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + penpal_location, + receiver.clone(), + asset_amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + let sender_initial_balance = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_initial_native_balance = AssetHubWestend::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_initial_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + + test.set_assertion::(system_para_to_para_sender_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); + test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); + test.assert(); + + let sender_after_balance = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_after_native_balance = AssetHubWestend::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_after_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since + // they aren't used for fees. + assert!(sender_after_native_balance < sender_initial_native_balance); + // Sender account's balance decreases. + assert_eq!(sender_after_balance, sender_initial_balance - asset_amount_to_send); + // Receiver account's balance increases. + assert!(receiver_after_balance > receiver_initial_balance); + assert!(receiver_after_balance < receiver_initial_balance + asset_amount_to_send); +} + +// =================================================================================== +// == Reserve Transfers USDT - Parachain->AssetHub->Parachain - pay fees using pool == +// =================================================================================== +// +// Transfer USDT From Penpal A to Penpal B with AssetHub as the reserve, while paying fees using +// USDT by making use of existing USDT pools on AssetHub and destination. +#[test] +fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let asset_amount_to_send: Balance = WESTEND_ED * 10000; + let fee_amount_to_send: Balance = WESTEND_ED * 10000; + let sender_chain_as_seen_by_asset_hub = + AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubWestend::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + ]); + + // Give USDT to sov account of sender. + let usdt_id = 1984; + AssetHubWestend::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &sov_of_sender_on_asset_hub.clone().into(), + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let native_asset: Location = Parent.into(); + let usdt = Location::new( + 0, + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], + ); + + // set up pool with USDT <> native pair + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + usdt_id.into(), + AssetHubWestendSender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(native_asset.clone()), + Box::new(usdt.clone()), + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(native_asset), + Box::new(usdt), + 1_000_000_000_000, + 2_000_000_000_000, // usdt is worth half of `native_asset` + 0, + 0, + AssetHubWestendSender::get().into() + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // We also need a pool between WND and USDT on PenpalB. + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset = RelayLocation::get(); + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalBReceiver::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset), + Box::new(usdt_from_asset_hub.clone()), + 1_000_000_000_000, + 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` + 0, + 0, + PenpalBReceiver::get().into() + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // Prepare assets to transfer. + let assets: Assets = + (usdt_from_asset_hub.clone(), asset_amount_to_send + fee_amount_to_send).into(); + // Just to be very specific we're not including anything other than USDT. + assert_eq!(assets.len(), 1); + + // Give the sender enough Relay tokens to pay for local delivery fees. + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we support local delivery fee payment in other assets, we don't need this. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Init Test + let fee_asset_index = 0; + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + test.set_assertion::(para_to_para_through_hop_sender_assertions); + test.set_assertion::(para_to_para_asset_hub_hop_assertions); + test.set_assertion::(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::( + para_to_para_through_asset_hub_limited_reserve_transfer_assets, + ); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // Sender's balance is reduced by amount + assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); // Receiver's balance is increased assert!(receiver_assets_after > receiver_assets_before); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index eb0e985cc0ce6f84318f763cb37e707beaeca718..761c7c12255c5890fd99e53a20b81060e2e2b8c2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -18,7 +18,7 @@ use crate::imports::*; /// Relay Chain should be able to execute `Transact` instructions in System Parachain /// when `OriginKind::Superuser`. #[test] -fn send_transact_as_superuser_from_relay_to_system_para_works() { +fn send_transact_as_superuser_from_relay_to_asset_hub_works() { AssetHubWestend::force_create_asset_from_relay_as_root( ASSET_ID, ASSET_MIN_BALANCE, @@ -29,28 +29,25 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system asset in the System Parachain -/// - Parachain should be able to create a new Foreign Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using system asset +/// - Parachain should be able to create a new Foreign Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_system_asset() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); - let asset_location_on_penpal = v3::Location::new( + let asset_location_on_penpal = Location::new( 0, - [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), - ], + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())], ); let foreign_asset_at_asset_hub = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); // Encoded `create_asset` call to be executed in AssetHub let call = AssetHubWestend::create_foreign_asset_call( - foreign_asset_at_asset_hub, + foreign_asset_at_asset_hub.clone(), ASSET_MIN_BALANCE, para_sovereign_account.clone(), ); @@ -86,12 +83,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubWestend::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubWestend, vec![ @@ -115,15 +107,15 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_system_assets_works() { } /// We tests two things here: -/// - Parachain should be able to send XCM paying its fee with system assets in the System Parachain -/// - Parachain should be able to create a new Asset in the System Parachain +/// - Parachain should be able to send XCM paying its fee at Asset Hub using sufficient asset +/// - Parachain should be able to create a new Asset at Asset Hub #[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { +fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( AssetHubWestend::sibling_location_of(PenpalA::para_id()), ); - // Force create and mint assets for Parachain's sovereign account + // Force create and mint sufficient assets for Parachain's sovereign account AssetHubWestend::force_create_and_mint_asset( ASSET_ID, ASSET_MIN_BALANCE, @@ -170,12 +162,7 @@ fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - + AssetHubWestend::assert_xcmp_queue_success(None); assert_expected_events!( AssetHubWestend, vec![ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index cf429378cf6d8eb79eb0c53a9da1c882fc86537a..1a2821452155db77eca049f490a7ce6cacd873d0 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -18,12 +18,12 @@ use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { let asset_native = - Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works")); - let asset_one = Box::new(v3::Location { + Box::new(Location::try_from(RelayLocation::get()).expect("conversion works")); + let asset_one = Box::new(Location { parents: 0, interior: [ - v3::Junction::PalletInstance(ASSETS_PALLET_ID), - v3::Junction::GeneralIndex(ASSET_ID.into()), + Junction::PalletInstance(ASSETS_PALLET_ID), + Junction::GeneralIndex(ASSET_ID.into()), ] .into(), }); @@ -112,11 +112,11 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); + let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap()); let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion_works"); + Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion_works"); let foreign_asset_at_asset_hub_westend = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); @@ -141,7 +141,7 @@ fn swap_locally_on_chain_using_foreign_assets() { // 1. Mint foreign asset (in reality this should be a teleport or some such) assert_ok!(::ForeignAssets::mint( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()), - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone(), sov_penpal_on_ahr.clone().into(), ASSET_HUB_WESTEND_ED * 3_000_000_000_000, )); @@ -157,7 +157,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_westend), + Box::new(foreign_asset_at_asset_hub_westend.clone()), )); assert_expected_events!( @@ -171,7 +171,7 @@ fn swap_locally_on_chain_using_foreign_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()), asset_native.clone(), - Box::new(foreign_asset_at_asset_hub_westend), + Box::new(foreign_asset_at_asset_hub_westend.clone()), 1_000_000_000_000_000, 2_000_000_000_000_000, 0, @@ -189,7 +189,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 4. Swap! - let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend)]; + let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_westend.clone())]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( @@ -252,8 +252,8 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(v3::Location::try_from(asset_native).expect("conversion works")), - Box::new(v3::Location::try_from(asset_one).expect("conversion works")), + Box::new(Location::try_from(asset_native).expect("conversion works")), + Box::new(Location::try_from(asset_one).expect("conversion works")), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); @@ -262,12 +262,12 @@ fn cannot_create_pool_from_pool_assets() { #[test] fn pay_xcm_fee_with_some_asset_swapped_for_native() { - let asset_native = v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let asset_one = xcm::v3::Location { + let asset_native = Location::try_from(RelayLocation::get()).expect("conversion works"); + let asset_one = Location { parents: 0, interior: [ - xcm::v3::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v3::Junction::GeneralIndex(ASSET_ID.into()), + Junction::PalletInstance(ASSETS_PALLET_ID), + Junction::GeneralIndex(ASSET_ID.into()), ] .into(), }; @@ -296,8 +296,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(asset_native), - Box::new(asset_one), + Box::new(asset_native.clone()), + Box::new(asset_one.clone()), )); assert_expected_events!( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs index a524b87b2daf3a1352af1ea33b64282c2f4a8137..15d39858acca4b099cd0215a55d017b42a695dda 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs @@ -15,53 +15,6 @@ use crate::imports::*; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(631_531_000, 7_186))); - - assert_expected_events!( - Westend, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Westend::assert_ump_queue_processed( - true, - Some(AssetHubWestend::para_id()), - Some(Weight::from_parts(307_225_000, 7_186)), - ); - - assert_expected_events!( - Westend, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { Westend::assert_ump_queue_processed( false, @@ -92,22 +45,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(157_718_000, 3593))); - - assert_expected_events!( - AssetHubWestend, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; let system_para_native_asset_location = RelayLocation::get(); @@ -141,7 +78,6 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { ); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); AssetHubWestend::assert_xcmp_queue_success(None); @@ -159,7 +95,7 @@ fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) { who: *who == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.receiver.account_id, amount: *amount == expected_foreign_asset_amount, }, @@ -173,7 +109,6 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { AssetHubWestend::assert_xcm_pallet_attempted_complete(None); let (expected_foreign_asset_id, expected_foreign_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); - let expected_foreign_asset_id_v3: v3::Location = expected_foreign_asset_id.try_into().unwrap(); assert_expected_events!( AssetHubWestend, vec![ @@ -189,7 +124,7 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { }, // foreign asset is burned locally as part of teleportation RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { - asset_id: *asset_id == expected_foreign_asset_id_v3, + asset_id: *asset_id == expected_foreign_asset_id, owner: *owner == t.sender.account_id, balance: *balance == expected_foreign_asset_amount, }, @@ -232,17 +167,6 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { ); } -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -276,90 +200,41 @@ fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResul ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work #[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = WESTEND_ED * 1000; - let dest = Westend::child_location_of(AssetHubWestend::para_id()); - let beneficiary_id = AssetHubWestendReceiver::get(); - let test_args = TestContext { - sender: WestendSender::get(), - receiver: AssetHubWestendReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Westend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; +fn teleport_to_other_system_parachains_works() { + let amount = ASSET_HUB_WESTEND_ED * 100; + let native_asset: Assets = (Parent, amount).into(); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter!( + AssetHubWestend, // Origin + AssetHubWestendXcmConfig, // XCM Configuration + vec![BridgeHubWestend], // Destinations + (native_asset, amount) + ); } -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` #[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - // Init values for Relay Chain - let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; - let destination = AssetHubWestend::parent_location(); - let beneficiary_id = WestendReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - let test_args = TestContext { - sender: AssetHubWestendSender::get(), - receiver: WestendReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); +fn teleport_from_and_to_relay() { + let amount = WESTEND_ED * 100; + let native_asset: Assets = (Here, amount).into(); - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = AssetHubWestend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); + test_relay_is_trusted_teleporter!( + Westend, + WestendXcmConfig, + vec![AssetHubWestend], + (native_asset, amount) + ); - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); + test_parachain_is_trusted_teleporter_for_relay!( + AssetHubWestend, + AssetHubWestendXcmConfig, + Westend, + amount + ); } /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain @@ -399,19 +274,6 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { assert_eq!(receiver_balance_after, receiver_balance_before); } -#[test] -fn teleport_to_other_system_parachains_works() { - let amount = ASSET_HUB_WESTEND_ED * 100; - let native_asset: Assets = (Parent, amount).into(); - - test_parachain_is_trusted_teleporter!( - AssetHubWestend, // Origin - AssetHubWestendXcmConfig, // XCM Configuration - vec![BridgeHubWestend], // Destinations - (native_asset, amount) - ); -} - /// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying /// fees using (reserve transferred) native asset. pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs index 8cbce3e0d223277e240e159e99f76696b7b339be..b70967184387a868b4bd2b49970f8e16d418d62d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs @@ -14,15 +14,17 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; -use frame_support::traits::fungibles::{Create, Inspect, Mutate}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; +use frame_support::traits::fungibles::{Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; use xcm_executor::traits::ConvertLocation; #[test] fn create_and_claim_treasury_spend() { - const ASSET_ID: u32 = 1984; - const SPEND_AMOUNT: u128 = 1_000_000; + const SPEND_AMOUNT: u128 = 1_000_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, PalletInstance(37)); // treasury account on a sibling parachain. @@ -33,7 +35,7 @@ fn create_and_claim_treasury_spend() { // asset kind to be spend from the treasury. let asset_kind = VersionedLocatableAsset::V4 { location: asset_hub_location, - asset_id: AssetId([PalletInstance(50), GeneralIndex(ASSET_ID.into())].into()), + asset_id: AssetId([PalletInstance(50), GeneralIndex(USDT_ID.into())].into()), }; // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); @@ -43,16 +45,10 @@ fn create_and_claim_treasury_spend() { AssetHubWestend::execute_with(|| { type Assets = ::Assets; - // create an asset class and mint some assets to the treasury account. - assert_ok!(>::create( - ASSET_ID, - treasury_account.clone(), - true, - SPEND_AMOUNT / 2 - )); - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + // USDT created at genesis, mint some assets to the treasury account. + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); Westend::execute_with(|| { @@ -94,7 +90,7 @@ fn create_and_claim_treasury_spend() { AssetHubWestend, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -104,7 +100,7 @@ fn create_and_claim_treasury_spend() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); Westend::execute_with(|| { diff --git a/cumulus/parachains/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..86ace7d564e8a877016ab350293f1dba94337592 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml @@ -31,6 +31,7 @@ xcm-executor = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } @@ -38,7 +39,7 @@ emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-system-emulated-network = { workspace = true } rococo-westend-system-emulated-network = { workspace = true } -testnet-parachains-constants = { features = ["rococo"], workspace = true, default-features = true } +testnet-parachains-constants = { features = ["rococo", "westend"], workspace = true, default-features = true } # Snowbridge snowbridge-core = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 3ee509389c67ccb5105f249667815aa587b1d29d..ac08e48ded68dacad27756ff62b55caf6a1a7f32 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -23,7 +23,8 @@ mod imports { pub use xcm::{ latest::ParentThen, prelude::{AccountId32 as AccountId32Junction, *}, - v3::{self, NetworkId::Westend as WestendId}, + v4, + v4::NetworkId::Westend as WestendId, }; pub use xcm_executor::traits::TransferType; @@ -31,7 +32,8 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::ALICE, impls::Inspect, - test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, + test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, @@ -59,15 +61,20 @@ mod imports { }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, }, - rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, + rococo_emulated_chain::{ + genesis::ED as ROCOCO_ED, rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig, + RococoRelayPallet as RococoPallet, + }, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, + BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend, PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, - RococoRelay as Rococo, + RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, + RococoRelaySender as RococoSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 6053936487b26e97fe8575cd5b666a03cff7ccf6..6df51c5f7048aac5e4a16959acb01ad3986088b2 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(); @@ -36,10 +39,10 @@ fn send_assets_over_bridge(send_fn: F) { fn set_up_rocs_for_penpal_rococo_through_ahr_to_ahw( sender: &AccountId, amount: u128, -) -> (Location, v3::Location) { +) -> (Location, v4::Location) { let roc_at_rococo_parachains = roc_at_ah_rococo(); - let roc_at_asset_hub_westend = bridged_roc_at_ah_westend().try_into().unwrap(); - create_foreign_on_ah_westend(roc_at_asset_hub_westend, true); + let roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); + create_foreign_on_ah_westend(roc_at_asset_hub_westend.clone(), true); let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location); @@ -121,11 +124,11 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { let amount = ASSET_HUB_ROCOCO_ED * 1_000_000; let sender = AssetHubRococoSender::get(); let receiver = AssetHubWestendReceiver::get(); - let roc_at_asset_hub_rococo: v3::Location = roc_at_ah_rococo().try_into().unwrap(); - let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend().try_into().unwrap(); + let roc_at_asset_hub_rococo = roc_at_ah_rococo(); + let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); - create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend, true); - set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend); + create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); + set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend.clone()); //////////////////////////////////////////////////////////// // Let's first send over just some ROCs as a simple example @@ -138,12 +141,13 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { ::account_data_of(sov_ahw_on_ahr.clone()).free; let sender_rocs_before = ::account_data_of(sender.clone()).free; let receiver_rocs_before = - foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend, &receiver); + foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), &receiver); // send ROCs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_westend_location(); - let assets: Assets = (Location::try_from(roc_at_asset_hub_rococo).unwrap(), amount).into(); + let assets: Assets = + (Location::try_from(roc_at_asset_hub_rococo.clone()).unwrap(), amount).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_rococo(destination, assets, fee_idx)); }); @@ -185,9 +189,9 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { ///////////////////////////////////////////////////////////// let usdt_at_asset_hub_rococo = usdt_at_ah_rococo(); - let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend().try_into().unwrap(); + let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend(); // wETH has same relative location on both Rococo and Westend AssetHubs - let bridged_weth_at_ah = weth_at_asset_hubs().try_into().unwrap(); + let bridged_weth_at_ah = weth_at_asset_hubs(); // mint USDT in sender's account (USDT already created in genesis) AssetHubRococo::mint_asset( @@ -197,19 +201,23 @@ fn send_roc_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { amount * 2, ); // create wETH at src and dest and prefund sender's account - create_foreign_on_ah_rococo(bridged_weth_at_ah, true, vec![(sender.clone(), amount * 2)]); - create_foreign_on_ah_westend(bridged_weth_at_ah, true); - create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend, true); - set_up_pool_with_wnd_on_ah_westend(bridged_usdt_at_asset_hub_westend); + create_foreign_on_ah_rococo( + bridged_weth_at_ah.clone(), + true, + vec![(sender.clone(), amount * 2)], + ); + create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true); + create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), true); + set_up_pool_with_wnd_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone()); let receiver_usdts_before = - foreign_balance_on_ah_westend(bridged_usdt_at_asset_hub_westend, &receiver); - let receiver_weth_before = foreign_balance_on_ah_westend(bridged_weth_at_ah, &receiver); + foreign_balance_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), &receiver); + let receiver_weth_before = foreign_balance_on_ah_westend(bridged_weth_at_ah.clone(), &receiver); // send USDTs and wETHs let assets: Assets = vec![ (usdt_at_asset_hub_rococo.clone(), amount).into(), - (Location::try_from(bridged_weth_at_ah).unwrap(), amount).into(), + (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount).into(), ] .into(); // use USDT for fees @@ -258,9 +266,8 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { let sender = AssetHubRococoSender::get(); let receiver = AssetHubWestendReceiver::get(); let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); - let wnd_at_asset_hub_rococo_v3 = wnd_at_asset_hub_rococo.clone().try_into().unwrap(); let prefund_accounts = vec![(sender.clone(), prefund_amount)]; - create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo_v3, true, prefund_accounts); + create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), true, prefund_accounts); // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( @@ -273,14 +280,14 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { ::account_data_of(sov_ahr_on_ahw.clone()).free; assert_eq!(wnds_in_reserve_on_ahw_before, prefund_amount); - let sender_wnds_before = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo_v3, &sender); + let sender_wnds_before = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), &sender); assert_eq!(sender_wnds_before, prefund_amount); let receiver_wnds_before = ::account_data_of(receiver.clone()).free; // send back WNDs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_westend_location(); - let assets: Assets = (wnd_at_asset_hub_rococo, amount_to_send).into(); + let assets: Assets = (wnd_at_asset_hub_rococo.clone(), amount_to_send).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_rococo(destination, assets, fee_idx)); }); @@ -309,7 +316,7 @@ fn send_back_wnds_from_asset_hub_rococo_to_asset_hub_westend() { ); }); - let sender_wnds_after = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo_v3, &sender); + let sender_wnds_after = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo, &sender); let receiver_wnds_after = ::account_data_of(receiver).free; let wnds_in_reserve_on_ahw_after = ::account_data_of(sov_ahr_on_ahw).free; @@ -341,7 +348,8 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() type ForeignAssets = ::ForeignAssets; >::balance(roc_at_rococo_parachains.clone(), &sender) }); - let receiver_rocs_before = foreign_balance_on_ah_westend(roc_at_asset_hub_westend, &receiver); + let receiver_rocs_before = + foreign_balance_on_ah_westend(roc_at_asset_hub_westend.clone(), &receiver); // Send ROCs over bridge { @@ -372,7 +380,7 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() vec![ // issue ROCs on AHW RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == roc_at_rococo_parachains.clone().try_into().unwrap(), + asset_id: *asset_id == roc_at_rococo_parachains.clone(), owner: owner == &receiver, }, // message processed successfully @@ -403,7 +411,6 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() #[test] fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() { let wnd_at_rococo_parachains = bridged_wnd_at_ah_rococo(); - let wnd_at_rococo_parachains_v3 = wnd_at_rococo_parachains.clone().try_into().unwrap(); let amount = ASSET_HUB_ROCOCO_ED * 10_000_000; let sender = PenpalASender::get(); let receiver = AssetHubWestendReceiver::get(); @@ -416,7 +423,7 @@ fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_weste let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location); let prefund_accounts = vec![(sov_penpal_on_ahr, amount * 2)]; - create_foreign_on_ah_rococo(wnd_at_rococo_parachains_v3, true, prefund_accounts); + create_foreign_on_ah_rococo(wnd_at_rococo_parachains.clone(), true, prefund_accounts); let asset_owner: AccountId = AssetHubRococo::account_id_of(ALICE); PenpalA::force_create_foreign_asset( wnd_at_rococo_parachains.clone(), diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index ceccf98a0240dade42b8a00372e1ee8737cf1e03..b540f55642a5f13fcdceba1594f7c849bcbc3df8 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)]) } @@ -69,7 +72,7 @@ pub(crate) fn weth_at_asset_hubs() -> Location { } pub(crate) fn create_foreign_on_ah_rococo( - id: v3::Location, + id: v4::Location, sufficient: bool, prefund_accounts: Vec<(AccountId, u128)>, ) { @@ -78,18 +81,18 @@ pub(crate) fn create_foreign_on_ah_rococo( AssetHubRococo::force_create_foreign_asset(id, owner, sufficient, min, prefund_accounts); } -pub(crate) fn create_foreign_on_ah_westend(id: v3::Location, sufficient: bool) { +pub(crate) fn create_foreign_on_ah_westend(id: v4::Location, sufficient: bool) { let owner = AssetHubWestend::account_id_of(ALICE); AssetHubWestend::force_create_foreign_asset(id, owner, sufficient, ASSET_MIN_BALANCE, vec![]); } -pub(crate) fn foreign_balance_on_ah_rococo(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_rococo(id: v4::Location, who: &AccountId) -> u128 { AssetHubRococo::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) -> u128 { AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) @@ -97,8 +100,8 @@ pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) - } // set up pool -pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v3::Location) { - let wnd: v3::Location = v3::Parent.into(); +pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v4::Location) { + let wnd: v4::Location = v4::Parent.into(); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; let owner = AssetHubWestendSender::get(); @@ -106,14 +109,14 @@ pub(crate) fn set_up_pool_with_wnd_on_ah_westend(foreign_asset: v3::Location) { assert_ok!(::ForeignAssets::mint( signed_owner.clone(), - foreign_asset.into(), + foreign_asset.clone().into(), owner.clone().into(), 3_000_000_000_000, )); assert_ok!(::AssetConversion::create_pool( signed_owner.clone(), - Box::new(wnd), - Box::new(foreign_asset), + Box::new(wnd.clone()), + Box::new(foreign_asset.clone()), )); assert_expected_events!( AssetHubWestend, @@ -210,3 +213,57 @@ pub(crate) fn assert_bridge_hub_westend_message_received() { ); }) } + +pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { + use testnet_parachains_constants::{ + rococo::currency::UNITS as ROC, westend::currency::UNITS as WND, + }; + + // open AHR -> AHW + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); + AssetHubRococo::open_bridge( + AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), + [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + Some(( + (roc_at_ah_rococo(), ROC * 1).into(), + BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( + AssetHubRococo::para_id(), + )), + )), + ); + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmOverBridgeHubWestend( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); + + // open AHW -> AHR + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); + AssetHubWestend::open_bridge( + AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), + [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + Some(( + (wnd_at_ah_westend(), WND * 1).into(), + BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( + AssetHubWestend::para_id(), + )), + )), + ); + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmOverBridgeHubRococo( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index 652447fa56010918d8816187207bc86e2ae99ea5..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 @@ -29,7 +29,7 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable let xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { - network: WestendId.into(), + network: WestendId, destination: [Parachain(AssetHubWestend::para_id().into())].into(), xcm: remote_xcm, }, @@ -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 40a1968ec557bd99730fd27e151a4aa9f2f2cfd9..84328fb7c6d201b86edf9aba954d84b7598e3005 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -560,7 +560,6 @@ fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u12 2, [EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }], ); - // (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }) // Fund asset hub sovereign on bridge hub let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( 1, @@ -669,8 +668,8 @@ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficie #[test] fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed( ) { - // On AH the xcm fee is 33_873_024 and the ED is 3_300_000 - send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 36_000_000); + // On AH the xcm fee is 26_789_690 and the ED is 3_300_000 + send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 30_000_000); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs index 1fb03748d926c9a422ede319977c5e39c0f24b16..8cdd9613dc52126ea134ed69ced701dbb0e5f744 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/teleport.rs @@ -27,3 +27,23 @@ fn teleport_to_other_system_parachains_works() { (native_asset, amount) ); } + +#[test] +fn teleport_from_and_to_relay() { + let amount = ROCOCO_ED * 100; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Rococo, + RococoXcmConfig, + vec![BridgeHubRococo], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + BridgeHubRococo, + BridgeHubRococoXcmConfig, + Rococo, + amount + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index 1f2d2c8ece2437fee863a888713ae8d868359b84..44121cbfdafbdcfd24890b751cd6694d55b83e9d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -32,13 +32,14 @@ xcm-executor = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-westend-system-emulated-network = { workspace = true } -testnet-parachains-constants = { workspace = true, features = ["westend"] } +testnet-parachains-constants = { features = ["rococo", "westend"], workspace = true, default-features = true } asset-hub-westend-runtime = { workspace = true } bridge-hub-westend-runtime = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 782b83bac4754ba6888b8d513a13887d5cb7ecf3..5e0462d14882a9922c8d6232dd7ebe781ca5c88a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -23,8 +23,7 @@ mod imports { pub use xcm::{ latest::ParentThen, prelude::{AccountId32 as AccountId32Junction, *}, - v3, - v4::NetworkId::Rococo as RococoId, + v4::{self, NetworkId::Rococo as RococoId}, }; pub use xcm_executor::traits::TransferType; @@ -32,7 +31,8 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::ALICE, impls::Inspect, - test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, + test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, @@ -55,14 +55,19 @@ mod imports { penpal_runtime::xcm_config::UniversalLocation as PenpalUniversalLocation, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, }, - westend_emulated_chain::WestendRelayPallet as WestendPallet, + westend_emulated_chain::{ + genesis::ED as WESTEND_ED, westend_runtime::xcm_config::XcmConfig as WestendXcmConfig, + WestendRelayPallet as WestendPallet, + }, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, BridgeHubWestendPara as BridgeHubWestend, + BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, PenpalBParaSender as PenpalBSender, WestendRelay as Westend, + WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index 0c0b04cd45a91c42592052799ba9580bd6e4219a..c3f81175da23f8716efc4e19db0a429d1cb4d2f4 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(); @@ -35,10 +38,10 @@ fn send_assets_over_bridge(send_fn: F) { fn set_up_wnds_for_penpal_westend_through_ahw_to_ahr( sender: &AccountId, amount: u128, -) -> (Location, v3::Location) { +) -> (Location, v4::Location) { let wnd_at_westend_parachains = wnd_at_ah_westend(); - let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo().try_into().unwrap(); - create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo, true); + let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); + create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), true); let penpal_location = AssetHubWestend::sibling_location_of(PenpalB::para_id()); let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of(penpal_location); @@ -116,10 +119,10 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { let sender = AssetHubWestendSender::get(); let receiver = AssetHubRococoReceiver::get(); let wnd_at_asset_hub_westend = wnd_at_ah_westend(); - let bridged_wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo().try_into().unwrap(); - create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, true); + let bridged_wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); + create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); - set_up_pool_with_roc_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, true); + set_up_pool_with_roc_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( Rococo, @@ -129,7 +132,7 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { ::account_data_of(sov_ahr_on_ahw.clone()).free; let sender_wnds_before = ::account_data_of(sender.clone()).free; let receiver_wnds_before = - foreign_balance_on_ah_rococo(bridged_wnd_at_asset_hub_rococo, &receiver); + foreign_balance_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), &receiver); // send WNDs, use them for fees send_assets_over_bridge(|| { @@ -187,10 +190,8 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { let sender = AssetHubWestendSender::get(); let receiver = AssetHubRococoReceiver::get(); let bridged_roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); - let bridged_roc_at_asset_hub_westend_v3 = - bridged_roc_at_asset_hub_westend.clone().try_into().unwrap(); let prefund_accounts = vec![(sender.clone(), prefund_amount)]; - create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend_v3, true, prefund_accounts); + create_foreign_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true, prefund_accounts); //////////////////////////////////////////////////////////// // Let's first send back just some ROCs as a simple example @@ -208,14 +209,14 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { assert_eq!(rocs_in_reserve_on_ahr_before, prefund_amount); let sender_rocs_before = - foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend_v3, &sender); + foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), &sender); assert_eq!(sender_rocs_before, prefund_amount); let receiver_rocs_before = ::account_data_of(receiver.clone()).free; // send back ROCs, use them for fees send_assets_over_bridge(|| { let destination = asset_hub_rococo_location(); - let assets: Assets = (bridged_roc_at_asset_hub_westend, amount_to_send).into(); + let assets: Assets = (bridged_roc_at_asset_hub_westend.clone(), amount_to_send).into(); let fee_idx = 0; assert_ok!(send_assets_from_asset_hub_westend(destination, assets, fee_idx)); }); @@ -245,7 +246,7 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { }); let sender_rocs_after = - foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend_v3, &sender); + foreign_balance_on_ah_westend(bridged_roc_at_asset_hub_westend, &sender); let receiver_rocs_after = ::account_data_of(receiver.clone()).free; let rocs_in_reserve_on_ahr_after = ::account_data_of(sov_ahw_on_ahr.clone()).free; @@ -262,14 +263,14 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { ////////////////////////////////////////////////////////////////// // wETH has same relative location on both Rococo and Westend AssetHubs - let bridged_weth_at_ah = weth_at_asset_hubs().try_into().unwrap(); - let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend().try_into().unwrap(); + let bridged_weth_at_ah = weth_at_asset_hubs(); + let bridged_usdt_at_asset_hub_westend = bridged_usdt_at_ah_westend(); // set up destination chain AH Rococo: // create a ROC/USDT pool to be able to pay fees with USDT (USDT created in genesis) - set_up_pool_with_roc_on_ah_rococo(usdt_at_ah_rococo().try_into().unwrap(), false); + set_up_pool_with_roc_on_ah_rococo(usdt_at_ah_rococo(), false); // create wETH on Rococo (IRL it's already created by Snowbridge) - create_foreign_on_ah_rococo(bridged_weth_at_ah, true); + create_foreign_on_ah_rococo(bridged_weth_at_ah.clone(), true); // prefund AHW's sovereign account on AHR to be able to withdraw USDT and wETH from reserves let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( Westend, @@ -283,7 +284,7 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { ); AssetHubRococo::mint_foreign_asset( ::RuntimeOrigin::signed(AssetHubRococo::account_id_of(ALICE)), - bridged_weth_at_ah, + bridged_weth_at_ah.clone(), sov_ahw_on_ahr, amount_to_send * 2, ); @@ -291,21 +292,21 @@ fn send_back_rocs_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { // set up source chain AH Westend: // create wETH and USDT foreign assets on Westend and prefund sender's account let prefund_accounts = vec![(sender.clone(), amount_to_send * 2)]; - create_foreign_on_ah_westend(bridged_weth_at_ah, true, prefund_accounts.clone()); - create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend, true, prefund_accounts); + create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true, prefund_accounts.clone()); + create_foreign_on_ah_westend(bridged_usdt_at_asset_hub_westend.clone(), true, prefund_accounts); // check balances before let receiver_usdts_before = AssetHubRococo::execute_with(|| { type Assets = ::Assets; >::balance(USDT_ID, &receiver) }); - let receiver_weth_before = foreign_balance_on_ah_rococo(bridged_weth_at_ah, &receiver); + let receiver_weth_before = foreign_balance_on_ah_rococo(bridged_weth_at_ah.clone(), &receiver); let usdt_id: AssetId = Location::try_from(bridged_usdt_at_asset_hub_westend).unwrap().into(); // send USDTs and wETHs let assets: Assets = vec![ (usdt_id.clone(), amount_to_send).into(), - (Location::try_from(bridged_weth_at_ah).unwrap(), amount_to_send).into(), + (Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount_to_send).into(), ] .into(); // use USDT for fees @@ -367,7 +368,8 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() type ForeignAssets = ::ForeignAssets; >::balance(wnd_at_westend_parachains.clone(), &sender) }); - let receiver_wnds_before = foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo, &receiver); + let receiver_wnds_before = + foreign_balance_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), &receiver); // Send WNDs over bridge { @@ -398,7 +400,7 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() vec![ // issue WNDs on AHR RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == wnd_at_westend_parachains.clone().try_into().unwrap(), + asset_id: *asset_id == wnd_at_westend_parachains.clone(), owner: owner == &receiver, }, // message processed successfully @@ -429,7 +431,6 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() #[test] fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() { let roc_at_westend_parachains = bridged_roc_at_ah_westend(); - let roc_at_westend_parachains_v3 = roc_at_westend_parachains.clone().try_into().unwrap(); let amount = ASSET_HUB_WESTEND_ED * 10_000_000; let sender = PenpalBSender::get(); let receiver = AssetHubRococoReceiver::get(); @@ -442,7 +443,7 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc let penpal_location = AssetHubWestend::sibling_location_of(PenpalB::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location); let prefund_accounts = vec![(sov_penpal_on_ahr, amount * 2)]; - create_foreign_on_ah_westend(roc_at_westend_parachains_v3, true, prefund_accounts); + create_foreign_on_ah_westend(roc_at_westend_parachains.clone(), true, prefund_accounts); let asset_owner: AccountId = AssetHubWestend::account_id_of(ALICE); PenpalB::force_create_foreign_asset( roc_at_westend_parachains.clone(), @@ -454,7 +455,7 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Westend, + Westend, AssetHubWestend::para_id(), ); AssetHubRococo::fund_accounts(vec![(sov_ahw_on_ahr.clone(), amount * 2)]); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index 87ae9aedd6f1fdb80a8aa0363091cd5a34d6eae0..699641d3328fc416ae6ef0c550eb1961084c50e1 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)]) } @@ -69,13 +73,13 @@ pub(crate) fn weth_at_asset_hubs() -> Location { ) } -pub(crate) fn create_foreign_on_ah_rococo(id: v3::Location, sufficient: bool) { +pub(crate) fn create_foreign_on_ah_rococo(id: v4::Location, sufficient: bool) { let owner = AssetHubRococo::account_id_of(ALICE); AssetHubRococo::force_create_foreign_asset(id, owner, sufficient, ASSET_MIN_BALANCE, vec![]); } pub(crate) fn create_foreign_on_ah_westend( - id: v3::Location, + id: v4::Location, sufficient: bool, prefund_accounts: Vec<(AccountId, u128)>, ) { @@ -84,13 +88,13 @@ pub(crate) fn create_foreign_on_ah_westend( AssetHubWestend::force_create_foreign_asset(id, owner, sufficient, min, prefund_accounts); } -pub(crate) fn foreign_balance_on_ah_rococo(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_rococo(id: v4::Location, who: &AccountId) -> u128 { AssetHubRococo::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) -> u128 { AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) @@ -98,8 +102,8 @@ pub(crate) fn foreign_balance_on_ah_westend(id: v3::Location, who: &AccountId) - } // set up pool -pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v3::Location, is_foreign: bool) { - let roc: v3::Location = v3::Parent.into(); +pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v4::Location, is_foreign: bool) { + let roc: v4::Location = v4::Parent.into(); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; let owner = AssetHubRococoSender::get(); @@ -108,13 +112,13 @@ pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v3::Location, is_foreign: if is_foreign { assert_ok!(::ForeignAssets::mint( signed_owner.clone(), - asset.into(), + asset.clone().into(), owner.clone().into(), 3_000_000_000_000, )); } else { - let asset_id = match asset.interior.split_last() { - (_, Some(v3::Junction::GeneralIndex(id))) => id as u32, + let asset_id = match asset.interior.last() { + Some(v4::Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; assert_ok!(::Assets::mint( @@ -126,8 +130,8 @@ pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v3::Location, is_foreign: } assert_ok!(::AssetConversion::create_pool( signed_owner.clone(), - Box::new(roc), - Box::new(asset), + Box::new(roc.clone()), + Box::new(asset.clone()), )); assert_expected_events!( AssetHubRococo, @@ -224,3 +228,57 @@ pub(crate) fn assert_bridge_hub_rococo_message_received() { ); }) } + +pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { + use testnet_parachains_constants::{ + rococo::currency::UNITS as ROC, westend::currency::UNITS as WND, + }; + + // open AHR -> AHW + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); + AssetHubRococo::open_bridge( + AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), + [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + Some(( + (roc_at_ah_rococo(), ROC * 1).into(), + BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( + AssetHubRococo::para_id(), + )), + )), + ); + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmOverBridgeHubWestend( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); + + // open AHW -> AHR + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); + AssetHubWestend::open_bridge( + AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), + [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + Some(( + (wnd_at_ah_westend(), WND * 1).into(), + BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( + AssetHubWestend::para_id(), + )), + )), + ); + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::XcmOverBridgeHubRococo( + pallet_xcm_bridge_hub::Event::BridgeOpened { .. } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs index 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/bridges/bridge-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs index 64378a844f52a7cd14fecd2808fd1b5051d62528..a5add3b82957cf83a3d49dc04e32ace90e9bf099 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/teleport.rs @@ -27,3 +27,23 @@ fn teleport_to_other_system_parachains_works() { (native_asset, amount) ); } + +#[test] +fn teleport_from_and_to_relay() { + let amount = WESTEND_ED * 100; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Westend, + WestendXcmConfig, + vec![BridgeHubWestend], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + BridgeHubWestend, + BridgeHubWestendXcmConfig, + Westend, + amount + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml index 3012e2b19f5326f9df8e61521a82afeaaaa73fae..c4d281b75a77b32a9772ade66e2dbb0837cc8fcf 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/Cargo.toml @@ -23,6 +23,7 @@ pallet-assets = { workspace = true } pallet-treasury = { workspace = true } pallet-message-queue = { workspace = true } pallet-utility = { workspace = true } +pallet-whitelist = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs new file mode 100644 index 0000000000000000000000000000000000000000..f97599bda7f08be29d3388cd4a0856db77a30858 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs @@ -0,0 +1,72 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use codec::Encode; +use collectives_fellowship::pallet_fellowship_origins::Origin::Fellows as FellowsOrigin; +use frame_support::{assert_ok, sp_runtime::traits::Dispatchable}; + +#[test] +fn fellows_whitelist_call() { + CollectivesWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeCall = ::RuntimeCall; + type RuntimeOrigin = ::RuntimeOrigin; + type Runtime = ::Runtime; + type WestendCall = ::RuntimeCall; + type WestendRuntime = ::Runtime; + + let call_hash = [1u8; 32].into(); + + let whitelist_call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::::send { + dest: bx!(VersionedLocation::from(Location::parent())), + message: bx!(VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: Weight::from_parts(5_000_000_000, 500_000), + call: WestendCall::Whitelist( + pallet_whitelist::Call::::whitelist_call { call_hash } + ) + .encode() + .into(), + } + ]))), + }); + + let fellows_origin: RuntimeOrigin = FellowsOrigin.into(); + + assert_ok!(whitelist_call.dispatch(fellows_origin)); + + assert_expected_events!( + CollectivesWestend, + vec![ + RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + Westend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + Westend, + vec![ + RuntimeEvent::Whitelist(pallet_whitelist::Event::CallWhitelisted { .. }) => {}, + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true, .. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs new file mode 100644 index 0000000000000000000000000000000000000000..840d2da4946306498605853366daf46fb145ca78 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_salary.rs @@ -0,0 +1,66 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use collectives_fellowship::FellowshipSalaryPaymaster; +use frame_support::{ + assert_ok, + traits::{fungibles::Mutate, tokens::Pay}, +}; +use xcm_executor::traits::ConvertLocation; + +const FELLOWSHIP_SALARY_PALLET_ID: u8 = 64; + +#[test] +fn pay_salary() { + let asset_id: u32 = 1984; + let fellowship_salary = ( + Parent, + Parachain(CollectivesWestend::para_id().into()), + PalletInstance(FELLOWSHIP_SALARY_PALLET_ID), + ); + let pay_from = + AssetHubLocationToAccountId::convert_location(&fellowship_salary.into()).unwrap(); + let pay_to = Westend::account_id_of(ALICE); + let pay_amount = 9_000_000_000; + + AssetHubWestend::execute_with(|| { + type AssetHubAssets = ::Assets; + assert_ok!(>::mint_into(asset_id, &pay_from, pay_amount * 2)); + }); + + CollectivesWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(FellowshipSalaryPaymaster::pay(&pay_to, (), pay_amount)); + assert_expected_events!( + CollectivesWestend, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::Assets(pallet_assets::Event::Transferred { .. }) => {}, + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true ,.. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs index 40e98a8b68694b48d9554e98555770a97c3e6b46..ef4e4885183d092c087329b8ebc414afa105a500 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/mod.rs @@ -13,5 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod fellowship; +mod fellowship_salary; mod fellowship_treasury; mod teleport; diff --git a/cumulus/parachains/integration-tests/emulated/tests/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/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs index 3c0533f775e2702bd63cf5c7ef7ef806a8fa84f3..06b0b6ba600534793b83f24e6e8d9b9d730736bd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs @@ -15,15 +15,8 @@ #[cfg(test)] mod imports { - pub use codec::Encode; - // Substrate - pub use frame_support::{ - assert_ok, - pallet_prelude::Weight, - sp_runtime::{AccountId32, DispatchResult}, - traits::fungibles::Inspect, - }; + pub use frame_support::{assert_ok, sp_runtime::DispatchResult, traits::fungibles::Inspect}; // Polkadot pub use xcm::prelude::*; @@ -37,20 +30,14 @@ mod imports { pub use parachains_common::Balance; pub use rococo_system_emulated_network::{ people_rococo_emulated_chain::{ - genesis::ED as PEOPLE_ROCOCO_ED, people_rococo_runtime::{ - people, xcm_config::XcmConfig as PeopleRococoXcmConfig, - ExistentialDeposit as PeopleRococoExistentialDeposit, Runtime as PeopleRuntime, + xcm_config::XcmConfig as PeopleRococoXcmConfig, + ExistentialDeposit as PeopleRococoExistentialDeposit, }, PeopleRococoParaPallet as PeopleRococoPallet, }, rococo_emulated_chain::{ - genesis::ED as ROCOCO_ED, - rococo_runtime::{ - xcm_config::XcmConfig as RococoXcmConfig, BasicDeposit, ByteDeposit, - MaxAdditionalFields, MaxSubAccounts, Runtime as RococoRuntime, - RuntimeOrigin as RococoOrigin, SubAccountDeposit, - }, + genesis::ED as ROCOCO_ED, rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig, RococoRelayPallet as RococoPallet, }, PeopleRococoPara as PeopleRococo, PeopleRococoParaReceiver as PeopleRococoReceiver, @@ -58,7 +45,6 @@ mod imports { RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, }; - pub type RelayToSystemParaTest = Test; pub type SystemParaToRelayTest = Test; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs index 3f18621224acabcbb5a2df4de4344db6b2408999..08749b295dc21def8feb2c2318bcbed2b258ba9e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/mod.rs @@ -14,5 +14,4 @@ // limitations under the License. mod claim_assets; -mod reap_identity; mod teleport; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs deleted file mode 100644 index 10f0c61ed63cfd12f235e1f4f2c3391b78a8ebc0..0000000000000000000000000000000000000000 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/reap_identity.rs +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # OnReapIdentity Tests -//! -//! This file contains the test cases for migrating Identity data away from the Rococo Relay -//! chain and to the PeopleRococo parachain. This migration is part of the broader Minimal Relay -//! effort: -//! https://github.com/polkadot-fellows/RFCs/blob/main/text/0032-minimal-relay.md -//! -//! ## Overview -//! -//! The tests validate the robustness and correctness of the `OnReapIdentityHandler` -//! ensuring that it behaves as expected in various scenarios. Key aspects tested include: -//! -//! - **Deposit Handling**: Confirming that deposits are correctly migrated from the Relay Chain to -//! the People parachain in various scenarios (different `IdentityInfo` fields and different -//! numbers of sub-accounts). -//! -//! ### Test Scenarios -//! -//! The tests are categorized into several scenarios, each resulting in different deposits required -//! on the destination parachain. The tests ensure: -//! -//! - Reserved deposits on the Relay Chain are fully released; -//! - The freed deposit from the Relay Chain is sufficient for the parachain deposit; and -//! - The account will exist on the parachain. - -use crate::imports::*; -use frame_support::BoundedVec; -use pallet_balances::Event as BalancesEvent; -use pallet_identity::{legacy::IdentityInfo, Data, Event as IdentityEvent, IdentityOf, SubsOf}; -use people::{ - BasicDeposit as BasicDepositParachain, ByteDeposit as ByteDepositParachain, - IdentityInfo as IdentityInfoParachain, SubAccountDeposit as SubAccountDepositParachain, -}; -use rococo_runtime_constants::currency::*; -use rococo_system_emulated_network::{ - rococo_emulated_chain::RococoRelayPallet, RococoRelay, RococoRelaySender, -}; - -type Balance = u128; -type RococoIdentity = ::Identity; -type RococoBalances = ::Balances; -type RococoIdentityMigrator = ::IdentityMigrator; -type PeopleRococoIdentity = ::Identity; -type PeopleRococoBalances = ::Balances; - -#[derive(Clone, Debug)] -struct Identity { - relay: IdentityInfo, - para: IdentityInfoParachain, - subs: Subs, -} - -impl Identity { - fn new( - full: bool, - additional: Option>, - subs: Subs, - ) -> Self { - let pgp_fingerprint = [ - 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, - ]; - let make_data = |data: &[u8], full: bool| -> Data { - if full { - Data::Raw(data.to_vec().try_into().unwrap()) - } else { - Data::None - } - }; - let (github, discord) = additional - .as_ref() - .and_then(|vec| vec.first()) - .map(|(g, d)| (g.clone(), d.clone())) - .unwrap_or((Data::None, Data::None)); - Self { - relay: IdentityInfo { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - riot: make_data(b"xcm-riot", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - additional: additional.unwrap_or_default(), - }, - para: IdentityInfoParachain { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - matrix: make_data(b"xcm-matrix@server", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - github, - discord, - }, - subs, - } - } -} - -#[derive(Clone, Debug)] -enum Subs { - Zero, - Many(u32), -} - -enum IdentityOn<'a> { - Relay(&'a IdentityInfo), - Para(&'a IdentityInfoParachain), -} - -impl IdentityOn<'_> { - fn calculate_deposit(self) -> Balance { - match self { - IdentityOn::Relay(id) => { - let base_deposit = BasicDeposit::get(); - let byte_deposit = - ByteDeposit::get() * TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - IdentityOn::Para(id) => { - let base_deposit = BasicDepositParachain::get(); - let byte_deposit = ByteDepositParachain::get() * - TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - } - } -} - -/// Generate an `AccountId32` from a `u32`. -/// This creates a 32-byte array, initially filled with `255`, and then repeatedly fills it -/// with the 4-byte little-endian representation of the `u32` value, until the array is full. -/// -/// **Example**: -/// -/// `account_from_u32(5)` will return an `AccountId32` with the bytes -/// `[0, 5, 0, 0, 0, 0, 0, 0, 0, 5 ... ]` -fn account_from_u32(id: u32) -> AccountId32 { - let mut buffer = [255u8; 32]; - let id_bytes = id.to_le_bytes(); - let id_size = id_bytes.len(); - for chunk in buffer.chunks_mut(id_size) { - chunk.clone_from_slice(&id_bytes); - } - AccountId32::new(buffer) -} - -// Set up the Relay Chain with an identity. -fn set_id_relay(id: &Identity) -> Balance { - let mut total_deposit: Balance = 0; - - // Set identity and Subs on Relay Chain - RococoRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - assert_ok!(RococoIdentity::set_identity( - RococoOrigin::signed(RococoRelaySender::get()), - Box::new(id.relay.clone()) - )); - - if let Subs::Many(n) = id.subs { - let subs: Vec<_> = (0..n) - .map(|i| (account_from_u32(i), Data::Raw(b"name".to_vec().try_into().unwrap()))) - .collect(); - - assert_ok!(RococoIdentity::set_subs( - RococoOrigin::signed(RococoRelaySender::get()), - subs, - )); - } - - let reserved_balance = RococoBalances::reserved_balance(RococoRelaySender::get()); - let id_deposit = IdentityOn::Relay(&id.relay).calculate_deposit(); - - let total_deposit = match id.subs { - Subs::Zero => { - total_deposit = id_deposit; // No subs - assert_expected_events!( - RococoRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == id_deposit, - }, - ] - ); - total_deposit - }, - Subs::Many(n) => { - let sub_account_deposit = n as Balance * SubAccountDeposit::get(); - total_deposit = - sub_account_deposit + IdentityOn::Relay(&id.relay).calculate_deposit(); - assert_expected_events!( - RococoRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == id_deposit, - }, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == sub_account_deposit, - }, - ] - ); - total_deposit - }, - }; - - assert_eq!(reserved_balance, total_deposit); - }); - total_deposit -} - -// Set up the parachain with an identity and (maybe) sub accounts, but with zero deposits. -fn assert_set_id_parachain(id: &Identity) { - // Set identity and Subs on Parachain with zero deposit - PeopleRococo::execute_with(|| { - let free_bal = PeopleRococoBalances::free_balance(PeopleRococoSender::get()); - let reserved_balance = PeopleRococoBalances::reserved_balance(PeopleRococoSender::get()); - - // total balance at Genesis should be zero - assert_eq!(reserved_balance + free_bal, 0); - - assert_ok!(PeopleRococoIdentity::set_identity_no_deposit( - &PeopleRococoSender::get(), - id.para.clone(), - )); - - match id.subs { - Subs::Zero => {}, - Subs::Many(n) => { - let subs: Vec<_> = (0..n) - .map(|ii| { - (account_from_u32(ii), Data::Raw(b"name".to_vec().try_into().unwrap())) - }) - .collect(); - assert_ok!(PeopleRococoIdentity::set_subs_no_deposit( - &PeopleRococoSender::get(), - subs, - )); - }, - } - - // No amount should be reserved as deposit amounts are set to 0. - let reserved_balance = PeopleRococoBalances::reserved_balance(PeopleRococoSender::get()); - assert_eq!(reserved_balance, 0); - assert!(IdentityOf::::get(PeopleRococoSender::get()).is_some()); - - let (_, sub_accounts) = SubsOf::::get(PeopleRococoSender::get()); - - match id.subs { - Subs::Zero => assert_eq!(sub_accounts.len(), 0), - Subs::Many(n) => assert_eq!(sub_accounts.len(), n as usize), - } - }); -} - -// Reap the identity on the Relay Chain and assert that the correct things happen there. -fn assert_reap_id_relay(total_deposit: Balance, id: &Identity) { - RococoRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - let free_bal_before_reap = RococoBalances::free_balance(RococoRelaySender::get()); - let reserved_balance = RococoBalances::reserved_balance(RococoRelaySender::get()); - - assert_eq!(reserved_balance, total_deposit); - - assert_ok!(RococoIdentityMigrator::reap_identity( - RococoOrigin::signed(RococoRelaySender::get()), - RococoRelaySender::get() - )); - - let remote_deposit = match id.subs { - Subs::Zero => calculate_remote_deposit(id.relay.encoded_size() as u32, 0), - Subs::Many(n) => calculate_remote_deposit(id.relay.encoded_size() as u32, n), - }; - - assert_expected_events!( - RococoRelay, - vec![ - // `reap_identity` sums the identity and subs deposits and unreserves them in one - // call. Therefore, we only expect one `Unreserved` event. - RuntimeEvent::Balances(BalancesEvent::Unreserved { who, amount }) => { - who: *who == RococoRelaySender::get(), - amount: *amount == total_deposit, - }, - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::IdentityReaped { - who, - }) => { - who: *who == PeopleRococoSender::get(), - }, - ] - ); - // Identity should be gone. - assert!(IdentityOf::::get(RococoRelaySender::get()).is_none()); - - // Subs should be gone. - let (_, sub_accounts) = SubsOf::::get(RococoRelaySender::get()); - assert_eq!(sub_accounts.len(), 0); - - let reserved_balance = RococoBalances::reserved_balance(RococoRelaySender::get()); - assert_eq!(reserved_balance, 0); - - // Free balance should be greater (i.e. the teleport should work even if 100% of an - // account's balance is reserved for Identity). - let free_bal_after_reap = RococoBalances::free_balance(RococoRelaySender::get()); - assert!(free_bal_after_reap > free_bal_before_reap); - - // Implicit: total_deposit > remote_deposit. As in, accounts should always have enough - // reserved for the parachain deposit. - assert_eq!(free_bal_after_reap, free_bal_before_reap + total_deposit - remote_deposit); - }); -} - -// Reaping the identity on the Relay Chain will have sent an XCM program to the parachain. Ensure -// that everything happens as expected. -fn assert_reap_parachain(id: &Identity) { - PeopleRococo::execute_with(|| { - let reserved_balance = PeopleRococoBalances::reserved_balance(PeopleRococoSender::get()); - let id_deposit = IdentityOn::Para(&id.para).calculate_deposit(); - let total_deposit = match id.subs { - Subs::Zero => id_deposit, - Subs::Many(n) => id_deposit + n as Balance * SubAccountDepositParachain::get(), - }; - assert_reap_events(id_deposit, id); - assert_eq!(reserved_balance, total_deposit); - - // Should have at least one ED after in free balance after the reap. - assert!(PeopleRococoBalances::free_balance(PeopleRococoSender::get()) >= PEOPLE_ROCOCO_ED); - }); -} - -// Assert the events that should happen on the parachain upon reaping an identity on the Relay -// Chain. -fn assert_reap_events(id_deposit: Balance, id: &Identity) { - type RuntimeEvent = ::RuntimeEvent; - match id.subs { - Subs::Zero => { - assert_expected_events!( - PeopleRococo, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleRococoSender::get(), - amount: *amount == id_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleRococoSender::get(), - identity: *identity == id_deposit, - subs: *subs == 0, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - Subs::Many(n) => { - let subs_deposit = n as Balance * SubAccountDepositParachain::get(); - assert_expected_events!( - PeopleRococo, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleRococoSender::get(), - amount: *amount == id_deposit, - }, - // Amount reserved for subs - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleRococoSender::get(), - amount: *amount == subs_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleRococoSender::get(), - identity: *identity == id_deposit, - subs: *subs == subs_deposit, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - }; -} - -/// Duplicate of the impl of `ToParachainIdentityReaper` in the Rococo runtime. -fn calculate_remote_deposit(bytes: u32, subs: u32) -> Balance { - // Note: These `deposit` functions and `EXISTENTIAL_DEPOSIT` correspond to the Relay Chain's. - // Pulled in: use rococo_runtime_constants::currency::*; - let para_basic_deposit = deposit(1, 17) / 100; - let para_byte_deposit = deposit(0, 1) / 100; - let para_sub_account_deposit = deposit(1, 53) / 100; - let para_existential_deposit = EXISTENTIAL_DEPOSIT / 10; - - // pallet deposits - let id_deposit = - para_basic_deposit.saturating_add(para_byte_deposit.saturating_mul(bytes as Balance)); - let subs_deposit = para_sub_account_deposit.saturating_mul(subs as Balance); - - id_deposit - .saturating_add(subs_deposit) - .saturating_add(para_existential_deposit.saturating_mul(2)) -} - -// Represent some `additional` data that would not be migrated to the parachain. The encoded size, -// and thus the byte deposit, should decrease. -fn nonsensical_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![( - Data::Raw(b"fOo".to_vec().try_into().unwrap()), - Data::Raw(b"baR".to_vec().try_into().unwrap()), - )]) - .unwrap() -} - -// Represent some `additional` data that will be migrated to the parachain as first-class fields. -fn meaningful_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![ - ( - Data::Raw(b"github".to_vec().try_into().unwrap()), - Data::Raw(b"niels-username".to_vec().try_into().unwrap()), - ), - ( - Data::Raw(b"discord".to_vec().try_into().unwrap()), - Data::Raw(b"bohr-username".to_vec().try_into().unwrap()), - ), - ]) - .unwrap() -} - -// Execute a single test case. -fn assert_relay_para_flow(id: &Identity) { - let total_deposit = set_id_relay(id); - assert_set_id_parachain(id); - assert_reap_id_relay(total_deposit, id); - assert_reap_parachain(id); -} - -// Tests with empty `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_zero_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_max_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_max_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo` and `additional` fields that will _not_ be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(nonsensical_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} - -// Tests with full `IdentityInfo` and `additional` fields that will be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(meaningful_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs index 4410d1bd40dcc1276bb5441962f855823861d1cd..44e6b3934f0e7999d15e78d0393aed129ada2455 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs @@ -14,68 +14,38 @@ // limitations under the License. use crate::imports::*; +use emulated_integration_tests_common::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); +#[test] +fn teleport_from_and_to_relay() { + let amount = ROCOCO_ED * 100; + let native_asset: Assets = (Here, amount).into(); - assert_expected_events!( + test_relay_is_trusted_teleporter!( Rococo, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Rococo::assert_ump_queue_processed( - true, - Some(PeopleRococo::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), + RococoXcmConfig, + vec![PeopleRococo], + (native_asset, amount) ); - assert_expected_events!( + test_parachain_is_trusted_teleporter_for_relay!( + PeopleRococo, + PeopleRococoXcmConfig, Rococo, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] + amount ); } fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { - Rococo::assert_ump_queue_processed( - false, - Some(PeopleRococo::para_id()), - Some(Weight::from_parts(157_718_000, 3_593)), - ); + Rococo::assert_ump_queue_processed(false, Some(PeopleRococo::para_id()), None); } fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 600_000_000, - 7_000, - ))); + PeopleRococo::assert_xcm_pallet_attempted_complete(None); PeopleRococo::assert_parachain_system_ump_sent(); @@ -91,33 +61,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - PeopleRococo::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); - - assert_expected_events!( - PeopleRococo, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -129,92 +72,8 @@ fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResu ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work -#[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = ROCOCO_ED * 1000; - let dest = Rococo::child_location_of(PeopleRococo::para_id()); - let beneficiary_id = PeopleRococoReceiver::get(); - let test_args = TestContext { - sender: RococoSender::get(), - receiver: PeopleRococoReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Rococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` -#[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - let amount_to_send: Balance = PEOPLE_ROCOCO_ED * 1000; - let destination = PeopleRococo::parent_location(); - let beneficiary_id = RococoReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - // Fund a sender - PeopleRococo::fund_accounts(vec![(PeopleRococoSender::get(), ROCOCO_ED * 2_000u128)]); - - let test_args = TestContext { - sender: PeopleRococoSender::get(), - receiver: RococoReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PeopleRococo::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml index f7e1cce85a2cf7895d178cb3f95a24070c790198..aa6eebc5458f4a1f149b8d5981f18d9e646e7339 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/Cargo.toml @@ -15,6 +15,7 @@ frame-support = { workspace = true } pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } pallet-identity = { workspace = true } +pallet-xcm = { workspace = true } sp-runtime = { workspace = true } # Polkadot diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs index 689409fe5040708b822892357eff14d3ac0c591a..418cfea07ddc2b7fec7e372fd7c54e62631f4df8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs @@ -15,14 +15,8 @@ #[cfg(test)] mod imports { - pub use codec::Encode; // Substrate - pub use frame_support::{ - assert_ok, - pallet_prelude::Weight, - sp_runtime::{AccountId32, DispatchResult}, - traits::fungibles::Inspect, - }; + pub use frame_support::{assert_ok, sp_runtime::DispatchResult, traits::fungibles::Inspect}; // Polkadot pub use xcm::prelude::*; @@ -37,20 +31,14 @@ mod imports { pub use westend_system_emulated_network::{ self, people_westend_emulated_chain::{ - genesis::ED as PEOPLE_WESTEND_ED, people_westend_runtime::{ - people, xcm_config::XcmConfig as PeopleWestendXcmConfig, - ExistentialDeposit as PeopleWestendExistentialDeposit, Runtime as PeopleRuntime, + xcm_config::XcmConfig as PeopleWestendXcmConfig, + ExistentialDeposit as PeopleWestendExistentialDeposit, }, PeopleWestendParaPallet as PeopleWestendPallet, }, westend_emulated_chain::{ - genesis::ED as WESTEND_ED, - westend_runtime::{ - xcm_config::XcmConfig as WestendXcmConfig, BasicDeposit, ByteDeposit, - MaxAdditionalFields, MaxSubAccounts, Runtime as WestendRuntime, - RuntimeOrigin as WestendOrigin, SubAccountDeposit, - }, + genesis::ED as WESTEND_ED, westend_runtime::xcm_config::XcmConfig as WestendXcmConfig, WestendRelayPallet as WestendPallet, }, PeopleWestendPara as PeopleWestend, PeopleWestendParaReceiver as PeopleWestendReceiver, @@ -58,7 +46,6 @@ mod imports { WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, }; - pub type RelayToSystemParaTest = Test; pub type SystemParaToRelayTest = Test; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs index 3f18621224acabcbb5a2df4de4344db6b2408999..08749b295dc21def8feb2c2318bcbed2b258ba9e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/mod.rs @@ -14,5 +14,4 @@ // limitations under the License. mod claim_assets; -mod reap_identity; mod teleport; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs deleted file mode 100644 index cfbf4d7d95800e589dec0774149325ccaeb96762..0000000000000000000000000000000000000000 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/reap_identity.rs +++ /dev/null @@ -1,547 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # OnReapIdentity Tests -//! -//! This file contains the test cases for migrating Identity data away from the Westend Relay -//! chain and to the PeopleWestend parachain. This migration is part of the broader Minimal Relay -//! effort: -//! https://github.com/polkadot-fellows/RFCs/blob/main/text/0032-minimal-relay.md -//! -//! ## Overview -//! -//! The tests validate the robustness and correctness of the `OnReapIdentityHandler` -//! ensuring that it behaves as expected in various scenarios. Key aspects tested include: -//! -//! - **Deposit Handling**: Confirming that deposits are correctly migrated from the Relay Chain to -//! the People parachain in various scenarios (different `IdentityInfo` fields and different -//! numbers of sub-accounts). -//! -//! ### Test Scenarios -//! -//! The tests are categorized into several scenarios, each resulting in different deposits required -//! on the destination parachain. The tests ensure: -//! -//! - Reserved deposits on the Relay Chain are fully released; -//! - The freed deposit from the Relay Chain is sufficient for the parachain deposit; and -//! - The account will exist on the parachain. - -use crate::imports::*; -use frame_support::BoundedVec; -use pallet_balances::Event as BalancesEvent; -use pallet_identity::{legacy::IdentityInfo, Data, Event as IdentityEvent, IdentityOf, SubsOf}; -use people::{ - BasicDeposit as BasicDepositParachain, ByteDeposit as ByteDepositParachain, - IdentityInfo as IdentityInfoParachain, SubAccountDeposit as SubAccountDepositParachain, -}; -use westend_runtime_constants::currency::*; -use westend_system_emulated_network::{ - westend_emulated_chain::WestendRelayPallet, WestendRelay, WestendRelaySender, -}; - -type Balance = u128; -type WestendIdentity = ::Identity; -type WestendBalances = ::Balances; -type WestendIdentityMigrator = ::IdentityMigrator; -type PeopleWestendIdentity = ::Identity; -type PeopleWestendBalances = ::Balances; - -#[derive(Clone, Debug)] -struct Identity { - relay: IdentityInfo, - para: IdentityInfoParachain, - subs: Subs, -} - -impl Identity { - fn new( - full: bool, - additional: Option>, - subs: Subs, - ) -> Self { - let pgp_fingerprint = [ - 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, - ]; - let make_data = |data: &[u8], full: bool| -> Data { - if full { - Data::Raw(data.to_vec().try_into().unwrap()) - } else { - Data::None - } - }; - let (github, discord) = additional - .as_ref() - .and_then(|vec| vec.first()) - .map(|(g, d)| (g.clone(), d.clone())) - .unwrap_or((Data::None, Data::None)); - Self { - relay: IdentityInfo { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - riot: make_data(b"xcm-riot", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - additional: additional.unwrap_or_default(), - }, - para: IdentityInfoParachain { - display: make_data(b"xcm-test", full), - legal: make_data(b"The Xcm Test, Esq.", full), - web: make_data(b"https://visitme/", full), - matrix: make_data(b"xcm-matrix@server", full), - email: make_data(b"xcm-test@gmail.com", full), - pgp_fingerprint: Some(pgp_fingerprint), - image: make_data(b"xcm-test.png", full), - twitter: make_data(b"@xcm-test", full), - github, - discord, - }, - subs, - } - } -} - -#[derive(Clone, Debug)] -enum Subs { - Zero, - Many(u32), -} - -enum IdentityOn<'a> { - Relay(&'a IdentityInfo), - Para(&'a IdentityInfoParachain), -} - -impl IdentityOn<'_> { - fn calculate_deposit(self) -> Balance { - match self { - IdentityOn::Relay(id) => { - let base_deposit = BasicDeposit::get(); - let byte_deposit = - ByteDeposit::get() * TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - IdentityOn::Para(id) => { - let base_deposit = BasicDepositParachain::get(); - let byte_deposit = ByteDepositParachain::get() * - TryInto::::try_into(id.encoded_size()).unwrap(); - base_deposit + byte_deposit - }, - } - } -} - -/// Generate an `AccountId32` from a `u32`. -/// This creates a 32-byte array, initially filled with `255`, and then repeatedly fills it -/// with the 4-byte little-endian representation of the `u32` value, until the array is full. -/// -/// **Example**: -/// -/// `account_from_u32(5)` will return an `AccountId32` with the bytes -/// `[0, 5, 0, 0, 0, 0, 0, 0, 0, 5 ... ]` -fn account_from_u32(id: u32) -> AccountId32 { - let mut buffer = [255u8; 32]; - let id_bytes = id.to_le_bytes(); - let id_size = id_bytes.len(); - for chunk in buffer.chunks_mut(id_size) { - chunk.clone_from_slice(&id_bytes); - } - AccountId32::new(buffer) -} - -// Set up the Relay Chain with an identity. -fn set_id_relay(id: &Identity) -> Balance { - let mut total_deposit: Balance = 0; - - // Set identity and Subs on Relay Chain - WestendRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - assert_ok!(WestendIdentity::set_identity( - WestendOrigin::signed(WestendRelaySender::get()), - Box::new(id.relay.clone()) - )); - - if let Subs::Many(n) = id.subs { - let subs: Vec<_> = (0..n) - .map(|i| (account_from_u32(i), Data::Raw(b"name".to_vec().try_into().unwrap()))) - .collect(); - - assert_ok!(WestendIdentity::set_subs( - WestendOrigin::signed(WestendRelaySender::get()), - subs, - )); - } - - let reserved_balance = WestendBalances::reserved_balance(WestendRelaySender::get()); - let id_deposit = IdentityOn::Relay(&id.relay).calculate_deposit(); - - let total_deposit = match id.subs { - Subs::Zero => { - total_deposit = id_deposit; // No subs - assert_expected_events!( - WestendRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == id_deposit, - }, - ] - ); - total_deposit - }, - Subs::Many(n) => { - let sub_account_deposit = n as Balance * SubAccountDeposit::get(); - total_deposit = - sub_account_deposit + IdentityOn::Relay(&id.relay).calculate_deposit(); - assert_expected_events!( - WestendRelay, - vec![ - RuntimeEvent::Identity(IdentityEvent::IdentitySet { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == id_deposit, - }, - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == sub_account_deposit, - }, - ] - ); - total_deposit - }, - }; - - assert_eq!(reserved_balance, total_deposit); - }); - total_deposit -} - -// Set up the parachain with an identity and (maybe) sub accounts, but with zero deposits. -fn assert_set_id_parachain(id: &Identity) { - // Set identity and Subs on Parachain with zero deposit - PeopleWestend::execute_with(|| { - let free_bal = PeopleWestendBalances::free_balance(PeopleWestendSender::get()); - let reserved_balance = PeopleWestendBalances::reserved_balance(PeopleWestendSender::get()); - - // total balance at Genesis should be zero - assert_eq!(reserved_balance + free_bal, 0); - - assert_ok!(PeopleWestendIdentity::set_identity_no_deposit( - &PeopleWestendSender::get(), - id.para.clone(), - )); - - match id.subs { - Subs::Zero => {}, - Subs::Many(n) => { - let subs: Vec<_> = (0..n) - .map(|ii| { - (account_from_u32(ii), Data::Raw(b"name".to_vec().try_into().unwrap())) - }) - .collect(); - assert_ok!(PeopleWestendIdentity::set_subs_no_deposit( - &PeopleWestendSender::get(), - subs, - )); - }, - } - - // No amount should be reserved as deposit amounts are set to 0. - let reserved_balance = PeopleWestendBalances::reserved_balance(PeopleWestendSender::get()); - assert_eq!(reserved_balance, 0); - assert!(IdentityOf::::get(PeopleWestendSender::get()).is_some()); - - let (_, sub_accounts) = SubsOf::::get(PeopleWestendSender::get()); - - match id.subs { - Subs::Zero => assert_eq!(sub_accounts.len(), 0), - Subs::Many(n) => assert_eq!(sub_accounts.len(), n as usize), - } - }); -} - -// Reap the identity on the Relay Chain and assert that the correct things happen there. -fn assert_reap_id_relay(total_deposit: Balance, id: &Identity) { - WestendRelay::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - let free_bal_before_reap = WestendBalances::free_balance(WestendRelaySender::get()); - let reserved_balance = WestendBalances::reserved_balance(WestendRelaySender::get()); - - assert_eq!(reserved_balance, total_deposit); - - assert_ok!(WestendIdentityMigrator::reap_identity( - WestendOrigin::signed(WestendRelaySender::get()), - WestendRelaySender::get() - )); - - let remote_deposit = match id.subs { - Subs::Zero => calculate_remote_deposit(id.relay.encoded_size() as u32, 0), - Subs::Many(n) => calculate_remote_deposit(id.relay.encoded_size() as u32, n), - }; - - assert_expected_events!( - WestendRelay, - vec![ - // `reap_identity` sums the identity and subs deposits and unreserves them in one - // call. Therefore, we only expect one `Unreserved` event. - RuntimeEvent::Balances(BalancesEvent::Unreserved { who, amount }) => { - who: *who == WestendRelaySender::get(), - amount: *amount == total_deposit, - }, - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::IdentityReaped { - who, - }) => { - who: *who == PeopleWestendSender::get(), - }, - ] - ); - // Identity should be gone. - assert!(IdentityOf::::get(WestendRelaySender::get()).is_none()); - - // Subs should be gone. - let (_, sub_accounts) = SubsOf::::get(WestendRelaySender::get()); - assert_eq!(sub_accounts.len(), 0); - - let reserved_balance = WestendBalances::reserved_balance(WestendRelaySender::get()); - assert_eq!(reserved_balance, 0); - - // Free balance should be greater (i.e. the teleport should work even if 100% of an - // account's balance is reserved for Identity). - let free_bal_after_reap = WestendBalances::free_balance(WestendRelaySender::get()); - assert!(free_bal_after_reap > free_bal_before_reap); - - // Implicit: total_deposit > remote_deposit. As in, accounts should always have enough - // reserved for the parachain deposit. - assert_eq!(free_bal_after_reap, free_bal_before_reap + total_deposit - remote_deposit); - }); -} - -// Reaping the identity on the Relay Chain will have sent an XCM program to the parachain. Ensure -// that everything happens as expected. -fn assert_reap_parachain(id: &Identity) { - PeopleWestend::execute_with(|| { - let reserved_balance = PeopleWestendBalances::reserved_balance(PeopleWestendSender::get()); - let id_deposit = IdentityOn::Para(&id.para).calculate_deposit(); - let total_deposit = match id.subs { - Subs::Zero => id_deposit, - Subs::Many(n) => id_deposit + n as Balance * SubAccountDepositParachain::get(), - }; - assert_reap_events(id_deposit, id); - assert_eq!(reserved_balance, total_deposit); - - // Should have at least one ED after in free balance after the reap. - assert!( - PeopleWestendBalances::free_balance(PeopleWestendSender::get()) >= PEOPLE_WESTEND_ED - ); - }); -} - -// Assert the events that should happen on the parachain upon reaping an identity on the Relay -// Chain. -fn assert_reap_events(id_deposit: Balance, id: &Identity) { - type RuntimeEvent = ::RuntimeEvent; - match id.subs { - Subs::Zero => { - assert_expected_events!( - PeopleWestend, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleWestendSender::get(), - amount: *amount == id_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleWestendSender::get(), - identity: *identity == id_deposit, - subs: *subs == 0, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - Subs::Many(n) => { - let subs_deposit = n as Balance * SubAccountDepositParachain::get(); - assert_expected_events!( - PeopleWestend, - vec![ - // Deposit and Endowed from teleport - RuntimeEvent::Balances(BalancesEvent::Minted { .. }) => {}, - RuntimeEvent::Balances(BalancesEvent::Endowed { .. }) => {}, - // Amount reserved for identity info - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleWestendSender::get(), - amount: *amount == id_deposit, - }, - // Amount reserved for subs - RuntimeEvent::Balances(BalancesEvent::Reserved { who, amount }) => { - who: *who == PeopleWestendSender::get(), - amount: *amount == subs_deposit, - }, - // Confirmation from Migrator with individual identity and subs deposits - RuntimeEvent::IdentityMigrator( - polkadot_runtime_common::identity_migrator::Event::DepositUpdated { - who, identity, subs - }) => { - who: *who == PeopleWestendSender::get(), - identity: *identity == id_deposit, - subs: *subs == subs_deposit, - }, - RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { .. }) => {}, - ] - ); - }, - }; -} - -/// Duplicate of the impl of `ToParachainIdentityReaper` in the Westend runtime. -fn calculate_remote_deposit(bytes: u32, subs: u32) -> Balance { - // Note: These `deposit` functions and `EXISTENTIAL_DEPOSIT` correspond to the Relay Chain's. - // Pulled in: use westend_runtime_constants::currency::*; - let para_basic_deposit = deposit(1, 17) / 100; - let para_byte_deposit = deposit(0, 1) / 100; - let para_sub_account_deposit = deposit(1, 53) / 100; - let para_existential_deposit = EXISTENTIAL_DEPOSIT / 10; - - // pallet deposits - let id_deposit = - para_basic_deposit.saturating_add(para_byte_deposit.saturating_mul(bytes as Balance)); - let subs_deposit = para_sub_account_deposit.saturating_mul(subs as Balance); - - id_deposit - .saturating_add(subs_deposit) - .saturating_add(para_existential_deposit.saturating_mul(2)) -} - -// Represent some `additional` data that would not be migrated to the parachain. The encoded size, -// and thus the byte deposit, should decrease. -fn nonsensical_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![( - Data::Raw(b"fOo".to_vec().try_into().unwrap()), - Data::Raw(b"baR".to_vec().try_into().unwrap()), - )]) - .unwrap() -} - -// Represent some `additional` data that will be migrated to the parachain as first-class fields. -fn meaningful_additional() -> BoundedVec<(Data, Data), MaxAdditionalFields> { - BoundedVec::try_from(vec![ - ( - Data::Raw(b"github".to_vec().try_into().unwrap()), - Data::Raw(b"niels-username".to_vec().try_into().unwrap()), - ), - ( - Data::Raw(b"discord".to_vec().try_into().unwrap()), - Data::Raw(b"bohr-username".to_vec().try_into().unwrap()), - ), - ]) - .unwrap() -} - -// Execute a single test case. -fn assert_relay_para_flow(id: &Identity) { - let total_deposit = set_id_relay(id); - assert_set_id_parachain(id); - assert_reap_id_relay(total_deposit, id); - assert_reap_parachain(id); -} - -// Tests with empty `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_zero_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_minimal_identity_with_max_subs() { - assert_relay_para_flow(&Identity::new(false, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo`. - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_no_additional_max_subs() { - assert_relay_para_flow(&Identity::new(true, None, Subs::Many(MaxSubAccounts::get()))); -} - -// Tests with full `IdentityInfo` and `additional` fields that will _not_ be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional() { - assert_relay_para_flow(&Identity::new(true, Some(nonsensical_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_nonsense_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(nonsensical_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} - -// Tests with full `IdentityInfo` and `additional` fields that will be migrated. - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_zero_subs() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Zero)); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional() { - assert_relay_para_flow(&Identity::new(true, Some(meaningful_additional()), Subs::Many(1))); -} - -#[test] -fn on_reap_identity_works_for_full_identity_meaningful_additional_max_subs() { - assert_relay_para_flow(&Identity::new( - true, - Some(meaningful_additional()), - Subs::Many(MaxSubAccounts::get()), - )); -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs index 6fd3cdeb61fbc3d3deb5445a7edf57a5008367c6..83888031723ffffef24f86717741ddab1bf2598a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs @@ -14,68 +14,38 @@ // limitations under the License. use crate::imports::*; +use emulated_integration_tests_common::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); +#[test] +fn teleport_from_and_to_relay() { + let amount = WESTEND_ED * 100; + let native_asset: Assets = (Here, amount).into(); - assert_expected_events!( + test_relay_is_trusted_teleporter!( Westend, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Westend::assert_ump_queue_processed( - true, - Some(PeopleWestend::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), + WestendXcmConfig, + vec![PeopleWestend], + (native_asset, amount) ); - assert_expected_events!( + test_parachain_is_trusted_teleporter_for_relay!( + PeopleWestend, + PeopleWestendXcmConfig, Westend, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] + amount ); } fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { - Westend::assert_ump_queue_processed( - false, - Some(PeopleWestend::para_id()), - Some(Weight::from_parts(157_718_000, 3_593)), - ); + Westend::assert_ump_queue_processed(false, Some(PeopleWestend::para_id()), None); } fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 351_425_000, - 3_593, - ))); + PeopleWestend::assert_xcm_pallet_attempted_complete(None); PeopleWestend::assert_parachain_system_ump_sent(); @@ -91,33 +61,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - PeopleWestend::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); - - assert_expected_events!( - PeopleWestend, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -129,92 +72,8 @@ fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResu ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work -#[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = WESTEND_ED * 1000; - let dest = Westend::child_location_of(PeopleWestend::para_id()); - let beneficiary_id = PeopleWestendReceiver::get(); - let test_args = TestContext { - sender: WestendSender::get(), - receiver: PeopleWestendReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Westend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` -#[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - let amount_to_send: Balance = PEOPLE_WESTEND_ED * 1000; - let destination = PeopleWestend::parent_location(); - let beneficiary_id = WestendReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - // Fund a sender - PeopleWestend::fund_accounts(vec![(PeopleWestendSender::get(), WESTEND_ED * 2_000u128)]); - - let test_args = TestContext { - sender: PeopleWestendSender::get(), - receiver: WestendReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PeopleWestend::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - /// Limited Teleport of native asset from System Parachain to Relay Chain -/// should't work when there is not enough balance in Relay Chain's `CheckAccount` +/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] fn limited_teleport_native_assets_from_system_para_to_relay_fails() { // Init values for Relay Chain diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 98df41090a4072f45177326b70f617bf967d735b..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", @@ -258,4 +260,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-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..41b7e622b1b2a9be0f3ca7ee0818d36e7d245ddb --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs @@ -0,0 +1,183 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Asset Hub Rococo Runtime genesis config presets + +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use hex_literal::hex; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId, Balance as AssetHubBalance}; +use sp_core::{crypto::UncheckedInto, sr25519}; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::rococo::xcm_version::SAFE_XCM_VERSION; + +const ASSET_HUB_ROCOCO_ED: AssetHubBalance = crate::ExistentialDeposit::get(); + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn asset_hub_rococo_session_keys(keys: AuraId) -> crate::SessionKeys { + crate::SessionKeys { aura: keys } +} + +fn asset_hub_rococo_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + endowment: AssetHubBalance, + id: ParaId, +) -> serde_json::Value { + serde_json::json!({ + "balances": crate::BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, endowment)) + .collect(), + }, + "parachainInfo": crate::ParachainInfoConfig { + parachain_id: id, + ..Default::default() + }, + "collatorSelection": crate::CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, + ..Default::default() + }, + "session": crate::SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + asset_hub_rococo_session_keys(aura), // session keys + ) + }) + .collect(), + ..Default::default() + }, + "polkadotXcm": crate::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + } + }) +} + +/// Encapsulates names of predefined presets. +mod preset_names { + pub const PRESET_DEVELOPMENT: &str = "development"; + pub const PRESET_LOCAL: &str = "local"; + pub const PRESET_GENESIS: &str = "genesis"; +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + use preset_names::*; + let patch = match id.try_into() { + Ok(PRESET_GENESIS) => asset_hub_rococo_genesis( + // initial collators. + vec![ + // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG + ( + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") + .unchecked_into(), + ), + // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri + ( + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") + .unchecked_into(), + ), + // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c + ( + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") + .unchecked_into(), + ), + // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt + ( + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") + .unchecked_into(), + ), + ], + Vec::new(), + ASSET_HUB_ROCOCO_ED * 524_288, + 1000.into(), + ), + Ok(PRESET_LOCAL) => asset_hub_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, + 1000.into(), + ), + Ok(PRESET_DEVELOPMENT) => asset_hub_rococo_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, + 1000.into(), + ), + Err(_) | Ok(_) => return None, + }; + + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + use preset_names::*; + vec![ + PresetId::from(PRESET_GENESIS), + PresetId::from(PRESET_DEVELOPMENT), + PresetId::from(PRESET_LOCAL), + ] +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 7414adacc448630d2895896d359ecf7f388bffbd..efe4a4052a991579735e09e8e301194c1fa55e39 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -24,6 +24,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -40,6 +41,7 @@ use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::AggregateMessageOrigin; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_genesis_builder::PresetId; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Saturating, Verify}, @@ -61,8 +63,7 @@ use frame_support::{ ord_parameter_types, parameter_types, traits::{ fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool, - ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter, - TransformOrigin, + ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -83,10 +84,13 @@ use sp_runtime::{Perbill, RuntimeDebug}; use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, - PoolAssetsConvertedConcreteId, TokenLocation, TokenLocationV3, - TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocationV3, + PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId, + TrustBackedAssetsPalletLocation, }; +#[cfg(test)] +mod tests; + #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -124,7 +128,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -262,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")] @@ -324,11 +328,11 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssets, - xcm::v3::Location, + xcm::v4::Location, >, - xcm::v3::Location, + xcm::v4::Location, AccountId, >; @@ -336,25 +340,25 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< pub type NativeAndAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, - TargetFromLeft, - xcm::v3::Location, + TargetFromLeft, + xcm::v4::Location, AccountId, >; pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, - (xcm::v3::Location, xcm::v3::Location), + (xcm::v4::Location, xcm::v4::Location), >; impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type AssetKind = xcm::v3::Location; + type AssetKind = xcm::v4::Location; type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< - TokenLocationV3, + TokenLocation, AccountId, Self::AssetKind, PoolIdToAccountId, @@ -362,7 +366,7 @@ impl pallet_asset_conversion::Config for Runtime { type PoolAssetId = u32; type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeAsset = TokenLocationV3; + type PoolSetupFeeAsset = TokenLocation; type PoolSetupFeeTarget = ResolveAssetTo; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; @@ -372,10 +376,10 @@ impl pallet_asset_conversion::Config for Runtime { type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< - TokenLocationV3, + TokenLocation, parachain_info::Pallet, xcm_config::TrustBackedAssetsPalletIndex, - xcm::v3::Location, + xcm::v4::Location, >; } @@ -409,17 +413,17 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v3::Location; - type AssetIdParameter = xcm::v3::Location; + type AssetId = xcm::v4::Location; + type AssetIdParameter = xcm::v4::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( - FromSiblingParachain, xcm::v3::Location>, - FromNetwork, + FromSiblingParachain, xcm::v4::Location>, + FromNetwork, ), ForeignCreatorsSovereignAccountOf, AccountId, - xcm::v3::Location, + xcm::v4::Location, >; type ForceOrigin = AssetsForceOrigin; type AssetDeposit = ForeignAssetsAssetDeposit; @@ -804,9 +808,9 @@ parameter_types! { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AssetId = xcm::v3::Location; + type AssetId = xcm::v4::Location; type OnChargeAssetTransaction = SwapAssetAdapter< - TokenLocationV3, + TokenLocation, NativeAndAssets, AssetConversion, ResolveAssetTo, @@ -912,29 +916,18 @@ impl pallet_nfts::Config for Runtime { /// consensus with dynamic fees and back-pressure. pub type ToWestendXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance3; impl pallet_xcm_bridge_hub_router::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; type UniversalLocation = xcm_config::UniversalLocation; + type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; type BridgedNetworkId = xcm_config::bridging::to_westend::WestendNetwork; type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - #[cfg(not(feature = "runtime-benchmarks"))] - type BridgeHubOrigin = EnsureXcm>; - #[cfg(feature = "runtime-benchmarks")] - type BridgeHubOrigin = EitherOfDiverse< - // for running benchmarks - EnsureRoot, - // for running tests with `--feature runtime-benchmarks` - EnsureXcm>, - >; - type ToBridgeHubSender = XcmpQueue; - type WithBridgeHubChannel = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider< - xcm_config::bridging::SiblingBridgeHubParaId, - Runtime, - >; + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; @@ -1019,7 +1012,6 @@ pub type SignedExtra = ( pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. -#[allow(deprecated)] pub type Migrations = ( InitStorageVersions, // unreleased @@ -1027,6 +1019,12 @@ pub type Migrations = ( cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, pallet_collator_selection::migration::v2::MigrationToV2, frame_support::migrations::RemovePallet, + // unreleased + pallet_assets::migration::next_asset_id::SetNextAssetId< + ConstU32<50_000_000>, + Runtime, + TrustBackedAssetsInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -1234,16 +1232,16 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - xcm::v3::Location, + xcm::v4::Location, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: xcm::v3::Location, asset2: xcm::v3::Location) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: xcm::v4::Location, asset2: xcm::v4::Location) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(asset1, asset2).ok() } } @@ -1767,12 +1765,12 @@ impl_runtime_apis! { build_state::(config) } - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + fn get_preset(id: &Option) -> Option> { + get_preset::(id, &genesis_config_presets::get_preset) } - fn preset_names() -> Vec { - vec![] + fn preset_names() -> Vec { + genesis_config_presets::preset_names() } } } @@ -1781,64 +1779,3 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{CENTS, MILLICENTS}; - use sp_runtime::traits::Zero; - use sp_weights::WeightToFee; - use testnet_parachains_constants::rococo::fee; - - /// We can fit at least 1000 transfers in a block. - #[test] - fn sane_block_weight() { - use pallet_balances::WeightInfo; - let block = RuntimeBlockWeights::get().max_block; - let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; - let transfer = - base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); - - let fit = block.checked_div_per_component(&transfer).unwrap_or_default(); - assert!(fit >= 1000, "{} should be at least 1000", fit); - } - - /// The fee for one transfer is at most 1 CENT. - #[test] - fn sane_transfer_fee() { - use pallet_balances::WeightInfo; - let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; - let transfer = - base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); - - let fee: Balance = fee::WeightToFee::weight_to_fee(&transfer); - assert!(fee <= CENTS, "{} MILLICENTS should be at most 1000", fee / MILLICENTS); - } - - /// Weight is being charged for both dimensions. - #[test] - fn weight_charged_for_both_components() { - let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(10_000, 0)); - assert!(!fee.is_zero(), "Charges for ref time"); - - let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, 10_000)); - assert_eq!(fee, CENTS, "10kb maps to CENT"); - } - - /// Filling up a block by proof size is at most 30 times more expensive than ref time. - /// - /// This is just a sanity check. - #[test] - fn full_block_fee_ratio() { - let block = RuntimeBlockWeights::get().max_block; - let time_fee: Balance = - fee::WeightToFee::weight_to_fee(&Weight::from_parts(block.ref_time(), 0)); - let proof_fee: Balance = - fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, block.proof_size())); - - let proof_o_time = proof_fee.checked_div(time_fee).unwrap_or_default(); - assert!(proof_o_time <= 30, "{} should be at most 30", proof_o_time); - let time_o_proof = time_fee.checked_div(proof_fee).unwrap_or_default(); - assert!(time_o_proof <= 30, "{} should be at most 30", time_o_proof); - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..12c0bc4e168800a7efd0a72daf75244e92b79255 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/tests/mod.rs @@ -0,0 +1,72 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Tests for the Rococo runtime. + +use super::*; +use crate::{CENTS, MILLICENTS}; +use sp_runtime::traits::Zero; +use sp_weights::WeightToFee; +use testnet_parachains_constants::rococo::fee; + +/// We can fit at least 1000 transfers in a block. +#[test] +fn sane_block_weight() { + use pallet_balances::WeightInfo; + let block = RuntimeBlockWeights::get().max_block; + let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; + let transfer = base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); + + let fit = block.checked_div_per_component(&transfer).unwrap_or_default(); + assert!(fit >= 1000, "{} should be at least 1000", fit); +} + +/// The fee for one transfer is at most 1 CENT. +#[test] +fn sane_transfer_fee() { + use pallet_balances::WeightInfo; + let base = RuntimeBlockWeights::get().get(DispatchClass::Normal).base_extrinsic; + let transfer = base + weights::pallet_balances::WeightInfo::::transfer_allow_death(); + + let fee: Balance = fee::WeightToFee::weight_to_fee(&transfer); + assert!(fee <= CENTS, "{} MILLICENTS should be at most 1000", fee / MILLICENTS); +} + +/// Weight is being charged for both dimensions. +#[test] +fn weight_charged_for_both_components() { + let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(10_000, 0)); + assert!(!fee.is_zero(), "Charges for ref time"); + + let fee: Balance = fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, 10_000)); + assert_eq!(fee, CENTS, "10kb maps to CENT"); +} + +/// Filling up a block by proof size is at most 30 times more expensive than ref time. +/// +/// This is just a sanity check. +#[test] +fn full_block_fee_ratio() { + let block = RuntimeBlockWeights::get().max_block; + let time_fee: Balance = + fee::WeightToFee::weight_to_fee(&Weight::from_parts(block.ref_time(), 0)); + let proof_fee: Balance = + fee::WeightToFee::weight_to_fee(&Weight::from_parts(0, block.proof_size())); + + let proof_o_time = proof_fee.checked_div(time_fee).unwrap_or_default(); + assert!(proof_o_time <= 30, "{} should be at most 30", proof_o_time); + let time_o_proof = time_fee.checked_div(proof_fee).unwrap_or_default(); + assert!(time_o_proof <= 30, "{} should be at most 30", time_o_proof); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/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/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 03d3785dccbd7f213eee3d8aa51cde7eb5980b48..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 @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-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: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 21_643_000 picoseconds. - Weight::from_parts(22_410_000, 3593) + // Minimum execution time: 34_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: 43_758_000 picoseconds. - Weight::from_parts(44_654_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,20 +90,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 87_978_000 picoseconds. - Weight::from_parts(88_517_000, 8799) + // Minimum execution time: 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)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) pub fn reserve_asset_deposited() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 6_883_000 picoseconds. - Weight::from_parts(6_979_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Estimated: `0` + // Minimum execution time: 1_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`) @@ -125,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 198_882_000 picoseconds. - Weight::from_parts(199_930_000, 6196) + // Minimum execution time: 108_117_000 picoseconds. + Weight::from_parts(110_416_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_343_000 picoseconds. - Weight::from_parts(3_487_000, 0) + // Minimum execution time: 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`) @@ -143,13 +140,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_399_000 picoseconds. - Weight::from_parts(19_659_000, 3593) + // Minimum execution time: 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)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -160,6 +155,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -168,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 59_017_000 picoseconds. - Weight::from_parts(60_543_000, 6196) + // Minimum execution time: 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)) } @@ -193,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 45_409_000 picoseconds. - Weight::from_parts(47_041_000, 3610) + // Minimum execution time: 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 a11dca4f6d7ccf8095c1a1095e436e131c508370..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,18 +54,18 @@ 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; parameter_types! { pub const TokenLocation: Location = Location::parent(); - pub const TokenLocationV3: xcm::v3::Location = xcm::v3::Location::parent(); pub const RelayNetwork: NetworkId = NetworkId::Rococo; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = @@ -74,8 +74,6 @@ parameter_types! { pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; - pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location = - xcm::v3::Junction::PalletInstance(::index() as u8).into(); pub ForeignAssetsPalletLocation: Location = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: Location = @@ -177,7 +175,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus, ), Balance, - xcm::v3::Location, + xcm::v4::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -329,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; @@ -361,7 +381,7 @@ impl xcm_executor::Config for XcmConfig { ResolveTo, >, cumulus_primitives_utility::SwapFirstAssetTrader< - TokenLocationV3, + TokenLocation, crate::AssetConversion, WeightToFee, crate::NativeAndAssets, @@ -369,7 +389,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, - xcm::v3::Location, + xcm::v4::Location, >, ForeignAssetsConvertedConcreteId, ), @@ -410,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, @@ -502,9 +522,9 @@ pub type ForeignCreatorsSovereignAccountOf = ( /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v3::Location { - xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> xcm::v4::Location { + xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(id)]) } } @@ -610,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 ee1461b7f9c85664e25dca1d02b4f248812d4219..6b0cf87a6f7a16e8a5e4af8a11d9bb9031f820fe 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, @@ -85,7 +85,7 @@ fn slot_durations() -> SlotDurations { fn setup_pool_for_paying_fees_with_foreign_assets( (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountId, - xcm::v3::Location, + Location, Balance, ), ) { @@ -93,7 +93,7 @@ fn setup_pool_for_paying_fees_with_foreign_assets( // setup a pool to pay fees with `foreign_asset_id_location` tokens let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = xcm::v3::Location::parent(); + let native_asset = Location::parent(); let pool_liquidity: Balance = existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); @@ -105,15 +105,15 @@ fn setup_pool_for_paying_fees_with_foreign_assets( assert_ok!(ForeignAssets::mint( RuntimeOrigin::signed(foreign_asset_owner), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), pool_owner.clone().into(), (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()) + Box::new(native_asset.clone().into()), + Box::new(foreign_asset_id_location.clone().into()) )); assert_ok!(AssetConversion::add_liquidity( @@ -217,24 +217,14 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") - ), - Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) - .expect("conversion works") - ) + Box::new(Location::try_from(native_location.clone()).expect("conversion works")), + Box::new(Location::try_from(asset_1_location.clone()).expect("conversion works")) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") - ), - Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) - .expect("conversion works") - ), + Box::new(Location::try_from(native_location.clone()).expect("conversion works")), + Box::new(Location::try_from(asset_1_location.clone()).expect("conversion works")), pool_liquidity, pool_liquidity, 1, @@ -270,8 +260,8 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = AssetConversion::get_reserves( - xcm::v3::Location::try_from(native_location).expect("conversion works"), - xcm::v3::Location::try_from(asset_1_location.clone()).expect("conversion works"), + Location::try_from(native_location).expect("conversion works"), + Location::try_from(asset_1_location.clone()).expect("conversion works"), ) .unwrap(); let asset_refund = @@ -309,14 +299,10 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let native_location = - xcm::v3::Location::try_from(TokenLocation::get()).expect("conversion works"); - let foreign_location = xcm::v3::Location { + Location::try_from(TokenLocation::get()).expect("conversion works"); + let foreign_location = Location { parents: 1, - interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), - ) - .into(), + interior: (Junction::Parachain(1234), Junction::GeneralIndex(12345)).into(), }; // bob's initial balance for native and `asset1` assets. let initial_balance = 200 * UNITS; @@ -325,26 +311,26 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { // init asset, balances and pool. assert_ok!(>::create( - foreign_location, + foreign_location.clone(), bob.clone(), true, 10 )); - assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); + assert_ok!(ForeignAssets::mint_into(foreign_location.clone(), &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location) + Box::new(native_location.clone()), + Box::new(foreign_location.clone()) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location), + Box::new(native_location.clone()), + Box::new(foreign_location.clone()), pool_liquidity, pool_liquidity, 1, @@ -353,11 +339,9 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { )); // keep initial total issuance to assert later. - let asset_total_issuance = ForeignAssets::total_issuance(foreign_location); + let asset_total_issuance = ForeignAssets::total_issuance(foreign_location.clone()); let native_total_issuance = Balances::total_issuance(); - let foreign_location_latest: Location = foreign_location.try_into().unwrap(); - // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); @@ -365,7 +349,7 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let payment: Asset = (foreign_location_latest.clone(), asset_fee + extra_amount).into(); + let payment: Asset = (foreign_location.clone(), asset_fee + extra_amount).into(); // init trader and buy weight. let mut trader = ::Trader::new(); @@ -373,13 +357,11 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); // assert. - let unused_amount = unused_asset - .fungible - .get(&foreign_location_latest.clone().into()) - .map_or(0, |a| *a); + let unused_amount = + unused_asset.fungible.get(&foreign_location.clone().into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!( - ForeignAssets::total_issuance(foreign_location), + ForeignAssets::total_issuance(foreign_location.clone()), asset_total_issuance + asset_fee ); @@ -387,13 +369,13 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = - AssetConversion::get_reserves(native_location, foreign_location).unwrap(); + AssetConversion::get_reserves(native_location, foreign_location.clone()).unwrap(); let asset_refund = AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); // refund. let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); - assert_eq!(actual_refund, (foreign_location_latest, asset_refund).into()); + assert_eq!(actual_refund, (foreign_location.clone(), asset_refund).into()); // assert. assert_eq!(Balances::balance(&staking_pot), initial_balance); @@ -500,17 +482,13 @@ fn test_foreign_asset_xcm_take_first_trader() { .execute_with(|| { // We need root origin to create a sufficient asset let minimum_asset_balance = 3333333_u128; - let foreign_location = xcm::v3::Location { + let foreign_location = Location { parents: 1, - interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), - ) - .into(), + interior: (Junction::Parachain(1234), Junction::GeneralIndex(12345)).into(), }; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), true, minimum_asset_balance @@ -519,13 +497,11 @@ fn test_foreign_asset_xcm_take_first_trader() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(ALICE)), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), minimum_asset_balance )); - let asset_location_v4: Location = foreign_location.try_into().unwrap(); - // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -535,7 +511,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets calculate amount needed let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( - foreign_location, + foreign_location.clone(), bought ) .expect("failed to compute"); @@ -543,7 +519,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets pay with: asset_amount_needed + asset_amount_extra let asset_amount_extra = 100_u128; let asset: Asset = - (asset_location_v4.clone(), asset_amount_needed + asset_amount_extra).into(); + (foreign_location.clone(), asset_amount_needed + asset_amount_extra).into(); let mut trader = ::Trader::new(); let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; @@ -551,16 +527,15 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets buy_weight and make sure buy_weight does not return an error let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok"); // Check whether a correct amount of unused assets is returned - assert_ok!( - unused_assets.ensure_contains(&(asset_location_v4, asset_amount_extra).into()) - ); + assert_ok!(unused_assets + .ensure_contains(&(foreign_location.clone(), asset_amount_extra).into())); // Drop trader drop(trader); // Make sure author(Alice) has received the amount assert_eq!( - ForeignAssets::balance(foreign_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_location.clone(), AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); @@ -841,15 +816,13 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; - let foreign_asset_id_location = xcm::v3::Location::new( - 1, - [xcm::v3::Junction::Parachain(1234), xcm::v3::Junction::GeneralIndex(12345)], - ); + let foreign_asset_id_location = + Location::new(1, [Junction::Parachain(1234), Junction::GeneralIndex(12345)]); // check before assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 0 ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 0); @@ -886,7 +859,7 @@ fn test_assets_balances_api_works() { let foreign_asset_minimum_asset_balance = 3333333_u128; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(SOME_ASSET_ADMIN).into(), false, foreign_asset_minimum_asset_balance @@ -895,7 +868,7 @@ fn test_assets_balances_api_works() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(SOME_ASSET_ADMIN)), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(ALICE).into(), 6 * foreign_asset_minimum_asset_balance )); @@ -906,7 +879,7 @@ fn test_assets_balances_api_works() { minimum_asset_balance ); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 6 * minimum_asset_balance ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), some_currency); @@ -932,10 +905,8 @@ fn test_assets_balances_api_works() { .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - WithLatestLocationConverter::::convert_back( - &foreign_asset_id_location - ) - .unwrap(), + WithLatestLocationConverter::::convert_back(&foreign_asset_id_location) + .unwrap(), 6 * foreign_asset_minimum_asset_balance ) .into()))); @@ -1025,14 +996,11 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ Runtime, XcmConfig, ForeignAssetsInstance, - xcm::v3::Location, + Location, JustTry, collator_session_keys(), ExistentialDeposit::get(), - xcm::v3::Location::new( - 1, - [xcm::v3::Junction::Parachain(1313), xcm::v3::Junction::GeneralIndex(12345)] - ), + Location::new(1, [Junction::Parachain(1313), Junction::GeneralIndex(12345)]), Box::new(|| { assert!(Assets::asset_ids().collect::>().is_empty()); }), @@ -1047,8 +1015,8 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p WeightToFee, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, - xcm::v3::Location, - WithLatestLocationConverter, + Location, + WithLatestLocationConverter, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), @@ -1138,16 +1106,17 @@ mod asset_hub_rococo_tests { let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = xcm::v3::Location::new( - 2, - [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Westend)], - ); + let foreign_asset_id_location = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); - let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + let foreign_asset_create_params = ( + foreign_asset_owner, + foreign_asset_id_location.clone(), + foreign_asset_id_minimum_balance, + ); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1181,7 +1150,7 @@ mod asset_hub_rococo_tests { // check now foreign asset for staking pot assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1195,7 +1164,7 @@ mod asset_hub_rococo_tests { // staking pot receives no foreign assets assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1211,16 +1180,17 @@ mod asset_hub_rococo_tests { let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = xcm::v3::Location::new( - 2, - [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Westend)], - ); + let foreign_asset_id_location = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); - let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + let foreign_asset_create_params = ( + foreign_asset_owner, + foreign_asset_id_location.clone(), + foreign_asset_id_minimum_balance, + ); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1245,7 +1215,7 @@ mod asset_hub_rococo_tests { // check block author before assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ), 0 @@ -1255,7 +1225,7 @@ mod asset_hub_rococo_tests { // `TakeFirstAssetTrader` puts fees to the block author assert!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ) > 0 ); @@ -1265,94 +1235,6 @@ mod asset_hub_rococo_tests { ) } - #[test] - fn report_bridge_status_from_xcm_bridge_router_for_westend_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - ToWestendXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_westend, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: false, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - ) - } - - #[test] - fn test_report_bridge_status_call_compatibility() { - // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding - assert_eq!( - RuntimeCall::ToWestendXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode(), - bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode() - ); - } - - #[test] - fn check_sane_weight_report_bridge_status_for_westend() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); - let max_weight = bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(); - assert!( - actual.all_lte(max_weight), - "max_weight: {:?} should be adjusted to actual {:?}", - max_weight, - actual - ); - } - #[test] fn reserve_transfer_native_asset_to_non_teleport_para_works() { asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::< diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 2f244a07e8f13a4ac467fb7394e3afaab6ce5b8b..77130ff846b5ad32dd760dd9364c9685dd619193 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -262,4 +262,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 201698ecb7f9b383f1d85f01a764420155bbc9cd..712b5c0ada949c456fadc9fde3bfe5d6669677f2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -45,8 +45,8 @@ use frame_support::{ traits::{ fungible, fungibles, tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Equals, - InstanceFilter, TransformOrigin, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -57,7 +57,6 @@ use frame_system::{ }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; -use pallet_xcm::EnsureXcm; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, @@ -80,8 +79,7 @@ use testnet_parachains_constants::westend::{ use xcm_config::{ ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocationV3, WestendLocation, WestendLocationV3, - XcmOriginToTransactDispatchOrigin, + TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -128,7 +126,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -265,7 +263,7 @@ impl pallet_assets::Config for Runtime { type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; - type CallbackHandle = (); + type CallbackHandle = pallet_assets::AutoIncAssetId; type AssetAccountDeposit = AssetAccountDeposit; type RemoveItemsLimit = ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] @@ -326,11 +324,11 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssets, - xcm::v3::Location, + xcm::v4::Location, >, - xcm::v3::Location, + xcm::v4::Location, AccountId, >; @@ -338,25 +336,25 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< pub type NativeAndAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, - TargetFromLeft, - xcm::v3::Location, + TargetFromLeft, + xcm::v4::Location, AccountId, >; pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, - (xcm::v3::Location, xcm::v3::Location), + (xcm::v4::Location, xcm::v4::Location), >; impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type AssetKind = xcm::v3::Location; + type AssetKind = xcm::v4::Location; type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< - WestendLocationV3, + WestendLocation, AccountId, Self::AssetKind, PoolIdToAccountId, @@ -364,7 +362,7 @@ impl pallet_asset_conversion::Config for Runtime { type PoolAssetId = u32; type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeAsset = WestendLocationV3; + type PoolSetupFeeAsset = WestendLocation; type PoolSetupFeeTarget = ResolveAssetTo; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; @@ -374,10 +372,10 @@ impl pallet_asset_conversion::Config for Runtime { type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< - WestendLocationV3, + WestendLocation, parachain_info::Pallet, xcm_config::TrustBackedAssetsPalletIndex, - xcm::v3::Location, + xcm::v4::Location, >; } @@ -411,17 +409,17 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v3::Location; - type AssetIdParameter = xcm::v3::Location; + type AssetId = xcm::v4::Location; + type AssetIdParameter = xcm::v4::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( - FromSiblingParachain, xcm::v3::Location>, - FromNetwork, + FromSiblingParachain, xcm::v4::Location>, + FromNetwork, ), ForeignCreatorsSovereignAccountOf, AccountId, - xcm::v3::Location, + xcm::v4::Location, >; type ForceOrigin = AssetsForceOrigin; type AssetDeposit = ForeignAssetsAssetDeposit; @@ -801,9 +799,9 @@ parameter_types! { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AssetId = xcm::v3::Location; + type AssetId = xcm::v4::Location; type OnChargeAssetTransaction = SwapAssetAdapter< - WestendLocationV3, + WestendLocation, NativeAndAssets, AssetConversion, ResolveAssetTo, @@ -909,29 +907,18 @@ impl pallet_nfts::Config for Runtime { /// consensus with dynamic fees and back-pressure. pub type ToRococoXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance1; impl pallet_xcm_bridge_hub_router::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; type UniversalLocation = xcm_config::UniversalLocation; + type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; type BridgedNetworkId = xcm_config::bridging::to_rococo::RococoNetwork; type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - #[cfg(not(feature = "runtime-benchmarks"))] - type BridgeHubOrigin = EnsureXcm>; - #[cfg(feature = "runtime-benchmarks")] - type BridgeHubOrigin = frame_support::traits::EitherOfDiverse< - // for running benchmarks - EnsureRoot, - // for running tests with `--feature runtime-benchmarks` - EnsureXcm>, - >; - type ToBridgeHubSender = XcmpQueue; - type WithBridgeHubChannel = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider< - xcm_config::bridging::SiblingBridgeHubParaId, - Runtime, - >; + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; @@ -1034,6 +1021,12 @@ pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + // unreleased + pallet_assets::migration::next_asset_id::SetNextAssetId< + ConstU32<50_000_000>, + Runtime, + TrustBackedAssetsInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -1331,18 +1324,18 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - xcm::v3::Location, + xcm::v4::Location, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: xcm::v3::Location, asset2: xcm::v3::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: xcm::v3::Location, asset2: xcm::v3::Location) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: xcm::v4::Location, asset2: xcm::v4::Location) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(asset1, asset2).ok() } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/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/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index fe8d186139256fbaac3989e0c56fb3acc494d63e..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 @@ -1,24 +1,25 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-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: @@ -53,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 20_295_000 picoseconds. - Weight::from_parts(21_142_000, 3593) + // Minimum execution time: 32_651_000 picoseconds. + Weight::from_parts(33_225_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -64,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 42_356_000 picoseconds. - Weight::from_parts(43_552_000, 6196) + // Minimum execution time: 41_059_000 picoseconds. + Weight::from_parts(41_730_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -89,20 +90,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 85_553_000 picoseconds. - Weight::from_parts(87_177_000, 8799) + // Minimum execution time: 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)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) pub fn reserve_asset_deposited() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 6_166_000 picoseconds. - Weight::from_parts(6_352_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Estimated: `0` + // Minimum execution time: 1_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`) @@ -124,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 184_462_000 picoseconds. - Weight::from_parts(189_593_000, 6196) + // Minimum execution time: 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)) } @@ -133,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_018_000 picoseconds. - Weight::from_parts(3_098_000, 0) + // Minimum execution time: 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`) @@ -142,13 +140,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 18_583_000 picoseconds. - Weight::from_parts(19_057_000, 3593) + // Minimum execution time: 25_234_000 picoseconds. + Weight::from_parts(25_561_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -159,6 +155,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -167,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 56_666_000 picoseconds. - Weight::from_parts(58_152_000, 6196) + // Minimum execution time: 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)) } @@ -192,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 44_197_000 picoseconds. - Weight::from_parts(45_573_000, 3610) + // Minimum execution time: 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 bf45e146e33400fdf9d1a7b094353087be05e19f..cc96bd59cb4880649224632f653b4fc6a392ab2f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ XcmpQueue, }; use assets_common::{ - matching::{FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromSiblingParachain, IsForeignConcreteAsset, ParentLocation}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -43,7 +43,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; -use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, @@ -51,18 +51,18 @@ use xcm_builder::{ DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, + MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; parameter_types! { pub const WestendLocation: Location = Location::parent(); - pub const WestendLocationV3: xcm::v3::Location = xcm::v3::Location::parent(); pub const RelayNetwork: Option = Some(NetworkId::Westend); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = @@ -71,8 +71,6 @@ parameter_types! { pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; - pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location = - xcm::v3::Junction::PalletInstance(::index() as u8).into(); pub ForeignAssetsPalletLocation: Location = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: Location = @@ -174,7 +172,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus, ), Balance, - xcm::v3::Location, + xcm::v4::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -353,6 +351,28 @@ pub type TrustedTeleporters = ( IsForeignConcreteAsset>>, ); +/// Asset converter for pool assets. +/// Used to convert one asset to another, when there is a pool available between the two. +/// This type thus allows paying fees with any asset as long as there is a pool between said +/// asset and the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation, + ForeignAssetsConvertedConcreteId, + // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. + MatchedConvertedConcreteId< + xcm::v4::Location, + Balance, + Equals, + WithLatestLocationConverter, + TryConvertInto, + >, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -384,7 +404,7 @@ impl xcm_executor::Config for XcmConfig { ResolveTo, >, cumulus_primitives_utility::SwapFirstAssetTrader< - WestendLocationV3, + WestendLocation, crate::AssetConversion, WeightToFee, crate::NativeAndAssets, @@ -392,7 +412,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, - xcm::v3::Location, + xcm::v4::Location, >, ForeignAssetsConvertedConcreteId, ), @@ -433,7 +453,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, SendXcmFeeToAccount, @@ -526,9 +546,9 @@ pub type ForeignCreatorsSovereignAccountOf = ( /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v3::Location { - xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> xcm::v4::Location { + xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(id)]) } } @@ -631,17 +651,6 @@ pub mod bridging { (StartsWith, StartsWith), AssetHubRococo, >; - - impl Contains for ToRococoXcmRouter { - fn contains(call: &RuntimeCall) -> bool { - matches!( - call, - RuntimeCall::ToRococoXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } - ) - ) - } - } } pub mod to_ethereum { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 48e6c11d268c7c2e915c3ce4c0213ede2c883911..ad3c450eb375d1bb85da7230b3c9a30ec8fd911f 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::{ @@ -86,7 +86,7 @@ fn slot_durations() -> SlotDurations { fn setup_pool_for_paying_fees_with_foreign_assets( (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountId, - xcm::v3::Location, + xcm::v4::Location, Balance, ), ) { @@ -94,7 +94,7 @@ fn setup_pool_for_paying_fees_with_foreign_assets( // setup a pool to pay fees with `foreign_asset_id_location` tokens let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = xcm::v3::Location::parent(); + let native_asset = xcm::v4::Location::parent(); let pool_liquidity: Balance = existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); @@ -106,15 +106,15 @@ fn setup_pool_for_paying_fees_with_foreign_assets( assert_ok!(ForeignAssets::mint( RuntimeOrigin::signed(foreign_asset_owner), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), pool_owner.clone().into(), (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()) + Box::new(native_asset.clone().into()), + Box::new(foreign_asset_id_location.clone().into()) )); assert_ok!(AssetConversion::add_liquidity( @@ -219,10 +219,10 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + xcm::v4::Location::try_from(native_location.clone()).expect("conversion works") ), Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) + xcm::v4::Location::try_from(asset_1_location.clone()) .expect("conversion works") ) )); @@ -230,10 +230,10 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), Box::new( - xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + xcm::v4::Location::try_from(native_location.clone()).expect("conversion works") ), Box::new( - xcm::v3::Location::try_from(asset_1_location.clone()) + xcm::v4::Location::try_from(asset_1_location.clone()) .expect("conversion works") ), pool_liquidity, @@ -271,8 +271,8 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = AssetConversion::get_reserves( - xcm::v3::Location::try_from(native_location).expect("conversion works"), - xcm::v3::Location::try_from(asset_1_location.clone()).expect("conversion works"), + xcm::v4::Location::try_from(native_location).expect("conversion works"), + xcm::v4::Location::try_from(asset_1_location.clone()).expect("conversion works"), ) .unwrap(); let asset_refund = @@ -310,12 +310,12 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let native_location = - xcm::v3::Location::try_from(WestendLocation::get()).expect("conversion works"); - let foreign_location = xcm::v3::Location { + xcm::v4::Location::try_from(WestendLocation::get()).expect("conversion works"); + let foreign_location = xcm::v4::Location { parents: 1, interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), + xcm::v4::Junction::Parachain(1234), + xcm::v4::Junction::GeneralIndex(12345), ) .into(), }; @@ -326,26 +326,26 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { // init asset, balances and pool. assert_ok!(>::create( - foreign_location, + foreign_location.clone(), bob.clone(), true, 10 )); - assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); + assert_ok!(ForeignAssets::mint_into(foreign_location.clone(), &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location) + Box::new(native_location.clone()), + Box::new(foreign_location.clone()) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(foreign_location), + Box::new(native_location.clone()), + Box::new(foreign_location.clone()), pool_liquidity, pool_liquidity, 1, @@ -354,11 +354,9 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { )); // keep initial total issuance to assert later. - let asset_total_issuance = ForeignAssets::total_issuance(foreign_location); + let asset_total_issuance = ForeignAssets::total_issuance(foreign_location.clone()); let native_total_issuance = Balances::total_issuance(); - let foreign_location_latest: Location = foreign_location.try_into().unwrap(); - // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); @@ -366,7 +364,7 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let payment: Asset = (foreign_location_latest.clone(), asset_fee + extra_amount).into(); + let payment: Asset = (foreign_location.clone(), asset_fee + extra_amount).into(); // init trader and buy weight. let mut trader = ::Trader::new(); @@ -374,13 +372,11 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); // assert. - let unused_amount = unused_asset - .fungible - .get(&foreign_location_latest.clone().into()) - .map_or(0, |a| *a); + let unused_amount = + unused_asset.fungible.get(&foreign_location.clone().into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!( - ForeignAssets::total_issuance(foreign_location), + ForeignAssets::total_issuance(foreign_location.clone()), asset_total_issuance + asset_fee ); @@ -388,13 +384,13 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = - AssetConversion::get_reserves(native_location, foreign_location).unwrap(); + AssetConversion::get_reserves(native_location, foreign_location.clone()).unwrap(); let asset_refund = AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); // refund. let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); - assert_eq!(actual_refund, (foreign_location_latest, asset_refund).into()); + assert_eq!(actual_refund, (foreign_location.clone(), asset_refund).into()); // assert. assert_eq!(Balances::balance(&staking_pot), initial_balance); @@ -501,17 +497,17 @@ fn test_foreign_asset_xcm_take_first_trader() { .execute_with(|| { // We need root origin to create a sufficient asset let minimum_asset_balance = 3333333_u128; - let foreign_location = xcm::v3::Location { + let foreign_location = xcm::v4::Location { parents: 1, interior: ( - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), + xcm::v4::Junction::Parachain(1234), + xcm::v4::Junction::GeneralIndex(12345), ) .into(), }; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), true, minimum_asset_balance @@ -520,12 +516,12 @@ fn test_foreign_asset_xcm_take_first_trader() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(ALICE)), - foreign_location.into(), + foreign_location.clone().into(), AccountId::from(ALICE).into(), minimum_asset_balance )); - let asset_location_v4: Location = foreign_location.try_into().unwrap(); + let asset_location_v4: Location = foreign_location.clone().try_into().unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -534,7 +530,7 @@ fn test_foreign_asset_xcm_take_first_trader() { let bought = Weight::from_parts(4_000_000_000u64, 0); // Lets calculate amount needed - let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(foreign_location, bought) + let asset_amount_needed = ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(foreign_location.clone(), bought) .expect("failed to compute"); // Lets pay with: asset_amount_needed + asset_amount_extra @@ -557,7 +553,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Make sure author(Alice) has received the amount assert_eq!( - ForeignAssets::balance(foreign_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_location.clone(), AccountId::from(ALICE)), minimum_asset_balance + asset_amount_needed ); @@ -837,11 +833,11 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; - let foreign_asset_id_location = xcm::v3::Location { + let foreign_asset_id_location = xcm::v4::Location { parents: 1, interior: [ - xcm::v3::Junction::Parachain(1234), - xcm::v3::Junction::GeneralIndex(12345), + xcm::v4::Junction::Parachain(1234), + xcm::v4::Junction::GeneralIndex(12345), ] .into(), }; @@ -849,7 +845,7 @@ fn test_assets_balances_api_works() { // check before assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 0 ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 0); @@ -886,7 +882,7 @@ fn test_assets_balances_api_works() { let foreign_asset_minimum_asset_balance = 3333333_u128; assert_ok!(ForeignAssets::force_create( RuntimeHelper::root_origin(), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(SOME_ASSET_ADMIN).into(), false, foreign_asset_minimum_asset_balance @@ -895,7 +891,7 @@ fn test_assets_balances_api_works() { // We first mint enough asset for the account to exist for assets assert_ok!(ForeignAssets::mint( RuntimeHelper::origin_of(AccountId::from(SOME_ASSET_ADMIN)), - foreign_asset_id_location, + foreign_asset_id_location.clone(), AccountId::from(ALICE).into(), 6 * foreign_asset_minimum_asset_balance )); @@ -906,7 +902,7 @@ fn test_assets_balances_api_works() { minimum_asset_balance ); assert_eq!( - ForeignAssets::balance(foreign_asset_id_location, AccountId::from(ALICE)), + ForeignAssets::balance(foreign_asset_id_location.clone(), AccountId::from(ALICE)), 6 * minimum_asset_balance ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), some_currency); @@ -932,7 +928,7 @@ fn test_assets_balances_api_works() { .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - WithLatestLocationConverter::::convert_back( + WithLatestLocationConverter::::convert_back( &foreign_asset_id_location ) .unwrap(), @@ -1025,13 +1021,13 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ Runtime, XcmConfig, ForeignAssetsInstance, - xcm::v3::Location, + xcm::v4::Location, JustTry, collator_session_keys(), ExistentialDeposit::get(), - xcm::v3::Location { + xcm::v4::Location { parents: 1, - interior: [xcm::v3::Junction::Parachain(1313), xcm::v3::Junction::GeneralIndex(12345)] + interior: [xcm::v4::Junction::Parachain(1313), xcm::v4::Junction::GeneralIndex(12345)] .into() }, Box::new(|| { @@ -1048,8 +1044,8 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p WeightToFee, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, - xcm::v3::Location, - WithLatestLocationConverter, + xcm::v4::Location, + WithLatestLocationConverter, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), @@ -1127,12 +1123,12 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s let staking_pot = StakingPot::get(); let foreign_asset_id_location = - xcm::v3::Location::new(2, [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Rococo)]); + xcm::v4::Location::new(2, [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::Rococo)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + (foreign_asset_owner, foreign_asset_id_location.clone(), foreign_asset_id_minimum_balance); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1166,7 +1162,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s // check now foreign asset for staking pot assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1180,7 +1176,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s // staking pot receives no foreign assets assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &staking_pot ), 0 @@ -1196,12 +1192,12 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic let staking_pot = StakingPot::get(); let foreign_asset_id_location = - xcm::v3::Location::new(2, [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::Rococo)]); + xcm::v4::Location::new(2, [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::Rococo)]); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); let foreign_asset_create_params = - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance); + (foreign_asset_owner, foreign_asset_id_location.clone(), foreign_asset_id_minimum_balance); asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_works::< Runtime, @@ -1226,7 +1222,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic // check block author before assert_eq!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ), 0 @@ -1236,7 +1232,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic // `TakeFirstAssetTrader` puts fees to the block author assert!( ForeignAssets::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &block_author_account ) > 0 ); @@ -1246,92 +1242,6 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic ) } -#[test] -fn report_bridge_status_from_xcm_bridge_router_for_rococo_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - ToRococoXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_rococo, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: false, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - ) -} - -#[test] -fn test_report_bridge_status_call_compatibility() { - // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding - assert_eq!( - RuntimeCall::ToRococoXcmRouter(pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }) - .encode(), - bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode() - ) -} - -#[test] -fn check_sane_weight_report_bridge_status() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); - let max_weight = bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(); - assert!( - actual.all_lte(max_weight), - "max_weight: {:?} should be adjusted to actual {:?}", - max_weight, - actual - ); -} - #[test] fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { asset_test_utils::test_cases::change_storage_constant_by_governance_works::< diff --git a/cumulus/parachains/runtimes/assets/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 884b71369e79ad9713ea3cc8e243820862c9c20d..67b585ecfe86ff71f59888524353c12af1bc44c8 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -367,9 +367,9 @@ pub fn teleports_for_foreign_assets_works< ::Balance: From + Into, SovereignAccountOf: ConvertLocation>, >::AssetId: - From + Into, + From + Into, >::AssetIdParameter: - From + Into, + From + Into, >::Balance: From + Into, ::AccountId: @@ -381,11 +381,11 @@ pub fn teleports_for_foreign_assets_works< { // foreign parachain with the same consensus currency as asset let foreign_para_id = 2222; - let foreign_asset_id_location = xcm::v3::Location { + let foreign_asset_id_location = xcm::v4::Location { parents: 1, interior: [ - xcm::v3::Junction::Parachain(foreign_para_id), - xcm::v3::Junction::GeneralIndex(1234567), + xcm::v4::Junction::Parachain(foreign_para_id), + xcm::v4::Junction::GeneralIndex(1234567), ] .into(), }; @@ -438,14 +438,14 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), 0.into() ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &CheckingAccount::get() ), 0.into() @@ -454,14 +454,14 @@ pub fn teleports_for_foreign_assets_works< assert_total::< pallet_assets::Pallet, AccountIdOf, - >(foreign_asset_id_location, 0, 0); + >(foreign_asset_id_location.clone(), 0, 0); // create foreign asset (0 total issuance) let asset_minimum_asset_balance = 3333333_u128; assert_ok!( >::force_create( RuntimeHelper::::root_origin(), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), asset_owner.into(), false, asset_minimum_asset_balance.into() @@ -470,12 +470,9 @@ pub fn teleports_for_foreign_assets_works< assert_total::< pallet_assets::Pallet, AccountIdOf, - >(foreign_asset_id_location, 0, 0); + >(foreign_asset_id_location.clone(), 0, 0); assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance); - let foreign_asset_id_location_latest: Location = - foreign_asset_id_location.try_into().unwrap(); - // 1. process received teleported assets from sibling parachain (foreign_para_id) let xcm = Xcm(vec![ // BuyExecution with relaychain native token @@ -489,12 +486,12 @@ pub fn teleports_for_foreign_assets_works< }, // Process teleported asset ReceiveTeleportedAsset(Assets::from(vec![Asset { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: Fungible(teleported_foreign_asset_amount), }])), DepositAsset { assets: Wild(AllOf { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: WildFungibility::Fungible, }), beneficiary: Location { @@ -526,7 +523,7 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), teleported_foreign_asset_amount.into() @@ -538,7 +535,7 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &CheckingAccount::get() ), 0.into() @@ -548,7 +545,7 @@ pub fn teleports_for_foreign_assets_works< pallet_assets::Pallet, AccountIdOf, >( - foreign_asset_id_location, + foreign_asset_id_location.clone(), teleported_foreign_asset_amount, teleported_foreign_asset_amount, ); @@ -566,7 +563,7 @@ pub fn teleports_for_foreign_assets_works< let target_account_balance_before_teleport = >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account, ); let asset_to_teleport_away = asset_minimum_asset_balance * 3; @@ -580,7 +577,7 @@ pub fn teleports_for_foreign_assets_works< // Make sure the target account has enough native asset to pay for delivery fees let delivery_fees = xcm_helpers::teleport_assets_delivery_fees::( - (foreign_asset_id_location_latest.clone(), asset_to_teleport_away).into(), + (foreign_asset_id_location.clone(), asset_to_teleport_away).into(), 0, Unlimited, dest_beneficiary.clone(), @@ -596,7 +593,7 @@ pub fn teleports_for_foreign_assets_works< RuntimeHelper::::origin_of(target_account.clone()), dest, dest_beneficiary, - (foreign_asset_id_location_latest.clone(), asset_to_teleport_away), + (foreign_asset_id_location.clone(), asset_to_teleport_away), Some((runtime_para_id, foreign_para_id)), included_head, &alice, @@ -606,14 +603,14 @@ pub fn teleports_for_foreign_assets_works< // check balances assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), (target_account_balance_before_teleport - asset_to_teleport_away.into()) ); assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &CheckingAccount::get() ), 0.into() @@ -623,7 +620,7 @@ pub fn teleports_for_foreign_assets_works< pallet_assets::Pallet, AccountIdOf, >( - foreign_asset_id_location, + foreign_asset_id_location.clone(), teleported_foreign_asset_amount - asset_to_teleport_away, teleported_foreign_asset_amount - asset_to_teleport_away, ); @@ -1559,9 +1556,6 @@ pub fn reserve_transfer_native_asset_to_non_teleport_para_works< ) .unwrap(); - let v4_xcm: Xcm<()> = xcm_sent.clone().try_into().unwrap(); - dbg!(&v4_xcm); - let delivery_fees = get_fungible_delivery_fees::< ::XcmSender, >(dest.clone(), Xcm::try_from(xcm_sent.clone()).unwrap()); diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index 0b2364dbb8bd858a95f6a109c800032cb286412b..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 @@ -331,7 +331,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< block_author_account: AccountIdOf, (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountIdOf, - xcm::v3::Location, + xcm::v4::Location, u128, ), foreign_asset_id_amount_to_transfer: u128, @@ -357,9 +357,9 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< BalanceOf: From + Into, XcmConfig: xcm_executor::Config, >::AssetId: - From + Into, + From + Into, >::AssetIdParameter: - From + Into, + From + Into, >::Balance: From + Into + From, ::AccountId: Into<<::RuntimeOrigin as OriginTrait>::AccountId> @@ -390,7 +390,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< assert_ok!( >::force_create( RuntimeHelper::::root_origin(), - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), foreign_asset_owner.into(), true, // is_sufficient=true foreign_asset_id_minimum_balance.into() @@ -409,7 +409,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< // ForeignAssets balances before assert_eq!( >::balance( - foreign_asset_id_location.into(), + foreign_asset_id_location.clone().into(), &target_account ), 0.into() @@ -418,11 +418,8 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< // additional check before additional_checks_before(); - let foreign_asset_id_location_latest: Location = - foreign_asset_id_location.try_into().unwrap(); - let expected_assets = Assets::from(vec![Asset { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: Fungible(foreign_asset_id_amount_to_transfer), }]); let expected_beneficiary = Location::new( @@ -439,7 +436,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< ClearOrigin, BuyExecution { fees: Asset { - id: AssetId(foreign_asset_id_location_latest.clone()), + id: AssetId(foreign_asset_id_location.clone()), fun: Fungible(foreign_asset_id_amount_to_transfer), }, weight_limit: Unlimited, @@ -545,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 f3e03aa743005f9fa79c8a071dba9bde222007e3..9fa1f3b1602c74356151e7beeb33ac0c23c29abf 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -3,7 +3,7 @@ name = "bridge-hub-rococo-runtime" version = "0.5.0" authors.workspace = true edition.workspace = true -description = "Rococo's BridgeHub parachain runtime" +description = "Rococo's BridgeHub parachain runtime" license = "Apache-2.0" [lints] @@ -97,7 +97,7 @@ bp-parachains = { workspace = true } bp-polkadot-bulletin = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +bp-runtime = { workspace = true } bp-rococo = { workspace = true } bp-westend = { workspace = true } pallet-bridge-grandpa = { workspace = true } @@ -123,9 +123,8 @@ bridge-hub-common = { workspace = true } [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } -bridge-runtime-common = { features = [ - "integrity-test", -], workspace = true, default-features = true } +bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } +pallet-bridge-relayers = { features = ["integrity-test"], workspace = true } sp-keyring = { workspace = true, default-features = true } snowbridge-runtime-test-common = { workspace = true, default-features = true } @@ -305,4 +304,4 @@ fast-runtime = [] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 83b92918dc4f442d3574d0476d25de90e9dc160a..00d902486c850f0fc99219beb81bce3c9c830c78 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -20,31 +20,33 @@ //! are reusing Polkadot Bulletin chain primitives everywhere here. use crate::{ - weights, xcm_config::UniversalLocation, BridgeRococoBulletinGrandpa, - BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverRococoBulletin, - XcmRouter, + weights, xcm_config::UniversalLocation, AccountId, Balance, Balances, + BridgeRococoBulletinGrandpa, BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverRococoBulletin, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, -}; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, + target_chain::FromBridgedChainMessagesProof, }; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; -use frame_support::{parameter_types, traits::PalletInfoAccess}; +use frame_support::{ + parameter_types, + traits::{Equals, PalletInfoAccess}, +}; +use frame_system::EnsureRoot; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm::EnsureXcm; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { /// Interior location (relative to this runtime) of the with-RococoBulletin messages pallet. @@ -58,12 +60,6 @@ parameter_types! { 2, [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())] ); - /// All active lanes that the current bridge supports. - pub ActiveOutboundLanesToRococoBulletin: &'static [bp_messages::LaneId] - = &[XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN]; - /// Lane identifier, used to connect Rococo People and Rococo Bulletin chain. - pub const RococoPeopleToRococoBulletinMessagesLane: bp_messages::LaneId - = XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN; // see the `FEE_BOOST_PER_RELAY_HEADER` constant get the meaning of this value pub PriorityBoostPerRelayHeader: u64 = 58_014_163_614_163; @@ -75,26 +71,11 @@ parameter_types! { /// meaning of this value. pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - /// Identifier of the sibling Rococo People parachain. - pub RococoPeopleParaId: cumulus_primitives_core::ParaId = rococo_runtime_constants::system_parachain::PEOPLE_ID.into(); - /// A route (XCM location and bridge lane) that the Rococo People Chain -> Rococo Bulletin Chain - /// message is following. - pub FromRococoPeopleToRococoBulletinRoute: SenderAndLane = SenderAndLane::new( - ParentThen(Parachain(RococoPeopleParaId::get().into()).into()).into(), - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, - ); - /// All active routes and their destinations. - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromRococoPeopleToRococoBulletinRoute::get(), - (RococoBulletinGlobalConsensusNetwork::get(), Here) - ) - ]; + /// PeopleRococo location + pub PeopleRococoLocation: Location = Location::new(1, [Parachain(rococo_runtime_constants::system_parachain::PEOPLE_ID)]); - /// XCM message that is never sent. - pub NeverSentMessage: Option> = None; + pub storage BridgeDeposit: Balance = 5 * ROC; } -pub const XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN: LaneId = LaneId([0, 0, 0, 0]); /// Proof of messages, coming from Rococo Bulletin chain. pub type FromRococoBulletinMessagesProof = @@ -110,34 +91,15 @@ type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher< BridgeRococoToRococoBulletinMessagesPalletInstance, >; -/// Export XCM messages to be relayed to the other side -pub type ToRococoBulletinHaulBlobExporter = XcmOverRococoBulletin; - -pub struct ToRococoBulletinXcmBlobHauler; -impl XcmBlobHauler for ToRococoBulletinXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithRococoBulletinMessagesInstance; - type ToSourceChainSender = XcmRouter; - type CongestedMessage = NeverSentMessage; - type UncongestedMessage = NeverSentMessage; -} - -/// On messages delivered callback. -type OnMessagesDeliveredFromRococoBulletin = - XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Rococo Bulletin /// chain. -pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubRococoRefundRococoBulletinMessages, Runtime, - RefundableMessagesLane< - WithRococoBulletinMessagesInstance, - RococoPeopleToRococoBulletinMessagesLane, - >, - ActualFeeRefund, + WithRococoBulletinMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundRococoBulletinMessages, >, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundRococoBulletinMessages); @@ -153,8 +115,6 @@ impl pallet_bridge_messages::Config for Runt type BridgedChain = bp_polkadot_bulletin::PolkadotBulletin; type BridgedHeaderChain = BridgeRococoBulletinGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanesToRococoBulletin; - type OutboundPayload = XcmAsPlainPayload; type InboundPayload = XcmAsPlainPayload; @@ -162,22 +122,38 @@ impl pallet_bridge_messages::Config for Runt type DeliveryConfirmationPayments = (); - type MessageDispatch = - XcmBlobMessageDispatch; - type OnMessagesDelivered = OnMessagesDeliveredFromRococoBulletin; + type MessageDispatch = XcmOverRococoBulletin; + type OnMessagesDelivered = XcmOverRococoBulletin; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverPolkadotBulletinInstance = pallet_xcm_bridge_hub::Instance2; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoBulletinGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithRococoBulletinMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToRococoBulletinXcmBlobHauler; + + type AdminOrigin = EnsureRoot; + // Only allow calls from sibling People parachain to directly open the bridge. + type OpenBridgeOrigin = EnsureXcm>; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from People parachains. + type AllowWithoutBridgeDeposit = Equals; + + type LocalXcmChannelManager = (); + type BlobDispatcher = FromRococoBulletinMessageBlobDispatcher; } #[cfg(test)] @@ -232,13 +208,13 @@ mod tests { // Bulletin chain - it has the same (almost) runtime for Polkadot Bulletin and Rococo // Bulletin, so we have to adhere Polkadot names here - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaRococoBulletinInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithRococoBulletinMessagesInstance, PriorityBoostPerMessage, @@ -252,3 +228,73 @@ mod tests { assert_eq!(BridgeRococoToRococoBulletinMessagesPalletInstance::get(), expected,); } } + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: bp_messages::LaneId, + sibling_para_id: u32, +) -> InteriorLocation { + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + use xcm_executor::traits::ConvertLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_destination = + [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get()), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location( + &sibling_parachain, + ) + .expect("valid AccountId"), + deposit: Balance::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert( + lane_id, bridge_id, + ); + + universal_source +} + +/// Contains the migration for the PeopleRococo<>RococoBulletin bridge. +pub mod migration { + use super::*; + use bp_messages::LaneId; + use frame_support::traits::ConstBool; + use sp_runtime::Either; + + parameter_types! { + pub RococoPeopleToRococoBulletinMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 0])); + pub BulletinRococoLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + } + + /// Ensure that the existing lanes for the People<>Bulletin bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverPolkadotBulletinInstance, + RococoPeopleToRococoBulletinMessagesLane, + ConstBool, + PeopleRococoLocation, + BulletinRococoLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_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..fc52413a909fe63894cc1d2f94729f85abdfaa26 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -20,30 +20,32 @@ use crate::{ bridge_common_config::{BridgeParachainWestendInstance, DeliveryRewardInBalance}, weights, xcm_config::UniversalLocation, - BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubWestend, XcmRouter, + AccountId, Balance, Balances, BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverBridgeHubWestend, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, -}; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, + target_chain::FromBridgedChainMessagesProof, }; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; -use codec::Encode; use frame_support::{parameter_types, traits::PalletInfoAccess}; +use frame_system::EnsureRoot; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm::EnsureXcm; +use parachains_common::xcm_config::{ + AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, +}; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub BridgeRococoToWestendMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); @@ -59,26 +61,6 @@ parameter_types! { // see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - pub AssetHubRococoParaId: cumulus_primitives_core::ParaId = bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID.into(); - pub AssetHubWestendParaId: cumulus_primitives_core::ParaId = bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID.into(); - - // Lanes - pub ActiveOutboundLanesToBridgeHubWestend: &'static [bp_messages::LaneId] = &[XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND]; - pub const AssetHubRococoToAssetHubWestendMessagesLane: bp_messages::LaneId = XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND; - pub FromAssetHubRococoToAssetHubWestendRoute: SenderAndLane = SenderAndLane::new( - ParentThen([Parachain(AssetHubRococoParaId::get().into())].into()).into(), - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, - ); - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromAssetHubRococoToAssetHubWestendRoute::get(), - (WestendGlobalConsensusNetwork::get(), [Parachain(AssetHubWestendParaId::get().into())].into()) - ) - ]; - - pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); - pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); - pub BridgeHubWestendLocation: Location = Location::new( 2, [ @@ -86,26 +68,8 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); -} -pub const XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND: LaneId = LaneId([0, 0, 0, 2]); - -fn build_congestion_message(is_congested: bool) -> alloc::vec::Vec> { - alloc::vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested, - } - ) - .encode() - .into(), - } - ] + + pub storage BridgeDeposit: Balance = 5 * ROC; } /// Proof of messages, coming from Westend. @@ -119,33 +83,14 @@ pub type ToWestendBridgeHubMessagesDeliveryProof = type FromWestendMessageBlobDispatcher = BridgeBlobDispatcher; -/// Export XCM messages to be relayed to the other side -pub type ToBridgeHubWestendHaulBlobExporter = XcmOverBridgeHubWestend; - -pub struct ToBridgeHubWestendXcmBlobHauler; -impl XcmBlobHauler for ToBridgeHubWestendXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithBridgeHubWestendMessagesInstance; - type ToSourceChainSender = XcmRouter; - type CongestedMessage = CongestedMessage; - type UncongestedMessage = UncongestedMessage; -} - -/// On messages delivered callback. -type OnMessagesDeliveredFromWestend = - XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Westend parachain. -pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubRococoRefundBridgeHubWestendMessages, Runtime, - RefundableMessagesLane< - WithBridgeHubWestendMessagesInstance, - AssetHubRococoToAssetHubWestendMessagesLane, - >, - ActualFeeRefund, + WithBridgeHubWestendMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundBridgeHubWestendMessages, >, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundBridgeHubWestendMessages); @@ -164,8 +109,6 @@ impl pallet_bridge_messages::Config for Ru bp_bridge_hub_westend::BridgeHubWestend, >; - type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubWestend; - type OutboundPayload = XcmAsPlainPayload; type InboundPayload = XcmAsPlainPayload; @@ -177,28 +120,86 @@ impl pallet_bridge_messages::Config for Ru DeliveryRewardInBalance, >; - type MessageDispatch = XcmBlobMessageDispatch< - FromWestendMessageBlobDispatcher, - Self::WeightInfo, - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider< - AssetHubRococoParaId, - Runtime, - >, - >; - type OnMessagesDelivered = OnMessagesDeliveredFromWestend; + type MessageDispatch = XcmOverBridgeHubWestend; + type OnMessagesDelivered = XcmOverBridgeHubWestend; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverBridgeHubWestendInstance = pallet_xcm_bridge_hub::Instance1; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = WestendGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithBridgeHubWestendMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToBridgeHubWestendXcmBlobHauler; + + type AdminOrigin = EnsureRoot; + // Only allow calls from relay chains and sibling parachains to directly open the bridge. + type OpenBridgeOrigin = EnsureXcm; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from system parachains or relay chain + type AllowWithoutBridgeDeposit = + RelayOrOtherSystemParachains; + + // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 + type LocalXcmChannelManager = (); + type BlobDispatcher = FromWestendMessageBlobDispatcher; +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: bp_messages::LaneId, + sibling_para_id: u32, +) -> InteriorLocation { + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + use xcm_executor::traits::ConvertLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_destination = [GlobalConsensus(Westend), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location( + &sibling_parachain, + ) + .expect("valid AccountId"), + deposit: Balance::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert( + lane_id, bridge_id, + ); + + universal_source } #[cfg(test)] @@ -207,14 +208,11 @@ mod tests { use crate::bridge_common_config::BridgeGrandpaWestendInstance; use bridge_runtime_common::{ assert_complete_bridge_types, - extensions::refund_relayer_extension::RefundableParachain, integrity::{ assert_complete_with_parachain_bridge_constants, check_message_lane_weights, AssertChainConstants, AssertCompleteBridgeConstants, }, }; - use parachains_common::Balance; - use testnet_parachains_constants::rococo; /// Every additional message in the message delivery transaction boosts its priority. /// So the priority of transaction with `N+1` messages is larger than priority of @@ -225,12 +223,12 @@ mod tests { /// /// We want this tip to be large enough (delivery transactions with more messages = less /// operational costs and a faster bridge), so this value should be significant. - const FEE_BOOST_PER_MESSAGE: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_MESSAGE: Balance = 2 * ROC; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * ROC; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * ROC; #[test] fn ensure_bridge_hub_rococo_message_lane_weights_are_correct() { @@ -268,19 +266,20 @@ mod tests { }, }); - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaWestendInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::< Runtime, - RefundableParachain, + WithBridgeHubWestendMessagesInstance, + bp_bridge_hub_westend::BridgeHubWestend, PriorityBoostPerParachainHeader, >(FEE_BOOST_PER_PARACHAIN_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubWestendMessagesInstance, PriorityBoostPerMessage, @@ -294,3 +293,29 @@ mod tests { assert_eq!(BridgeRococoToWestendMessagesPalletInstance::get(), expected,); } } + +/// Contains the migration for the AssetHubRococo<>AssetHubWestend bridge. +pub mod migration { + use super::*; + use bp_messages::LaneId; + use frame_support::traits::ConstBool; + use sp_runtime::Either; + + parameter_types! { + pub AssetHubRococoToAssetHubWestendMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 2])); + pub AssetHubRococoLocation: Location = Location::new(1, [Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)]); + pub AssetHubWestendUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)].into(); + } + + /// Ensure that the existing lanes for the AHR<>AHW bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverBridgeHubWestendInstance, + AssetHubRococoToAssetHubWestendMessagesLane, + // the lanes are already created for AHR<>AHW, but we need to link them to the bridge + // structs + ConstBool, + AssetHubRococoLocation, + AssetHubWestendUniversalLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c8face15627f0fece31598db3c35e4a8335527a7..6c6e2ec7efdd4bdbea8d4ded25ce4b795cf887c1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -39,26 +39,16 @@ 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 sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, Keccak256}, + traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, + ApplyExtrinsicResult, }; #[cfg(feature = "std")] @@ -79,16 +69,13 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; -use testnet_parachains_constants::rococo::{ - consensus::*, currency::*, fee::WeightToFee, snowbridge::INBOUND_QUEUE_PALLET_INDEX, time::*, -}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use bp_runtime::HeaderId; use bridge_hub_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, AggregateMessageOrigin, }; -use pallet_xcm::EnsureXcm; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm::VersionedLocation; @@ -99,7 +86,11 @@ pub use sp_runtime::BuildStorage; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use rococo_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID}; -use xcm::prelude::*; +use snowbridge_core::{ + outbound::{Command, Fee}, + AgentId, PricingParameters, +}; +use xcm::{latest::prelude::*, prelude::*}; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, @@ -114,8 +105,6 @@ use parachains_common::{ #[cfg(feature = "runtime-benchmarks")] use alloc::boxed::Box; -#[cfg(feature = "runtime-benchmarks")] -use benchmark_helpers::DoNothingRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -165,10 +154,36 @@ pub type Migrations = ( ConstU32, ConstU32, >, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, + >, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, + >, + bridge_to_westend_config::migration::StaticToDynamicLanes, + bridge_to_bulletin_config::migration::StaticToDynamicLanes, + frame_support::migrations::RemoveStorage< + BridgeWestendMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, + frame_support::migrations::RemoveStorage< + BridgePolkadotBulletinMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); +parameter_types! { + pub const BridgeWestendMessagesPalletName: &'static str = "BridgeWestendMessages"; + pub const BridgePolkadotBulletinMessagesPalletName: &'static str = "BridgePolkadotBulletinMessages"; + pub const OutboundLanesCongestedSignalsKey: &'static str = "OutboundLanesCongestedSignals"; +} + /// Migration to initialize storage versions for pallets added after genesis. /// /// Ideally this would be done automatically (see @@ -219,11 +234,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_015_000, + spec_version: 1_016_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -519,174 +534,6 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } -// Ethereum Bridge -parameter_types! { - pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); -} - -parameter_types! { - pub const CreateAssetCall: [u8;2] = [53, 0]; - pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; -} - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmark_helpers { - use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; - use codec::Encode; - use snowbridge_beacon_primitives::BeaconHeader; - use snowbridge_pallet_inbound_queue::BenchmarkHelper; - use sp_core::H256; - use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; - - impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { - EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); - } - } - - pub struct DoNothingRouter; - impl SendXcm for DoNothingRouter { - type Ticket = Xcm<()>; - - fn validate( - _dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - Ok((xcm.clone().unwrap(), Assets::new())) - } - fn deliver(xcm: Xcm<()>) -> Result { - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - Ok(hash) - } - } - - impl snowbridge_pallet_system::BenchmarkHelper for () { - fn make_xcm_origin(location: Location) -> RuntimeOrigin { - RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) - } - } -} - -impl snowbridge_pallet_inbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; - type GatewayAddress = EthereumGatewayAddress; - #[cfg(feature = "runtime-benchmarks")] - type Helper = Runtime; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - >; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; - type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; -} - -impl snowbridge_pallet_outbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Hashing = Keccak256; - type MessageQueue = MessageQueue; - type Decimals = ConstU8<12>; - type MaxMessagePayloadSize = ConstU32<2048>; - type MaxMessagesPerBlock = ConstU32<32>; - type GasMeter = snowbridge_core::outbound::ConstantGasMeter; - type Balance = Balance; - type WeightToFee = WeightToFee; - type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type Channels = EthereumSystem; -} - -#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [0, 0, 0, 0], // 0x00000000 - epoch: 0, - }, - altair: Fork { - version: [1, 0, 0, 0], // 0x01000000 - epoch: 0, - }, - bellatrix: Fork { - version: [2, 0, 0, 0], // 0x02000000 - epoch: 0, - }, - capella: Fork { - version: [3, 0, 0, 0], // 0x03000000 - epoch: 0, - }, - deneb: Fork { - version: [4, 0, 0, 0], // 0x04000000 - epoch: 0, - } - }; -} - -#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [144, 0, 0, 111], // 0x90000069 - epoch: 0, - }, - altair: Fork { - version: [144, 0, 0, 112], // 0x90000070 - epoch: 50, - }, - bellatrix: Fork { - version: [144, 0, 0, 113], // 0x90000071 - epoch: 100, - }, - capella: Fork { - version: [144, 0, 0, 114], // 0x90000072 - epoch: 56832, - }, - deneb: Fork { - version: [144, 0, 0, 115], // 0x90000073 - epoch: 132608, - }, - }; -} - -impl snowbridge_pallet_ethereum_client::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; -} - -impl snowbridge_pallet_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OutboundQueue = EthereumOutboundQueue; - type SiblingOrigin = EnsureXcm; - type AgentIdOf = snowbridge_core::AgentIdOf; - type TreasuryAccount = TreasuryAccount; - type Token = Balances; - type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type DefaultPricingParameters = Parameters; - type InboundDeliveryCost = EthereumInboundQueue; -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -780,10 +627,8 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { // Parachains CheckAndBoostBridgeParachainsTransactions< Runtime, - RefundableParachain< bridge_common_config::BridgeParachainWestendInstance, - bp_bridge_hub_westend::BridgeHubWestend, - >, + bp_bridge_hub_westend::BridgeHubWestend, bridge_to_westend_config::PriorityBoostPerParachainHeader, xcm_config::TreasuryAccount, >, @@ -1379,11 +1224,41 @@ impl_runtime_apis! { ); BenchmarkError::Stop("XcmVersion was not stored!") })?; + + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); + + // fund SA + use frame_support::traits::fungible::Mutate; + use xcm_executor::traits::ConvertLocation; + frame_support::assert_ok!( + Balances::mint_into( + &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), + bridge_to_westend_config::BridgeDeposit::get() + .saturating_add(ExistentialDeposit::get()) + .saturating_add(UNITS * 5) + ) + ); + + // open bridge + let origin = RuntimeOrigin::from(pallet_xcm::Origin::Xcm(sibling_parachain_location.clone())); + XcmOverBridgeHubWestend::open_bridge( + origin.clone(), + Box::new(VersionedInteriorLocation::from([GlobalConsensus(NetworkId::Westend), Parachain(8765)])), + ).map_err(|e| { + log::error!( + "Failed to `XcmOverBridgeHubWestend::open_bridge`({:?}, {:?})`, error: {:?}", + origin, + [GlobalConsensus(NetworkId::Westend), Parachain(8765)], + e + ); + BenchmarkError::Stop("Bridge was not opened!") + })?; + Ok( ( - bridge_to_westend_config::FromAssetHubRococoToAssetHubWestendRoute::get().location, + sibling_parachain_location, NetworkId::Westend, - [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into() + [Parachain(8765)].into() ) ) } @@ -1435,16 +1310,18 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_westend_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof { + let _ = bridge_to_westend_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, @@ -1470,16 +1347,18 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_bulletin_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof { + let _ = bridge_to_bulletin_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_delivery_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, @@ -1511,8 +1390,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)>, ) { @@ -1597,42 +1476,42 @@ mod tests { use bp_polkadot_core::SuffixedCommonSignedExtensionExt; sp_io::TestExternalities::default().execute_with(|| { - frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(10), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(10), - BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), - ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - - // for BridgeHubRococo - { - let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( - VERSION.spec_version, - VERSION.transaction_version, - bp_runtime::TransactionEra::Immortal, - System::block_hash(BlockNumber::zero()), - 10, - 10, - (((), ()), ((), ())), - ); - assert_eq!(payload.encode().split_last().unwrap().1, bhr_indirect_payload.encode()); - assert_eq!( - payload.additional_signed().unwrap().encode().split_last().unwrap().1, - bhr_indirect_payload.additional_signed().unwrap().encode() - ) - } - }); + frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + ( + bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), + bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), + ), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), + ); + + // for BridgeHubRococo + { + let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( + VERSION.spec_version, + VERSION.transaction_version, + bp_runtime::TransactionEra::Immortal, + System::block_hash(BlockNumber::zero()), + 10, + 10, + (((), ()), ((), ())), + ); + assert_eq!(payload.encode().split_last().unwrap().1, bhr_indirect_payload.encode()); + assert_eq!( + payload.additional_signed().unwrap().encode().split_last().unwrap().1, + bhr_indirect_payload.additional_signed().unwrap().encode() + ) + } + }); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-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: 37_206_000 picoseconds. - Weight::from_parts(38_545_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: 37_075_000 picoseconds. - Weight::from_parts(37_757_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 5_776 - .saturating_add(Weight::from_parts(11_586_768, 0).saturating_mul(n.into())) - .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_087_000 picoseconds. - Weight::from_parts(42_970_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_055_000 picoseconds. - Weight::from_parts(36_987_740, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 4 - .saturating_add(Weight::from_parts(2_316, 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,55 +149,77 @@ 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(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: 24_326_000 picoseconds. - Weight::from_parts(25_169_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // 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) /// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// 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(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: 24_484_000 picoseconds. - Weight::from_parts(25_130_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // 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) /// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// 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(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: 24_450_000 picoseconds. - Weight::from_parts(25_164_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // 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) /// Proof: `BridgePolkadotBulletinMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// 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) @@ -202,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: 54_317_000 picoseconds. - Weight::from_parts(59_171_547, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 7 - .saturating_add(Weight::from_parts(7_566, 0).saturating_mul(n.into())) - .saturating_add(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 9c05dae979daa0107e7754cc7107f80c3f753b7e..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-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: @@ -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: 41_396_000 picoseconds. - Weight::from_parts(43_141_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: 41_095_000 picoseconds. - Weight::from_parts(42_030_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 5_702 - .saturating_add(Weight::from_parts(11_627_951, 0).saturating_mul(n.into())) - .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_912_000 picoseconds. - Weight::from_parts(47_564_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_175_000 picoseconds. - Weight::from_parts(41_674_095, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 4 - .saturating_add(Weight::from_parts(2_305, 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,69 +149,89 @@ 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(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: 32_033_000 picoseconds. - Weight::from_parts(33_131_000, 0) - .saturating_add(Weight::from_parts(0, 3966)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + // 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) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, 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::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(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: 32_153_000 picoseconds. - Weight::from_parts(33_126_000, 0) - .saturating_add(Weight::from_parts(0, 3966)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + // 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) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, 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::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(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: 36_387_000 picoseconds. - Weight::from_parts(37_396_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + // 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) @@ -216,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_562_000 picoseconds. - Weight::from_parts(61_452_871, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(7_587, 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/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_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 057dc4313510f79aa1daa1216141538ceb357012..f2cee0e3e80751f3f9dde9cbb812623fb3ae2bf8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 19_610_000 picoseconds. - Weight::from_parts(19_980_000, 3593) + // Minimum execution time: 30_988_000 picoseconds. + Weight::from_parts(31_496_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 44_411_000 picoseconds. - Weight::from_parts(45_110_000, 6196) + // Minimum execution time: 42_805_000 picoseconds. + Weight::from_parts(44_207_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `223` // Estimated: `8799` - // Minimum execution time: 89_739_000 picoseconds. - Weight::from_parts(91_256_000, 8799) + // Minimum execution time: 103_376_000 picoseconds. + Weight::from_parts(104_770_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 60_045_000 picoseconds. - Weight::from_parts(60_710_000, 6196) + // Minimum execution time: 71_234_000 picoseconds. + Weight::from_parts(72_990_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +133,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_257_000 picoseconds. - Weight::from_parts(3_392_000, 0) + // Minimum execution time: 2_636_000 picoseconds. + Weight::from_parts(2_777_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,13 +142,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_423_000 picoseconds. - Weight::from_parts(19_823_000, 3593) + // Minimum execution time: 23_839_000 picoseconds. + Weight::from_parts(24_568_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -159,6 +157,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -167,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `122` // Estimated: `6196` - // Minimum execution time: 60_484_000 picoseconds. - Weight::from_parts(61_634_000, 6196) + // Minimum execution time: 78_345_000 picoseconds. + Weight::from_parts(80_558_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,8 +192,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 44_863_000 picoseconds. - Weight::from_parts(45_549_000, 3593) + // Minimum execution time: 46_614_000 picoseconds. + Weight::from_parts(47_354_000, 3593) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-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..e9b15024be8141eae569f227b661c3f3055bd2d1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -17,10 +17,9 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TransactionByteFee, WeightToFee, XcmpQueue, + TransactionByteFee, WeightToFee, XcmOverBridgeHubWestend, XcmOverRococoBulletin, XcmpQueue, }; -use bp_messages::LaneId; -use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; + use core::marker::PhantomData; use frame_support::{ parameter_types, @@ -39,22 +38,20 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_runtime_common::XcmExportFeeToSibling; -use sp_core::Get; use sp_runtime::traits::AccountIdConversion; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; use xcm_builder::{ - deposit_or_burn_fee, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, - AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, - FrameTransactionalProcessor, FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{ - traits::{FeeManager, FeeReason, FeeReason::Export, TransactAsset}, + traits::{FeeManager, FeeReason, FeeReason::Export}, XcmExecutor, }; @@ -207,13 +204,6 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = XcmFeeManagerFromComponentsBridgeHub< WaivedLocations, ( - XcmExportFeeToRelayerRewardAccounts< - Self::AssetTransactor, - crate::bridge_to_westend_config::WestendGlobalConsensusNetwork, - crate::bridge_to_westend_config::AssetHubWestendParaId, - bp_bridge_hub_westend::BridgeHubWestend, - crate::bridge_to_westend_config::AssetHubRococoToAssetHubWestendMessagesLane, - >, XcmExportFeeToSibling< bp_rococo::Balance, AccountId, @@ -226,8 +216,8 @@ impl xcm_executor::Config for XcmConfig { ), >; type MessageExporter = ( - crate::bridge_to_westend_config::ToBridgeHubWestendHaulBlobExporter, - crate::bridge_to_bulletin_config::ToRococoBulletinHaulBlobExporter, + XcmOverBridgeHubWestend, + XcmOverRococoBulletin, crate::bridge_to_ethereum_config::SnowbridgeExporter, ); type UniversalAliases = Nothing; @@ -294,100 +284,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -/// A `HandleFee` implementation that simply deposits the fees for `ExportMessage` XCM instructions -/// into the accounts that are used for paying the relayer rewards. -/// Burns the fees in case of a failure. -pub struct XcmExportFeeToRelayerRewardAccounts< - AssetTransactor, - DestNetwork, - DestParaId, - DestBridgedChain, - BridgeLaneId, ->(PhantomData<(AssetTransactor, DestNetwork, DestParaId, DestBridgedChain, BridgeLaneId)>); - -impl< - AssetTransactor: TransactAsset, - DestNetwork: Get, - DestParaId: Get, - DestBridgedChain: bp_runtime::Chain, - BridgeLaneId: Get, - > HandleFee - for XcmExportFeeToRelayerRewardAccounts< - AssetTransactor, - DestNetwork, - DestParaId, - DestBridgedChain, - BridgeLaneId, - > -{ - fn handle_fee(fee: Assets, maybe_context: Option<&XcmContext>, reason: FeeReason) -> Assets { - if matches!(reason, FeeReason::Export { network: bridged_network, destination } - if bridged_network == DestNetwork::get() && - destination == [Parachain(DestParaId::get().into())]) - { - let bridged_chain_id = DestBridgedChain::ID; - - // We have 2 relayer rewards accounts: - // - the SA of the source parachain on this BH: this pays the relayers for delivering - // Source para -> Target Para message delivery confirmations - // - the SA of the destination parachain on this BH: this pays the relayers for - // delivering Target para -> Source Para messages - // We split the `ExportMessage` fee between these 2 accounts. - let source_para_account = PayRewardFromAccount::< - pallet_balances::Pallet, - AccountId, - >::rewards_account(RewardsAccountParams::new( - BridgeLaneId::get(), - bridged_chain_id, - RewardsAccountOwner::ThisChain, - )); - - let dest_para_account = PayRewardFromAccount::< - pallet_balances::Pallet, - AccountId, - >::rewards_account(RewardsAccountParams::new( - BridgeLaneId::get(), - bridged_chain_id, - RewardsAccountOwner::BridgedChain, - )); - - for asset in fee.into_inner() { - match asset.fun { - Fungible(total_fee) => { - let source_fee = total_fee / 2; - deposit_or_burn_fee::( - Asset { id: asset.id.clone(), fun: Fungible(source_fee) }.into(), - maybe_context, - AccountId32 { network: None, id: source_para_account.clone().into() } - .into(), - ); - - let dest_fee = total_fee - source_fee; - deposit_or_burn_fee::( - Asset { id: asset.id, fun: Fungible(dest_fee) }.into(), - maybe_context, - AccountId32 { network: None, id: dest_para_account.clone().into() } - .into(), - ); - }, - NonFungible(_) => { - deposit_or_burn_fee::( - asset.into(), - maybe_context, - AccountId32 { network: None, id: source_para_account.clone().into() } - .into(), - ); - }, - } - } - - return Assets::new() - } - - fee - } -} - pub struct XcmFeeManagerFromComponentsBridgeHub( PhantomData<(WaivedLocations, HandleFee)>, ); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index e91837af0b21336d2a2ac5684d73318e3260a1d5..982c9fec6634496bd079b16177003ba9a5cf27cc 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}; @@ -149,11 +151,21 @@ mod bridge_hub_westend_tests { use bridge_hub_test_utils::test_cases::from_parachain; use bridge_to_westend_config::{ BridgeHubWestendLocation, WestendGlobalConsensusNetwork, - WithBridgeHubWestendMessagesInstance, XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + WithBridgeHubWestendMessagesInstance, XcmOverBridgeHubWestendInstance, }; - // Para id of sibling chain used in tests. - pub const SIBLING_PARACHAIN_ID: u32 = 1000; + // Random para id of sibling chain used in tests. + pub const SIBLING_PARACHAIN_ID: u32 = 2053; + // Random para id of sibling chain used in tests. + pub const SIBLING_SYSTEM_PARACHAIN_ID: u32 = 1008; + // Random para id of bridged chain from different global consensus used in tests. + pub const BRIDGED_LOCATION_PARACHAIN_ID: u32 = 1075; + + parameter_types! { + pub SiblingParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PARACHAIN_ID)]); + pub SiblingSystemParachainLocation: Location = Location::new(1, [Parachain(SIBLING_SYSTEM_PARACHAIN_ID)]); + pub BridgedUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(); + } // Runtime from tests PoV type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< @@ -313,14 +325,20 @@ mod bridge_hub_westend_tests { _ => None, } }), - || ExportMessage { network: Westend, destination: [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into(), xcm: Xcm(vec![]) }, + || ExportMessage { network: WestendGlobalConsensusNetwork::get(), destination: [Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(), xcm: Xcm(vec![]) }, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((TokenLocation::get(), bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get()).into()), || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubWestendLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()).1 }, ) } @@ -354,7 +372,7 @@ mod bridge_hub_westend_tests { _ => None, } }), - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || (), ) } @@ -368,7 +386,16 @@ mod bridge_hub_westend_tests { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -383,7 +410,16 @@ mod bridge_hub_westend_tests { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -446,6 +482,25 @@ mod bridge_hub_westend_tests { ), ) } + + #[test] + fn open_and_close_bridge_works() { + let origins = [SiblingParachainLocation::get(), SiblingSystemParachainLocation::get()]; + + for origin in origins { + bridge_hub_test_utils::test_cases::open_and_close_bridge_works::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + origin, + BridgedUniversalLocation::get(), + ) + } + } } mod bridge_hub_bulletin_tests { @@ -454,11 +509,17 @@ mod bridge_hub_bulletin_tests { use bridge_hub_test_utils::test_cases::from_grandpa_chain; use bridge_to_bulletin_config::{ RococoBulletinGlobalConsensusNetwork, RococoBulletinGlobalConsensusNetworkLocation, - WithRococoBulletinMessagesInstance, XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + WithRococoBulletinMessagesInstance, XcmOverPolkadotBulletinInstance, }; - // Para id of sibling chain used in tests. - pub const SIBLING_PARACHAIN_ID: u32 = rococo_runtime_constants::system_parachain::PEOPLE_ID; + // Random para id of sibling chain used in tests. + pub const SIBLING_PEOPLE_PARACHAIN_ID: u32 = + rococo_runtime_constants::system_parachain::PEOPLE_ID; + + parameter_types! { + pub SiblingPeopleParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PEOPLE_PARACHAIN_ID)]); + pub BridgedBulletinLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + } // Runtime from tests PoV type RuntimeTestsAdapter = from_grandpa_chain::WithRemoteGrandpaChainHelperAdapter< @@ -505,7 +566,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 +583,13 @@ mod bridge_hub_bulletin_tests { || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(RococoBulletinGlobalConsensusNetworkLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()).1 }, ) } @@ -543,7 +610,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 +623,7 @@ mod bridge_hub_bulletin_tests { _ => None, } }), - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || (), ) } @@ -567,9 +634,18 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || { + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -581,10 +657,38 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || { + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >(SiblingPeopleParachainLocation::get(), BridgedBulletinLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } + + #[test] + fn open_and_close_bridge_works() { + let origins = [SiblingPeopleParachainLocation::get()]; + + for origin in origins { + bridge_hub_test_utils::test_cases::open_and_close_bridge_works::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + origin, + BridgedBulletinLocation::get(), + ) + } + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index a9381501359e99d12056879506f52af2308c53c0..67d4eff0f7fe38ff4783753ab5018f9d078fad2e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -90,7 +90,7 @@ bp-messages = { workspace = true } bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +bp-runtime = { workspace = true } bp-rococo = { workspace = true } bp-westend = { workspace = true } pallet-bridge-grandpa = { workspace = true } @@ -117,6 +117,7 @@ snowbridge-runtime-common = { workspace = true } [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } +pallet-bridge-relayers = { features = ["integrity-test"], workspace = true } sp-keyring = { workspace = true, default-features = true } snowbridge-runtime-test-common = { workspace = true, default-features = true } @@ -292,6 +293,6 @@ try-runtime = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] fast-runtime = [] diff --git a/cumulus/parachains/runtimes/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..2d9e8f6642768a934be275a9b5602f13d81513f4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -18,33 +18,36 @@ use crate::{ bridge_common_config::DeliveryRewardInBalance, weights, xcm_config::UniversalLocation, - BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubRococo, XcmRouter, + AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, + target_chain::FromBridgedChainMessagesProof, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, -}; -use codec::Encode; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; + use frame_support::{ parameter_types, traits::{ConstU32, PalletInfoAccess}, }; +use frame_system::EnsureRoot; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm::EnsureXcm; +use parachains_common::xcm_config::{ + AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, +}; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::westend::currency::UNITS as WND; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; @@ -66,26 +69,6 @@ parameter_types! { // see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - pub AssetHubWestendParaId: cumulus_primitives_core::ParaId = bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID.into(); - pub AssetHubRococoParaId: cumulus_primitives_core::ParaId = bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID.into(); - - // Lanes - pub ActiveOutboundLanesToBridgeHubRococo: &'static [bp_messages::LaneId] = &[XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO]; - pub const AssetHubWestendToAssetHubRococoMessagesLane: bp_messages::LaneId = XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO; - pub FromAssetHubWestendToAssetHubRococoRoute: SenderAndLane = SenderAndLane::new( - ParentThen([Parachain(AssetHubWestendParaId::get().into())].into()).into(), - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, - ); - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromAssetHubWestendToAssetHubRococoRoute::get(), - (RococoGlobalConsensusNetwork::get(), [Parachain(AssetHubRococoParaId::get().into())].into()) - ) - ]; - - pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); - pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); - pub BridgeHubRococoLocation: Location = Location::new( 2, [ @@ -93,26 +76,8 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); -} -pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); - -fn build_congestion_message(is_congested: bool) -> alloc::vec::Vec> { - alloc::vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested, - } - ) - .encode() - .into(), - } - ] + + pub storage BridgeDeposit: Balance = 10 * WND; } /// Proof of messages, coming from Rococo. @@ -126,33 +91,14 @@ pub type ToRococoBridgeHubMessagesDeliveryProof = type FromRococoMessageBlobDispatcher = BridgeBlobDispatcher; -/// Export XCM messages to be relayed to the other side -pub type ToBridgeHubRococoHaulBlobExporter = XcmOverBridgeHubRococo; - -pub struct ToBridgeHubRococoXcmBlobHauler; -impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithBridgeHubRococoMessagesInstance; - - type ToSourceChainSender = XcmRouter; - type CongestedMessage = CongestedMessage; - type UncongestedMessage = UncongestedMessage; -} - -/// On messages delivered callback. -type OnMessagesDelivered = XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. -pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubWestendRefundBridgeHubRococoMessages, Runtime, - RefundableMessagesLane< - WithBridgeHubRococoMessagesInstance, - AssetHubWestendToAssetHubRococoMessagesLane, - >, - ActualFeeRefund, + WithBridgeHubRococoMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubWestendRefundBridgeHubRococoMessages, >, >; bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages); @@ -195,8 +141,6 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, >; - type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubRococo; - type OutboundPayload = XcmAsPlainPayload; type InboundPayload = XcmAsPlainPayload; @@ -208,27 +152,85 @@ impl pallet_bridge_messages::Config for Run DeliveryRewardInBalance, >; - type MessageDispatch = XcmBlobMessageDispatch< - FromRococoMessageBlobDispatcher, - Self::WeightInfo, - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider< - AssetHubWestendParaId, - Runtime, - >, - >; - type OnMessagesDelivered = OnMessagesDelivered; + type MessageDispatch = XcmOverBridgeHubRococo; + type OnMessagesDelivered = XcmOverBridgeHubRococo; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverBridgeHubRococoInstance = pallet_xcm_bridge_hub::Instance1; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithBridgeHubRococoMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToBridgeHubRococoXcmBlobHauler; + + type AdminOrigin = EnsureRoot; + // Only allow calls from relay chains and sibling parachains to directly open the bridge. + type OpenBridgeOrigin = EnsureXcm; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from system parachains or relay chain + type AllowWithoutBridgeDeposit = + RelayOrOtherSystemParachains; + + // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 + type LocalXcmChannelManager = (); + type BlobDispatcher = FromRococoMessageBlobDispatcher; +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: bp_messages::LaneId, + sibling_para_id: u32, +) -> InteriorLocation { + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + use xcm_executor::traits::ConvertLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Westend), Parachain(sibling_para_id)].into(); + let universal_destination = [GlobalConsensus(Rococo), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: crate::xcm_config::LocationToAccountId::convert_location( + &sibling_parachain, + ) + .expect("valid AccountId"), + deposit: Balance::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert( + lane_id, bridge_id, + ); + + universal_source } #[cfg(test)] @@ -236,14 +238,11 @@ mod tests { use super::*; use bridge_runtime_common::{ assert_complete_bridge_types, - extensions::refund_relayer_extension::RefundableParachain, integrity::{ assert_complete_with_parachain_bridge_constants, check_message_lane_weights, AssertChainConstants, AssertCompleteBridgeConstants, }, }; - use parachains_common::Balance; - use testnet_parachains_constants::westend; /// Every additional message in the message delivery transaction boosts its priority. /// So the priority of transaction with `N+1` messages is larger than priority of @@ -254,12 +253,12 @@ mod tests { /// /// We want this tip to be large enough (delivery transactions with more messages = less /// operational costs and a faster bridge), so this value should be significant. - const FEE_BOOST_PER_MESSAGE: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_MESSAGE: Balance = 2 * WND; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * WND; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * WND; #[test] fn ensure_bridge_hub_westend_message_lane_weights_are_correct() { @@ -297,19 +296,20 @@ mod tests { }, }); - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaRococoInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::< Runtime, - RefundableParachain, + WithBridgeHubRococoMessagesInstance, + bp_bridge_hub_rococo::BridgeHubRococo, PriorityBoostPerParachainHeader, >(FEE_BOOST_PER_PARACHAIN_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubRococoMessagesInstance, PriorityBoostPerMessage, @@ -323,3 +323,29 @@ mod tests { ); } } + +/// Contains the migration for the AssetHubWestend<>AssetHubRococo bridge. +pub mod migration { + use super::*; + use bp_messages::LaneId; + use frame_support::traits::ConstBool; + use sp_runtime::Either; + + parameter_types! { + pub AssetHubWestendToAssetHubRococoMessagesLane: LaneId = LaneId::from_inner(Either::Right([0, 0, 0, 2])); + pub AssetHubWestendLocation: Location = Location::new(1, [Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)]); + pub AssetHubRococoUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)].into(); + } + + /// Ensure that the existing lanes for the AHW<>AHR bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverBridgeHubRococoInstance, + AssetHubWestendToAssetHubRococoMessagesLane, + // the lanes are already created for AHR<>AHW, but we need to link them to the bridge + // structs + ConstBool, + AssetHubWestendLocation, + AssetHubRococoUniversalLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 7384bf2850fe000066a9ea135e6c14027213c8da..ddd40dbf60e010a2ab19dccf00af40430c41c521 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -37,10 +37,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use bridge_runtime_common::extensions::{ - check_obsolete_extension::{ - CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, - }, - refund_relayer_extension::RefundableParachain, + CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::ParaId; @@ -84,7 +81,6 @@ use xcm_runtime_apis::{ }; use bp_runtime::HeaderId; - #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -144,10 +140,25 @@ pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, + >, + bridge_to_rococo_config::migration::StaticToDynamicLanes, + frame_support::migrations::RemoveStorage< + BridgeRococoMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); +parameter_types! { + pub const BridgeRococoMessagesPalletName: &'static str = "BridgeRococoMessages"; + pub const OutboundLanesCongestedSignalsKey: &'static str = "OutboundLanesCongestedSignals"; +} + /// Migration to initialize storage versions for pallets added after genesis. /// /// Ideally this would be done automatically (see @@ -198,11 +209,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_015_000, + spec_version: 1_016_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -549,10 +560,8 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { // Parachains CheckAndBoostBridgeParachainsTransactions< Runtime, - RefundableParachain< - bridge_to_rococo_config::BridgeParachainRococoInstance, - bp_bridge_hub_rococo::BridgeHubRococo, - >, + bridge_to_rococo_config::BridgeParachainRococoInstance, + bp_bridge_hub_rococo::BridgeHubRococo, bridge_to_rococo_config::PriorityBoostPerParachainHeader, xcm_config::TreasuryAccount, >, @@ -1101,11 +1110,41 @@ impl_runtime_apis! { ); BenchmarkError::Stop("XcmVersion was not stored!") })?; + + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); + + // fund SA + use frame_support::traits::fungible::Mutate; + use xcm_executor::traits::ConvertLocation; + frame_support::assert_ok!( + Balances::mint_into( + &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), + bridge_to_rococo_config::BridgeDeposit::get() + .saturating_add(ExistentialDeposit::get()) + .saturating_add(UNITS * 5) + ) + ); + + // open bridge + let origin = RuntimeOrigin::from(pallet_xcm::Origin::Xcm(sibling_parachain_location.clone())); + XcmOverBridgeHubRococo::open_bridge( + origin.clone(), + alloc::boxed::Box::new(VersionedInteriorLocation::from([GlobalConsensus(NetworkId::Rococo), Parachain(8765)])), + ).map_err(|e| { + log::error!( + "Failed to `XcmOverBridgeHubRococo::open_bridge`({:?}, {:?})`, error: {:?}", + origin, + [GlobalConsensus(NetworkId::Rococo), Parachain(8765)], + e + ); + BenchmarkError::Stop("Bridge was not opened!") + })?; + Ok( ( - bridge_to_rococo_config::FromAssetHubWestendToAssetHubRococoRoute::get().location, + sibling_parachain_location, NetworkId::Rococo, - [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into() + [Parachain(8765)].into() ) ) } @@ -1154,16 +1193,18 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_rococo_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Westend), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof { + let _ = bridge_to_rococo_config::open_bridge_for_benchmarks(params.lane, 42); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, @@ -1195,8 +1236,8 @@ impl_runtime_apis! { parachain_head_size: u32, proof_params: bp_runtime::UnverifiedStorageProofParams, ) -> ( - pallet_bridge_parachains::RelayBlockNumber, - pallet_bridge_parachains::RelayBlockHash, + bp_parachains::RelayBlockNumber, + bp_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof, Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, ) { @@ -1281,40 +1322,40 @@ mod tests { use bp_polkadot_core::SuffixedCommonSignedExtensionExt; sp_io::TestExternalities::default().execute_with(|| { - frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(10), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(10), - BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), - ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - - { - let bh_indirect_payload = bp_bridge_hub_westend::SignedExtension::from_params( - VERSION.spec_version, - VERSION.transaction_version, - bp_runtime::TransactionEra::Immortal, - System::block_hash(BlockNumber::zero()), - 10, - 10, - (((), ()), ((), ())), - ); - assert_eq!(payload.encode().split_last().unwrap().1, bh_indirect_payload.encode()); - assert_eq!( - payload.additional_signed().unwrap().encode().split_last().unwrap().1, - bh_indirect_payload.additional_signed().unwrap().encode() - ) - } - }); + frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + ( + bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), + ), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), + ); + + { + let bh_indirect_payload = bp_bridge_hub_westend::SignedExtension::from_params( + VERSION.spec_version, + VERSION.transaction_version, + bp_runtime::TransactionEra::Immortal, + System::block_hash(BlockNumber::zero()), + 10, + 10, + (((), ()), ((), ())), + ); + assert_eq!(payload.encode().split_last().unwrap().1, bh_indirect_payload.encode()); + assert_eq!( + payload.additional_signed().unwrap().encode().split_last().unwrap().1, + bh_indirect_payload.additional_signed().unwrap().encode() + ) + } + }); } } 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_748_000 picoseconds. - Weight::from_parts(41_836_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_923_000 picoseconds. - Weight::from_parts(41_287_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 9_774 - .saturating_add(Weight::from_parts(11_469_207, 0).saturating_mul(n.into())) - .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: 45_946_000 picoseconds. - Weight::from_parts(47_547_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: 39_668_000 picoseconds. - Weight::from_parts(41_908_980, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 11 - .saturating_add(Weight::from_parts(2_209, 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,69 +147,89 @@ 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(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: 30_544_000 picoseconds. - Weight::from_parts(31_171_000, 0) - .saturating_add(Weight::from_parts(0, 3822)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + // 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) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, 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::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(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: 30_593_000 picoseconds. - Weight::from_parts(31_261_000, 0) - .saturating_add(Weight::from_parts(0, 3822)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(2)) + // 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) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, 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::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(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: 34_682_000 picoseconds. - Weight::from_parts(35_277_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + // 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) @@ -214,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: 56_465_000 picoseconds. - Weight::from_parts(61_575_775, 0) - .saturating_add(Weight::from_parts(0, 52645)) + // 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_197, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + .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/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_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 4310b24564758c4a3d95d27f047dd8988a43395c..5bd1d1680aa1ef596940b16f3e5df911647b15f1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: // target/production/polkadot-parachain @@ -33,10 +33,10 @@ // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --chain=bridge-hub-rococo-dev +// --chain=bridge-hub-westend-dev // --header=./cumulus/file_header.txt // --template=./cumulus/templates/xcm-bench-template.hbs -// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/ +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 19_037_000 picoseconds. - Weight::from_parts(19_602_000, 3593) + // Minimum execution time: 30_218_000 picoseconds. + Weight::from_parts(30_783_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,15 +65,13 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 43_115_000 picoseconds. - Weight::from_parts(43_897_000, 6196) + // Minimum execution time: 42_631_000 picoseconds. + Weight::from_parts(43_127_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: `System::Account` (r:3 w:3) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -90,11 +88,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `294` + // Measured: `260` // Estimated: `8799` - // Minimum execution time: 90_267_000 picoseconds. - Weight::from_parts(91_460_000, 8799) - .saturating_add(T::DbWeight::get().reads(11)) + // Minimum execution time: 100_978_000 picoseconds. + Weight::from_parts(102_819_000, 8799) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: `Benchmark::Override` (r:0 w:0) @@ -106,8 +104,6 @@ impl WeightInfo { // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. Weight::from_parts(18_446_744_073_709_551_000, 0) } - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -126,19 +122,19 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `242` + // Measured: `208` // Estimated: `6196` - // Minimum execution time: 60_477_000 picoseconds. - Weight::from_parts(61_314_000, 6196) - .saturating_add(T::DbWeight::get().reads(10)) + // Minimum execution time: 71_533_000 picoseconds. + Weight::from_parts(72_922_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_996_000 picoseconds. - Weight::from_parts(3_107_000, 0) + // Minimum execution time: 2_863_000 picoseconds. + Weight::from_parts(2_997_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -146,15 +142,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 18_907_000 picoseconds. - Weight::from_parts(19_475_000, 3593) + // Minimum execution time: 23_763_000 picoseconds. + Weight::from_parts(24_438_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: `System::Account` (r:2 w:2) - // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -165,21 +157,21 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `193` + // Measured: `159` // Estimated: `6196` - // Minimum execution time: 59_143_000 picoseconds. - Weight::from_parts(60_316_000, 6196) - .saturating_add(T::DbWeight::get().reads(10)) + // Minimum execution time: 78_182_000 picoseconds. + Weight::from_parts(79_575_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -198,11 +190,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `141` - // Estimated: `3606` - // Minimum execution time: 44_459_000 picoseconds. - Weight::from_parts(45_365_000, 3606) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `107` + // Estimated: `3593` + // Minimum execution time: 46_767_000 picoseconds. + Weight::from_parts(47_823_000, 3593) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } } diff --git a/cumulus/parachains/runtimes/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..491caa38dc5f6167c22f1f5c2e2f3b330112efe8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -17,7 +17,7 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TransactionByteFee, WeightToFee, XcmpQueue, + TransactionByteFee, WeightToFee, XcmOverBridgeHubRococo, XcmpQueue, }; use frame_support::{ parameter_types, @@ -212,10 +212,8 @@ impl xcm_executor::Config for XcmConfig { SendXcmFeeToAccount, ), >; - type MessageExporter = ( - crate::bridge_to_rococo_config::ToBridgeHubRococoHaulBlobExporter, - crate::bridge_to_ethereum_config::SnowbridgeExporter, - ); + type MessageExporter = + (XcmOverBridgeHubRococo, crate::bridge_to_ethereum_config::SnowbridgeExporter); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index 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..4391b069cf093fe6d2347b081d50fa7d898ffa2f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -21,14 +21,15 @@ use bridge_common_config::{DeliveryRewardInBalance, RequiredStakeForStakeAndSlas use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations}; use bridge_hub_westend_runtime::{ bridge_common_config, bridge_to_rococo_config, - xcm_config::{RelayNetwork, WestendLocation, XcmConfig}, + bridge_to_rococo_config::RococoGlobalConsensusNetwork, + xcm_config::{LocationToAccountId, RelayNetwork, WestendLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoLocation, BridgeParachainRococoInstance, - WithBridgeHubRococoMessagesInstance, XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + WithBridgeHubRococoMessagesInstance, XcmOverBridgeHubRococoInstance, }; use codec::{Decode, Encode}; use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; @@ -42,8 +43,18 @@ use sp_runtime::{ use testnet_parachains_constants::westend::{consensus::*, fee::WeightToFee}; use xcm::latest::prelude::*; -// Para id of sibling chain used in tests. -pub const SIBLING_PARACHAIN_ID: u32 = 1000; +// Random para id of sibling chain used in tests. +pub const SIBLING_PARACHAIN_ID: u32 = 2053; +// Random para id of sibling chain used in tests. +pub const SIBLING_SYSTEM_PARACHAIN_ID: u32 = 1008; +// Random para id of bridged chain from different global consensus used in tests. +pub const BRIDGED_LOCATION_PARACHAIN_ID: u32 = 1075; + +parameter_types! { + pub SiblingParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PARACHAIN_ID)]); + pub SiblingSystemParachainLocation: Location = Location::new(1, [Parachain(SIBLING_SYSTEM_PARACHAIN_ID)]); + pub BridgedUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(); +} // Runtime from tests PoV type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< @@ -211,14 +222,20 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { _ => None, } }), - || ExportMessage { network: Rococo, destination: [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into(), xcm: Xcm(vec![]) }, + || ExportMessage { network: RococoGlobalConsensusNetwork::get(), destination: [Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(), xcm: Xcm(vec![]) }, Some((WestendLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((WestendLocation::get(), bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get()).into()), || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubRococoLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()).1 }, ) } @@ -251,7 +268,7 @@ fn message_dispatch_routing_works() { _ => None, } }), - || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + || (), ) } @@ -264,7 +281,40 @@ fn relayed_incoming_message_works() { bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Westend, - || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, + construct_and_apply_extrinsic, + ) +} + +#[test] +fn free_relay_extrinsic_works() { + // from Rococo + from_parachain::free_relay_extrinsic_works::( + collator_session_keys(), + slot_durations(), + bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Westend, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >(SiblingParachainLocation::get(), BridgedUniversalLocation::get()) + .1 + }, construct_and_apply_extrinsic, ) } @@ -327,3 +377,22 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction() { ), ) } + +#[test] +fn open_and_close_bridge_works() { + let origins = [SiblingParachainLocation::get(), SiblingSystemParachainLocation::get()]; + + for origin in origins { + bridge_hub_test_utils::test_cases::open_and_close_bridge_works::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >( + collator_session_keys(), + bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, + origin, + BridgedUniversalLocation::get(), + ) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml index 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..8c048a0d2dbd8b033ad9b3a1cef93eb199e3d62f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -41,14 +41,17 @@ xcm-executor = { workspace = true } # Bridges bp-header-chain = { workspace = true } bp-messages = { workspace = true } +bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } bp-test-utils = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-parachains = { workspace = true } pallet-bridge-messages = { features = ["test-helpers"], workspace = true } pallet-bridge-relayers = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } bridge-runtime-common = { workspace = true } [features] @@ -57,10 +60,12 @@ std = [ "asset-test-utils/std", "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", "bp-polkadot-core/std", "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", + "bp-xcm-bridge-hub/std", "bridge-runtime-common/std", "codec/std", "cumulus-pallet-parachain-system/std", @@ -75,6 +80,7 @@ std = [ "pallet-bridge-relayers/std", "pallet-timestamp/std", "pallet-utility/std", + "pallet-xcm-bridge-hub/std", "parachains-common/std", "parachains-runtimes-test-utils/std", "sp-core/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index 0b3463f0df974dcc9ce46c1d811b363d5011bbb4..b8d6d87051c76f346bf24d98ef6e8ccf85e5a7db 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -24,6 +24,7 @@ extern crate alloc; pub use bp_test_utils::test_header; pub use parachains_runtimes_test_utils::*; use sp_runtime::Perbill; +pub use test_cases::helpers::ensure_opened_bridge; /// A helper function for comparing the actual value of a fee constant with its estimated value. The /// estimated value can be overestimated (`overestimate_in_percent`), and if the difference to the diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs index f2f0ccecba132b9199f5ad75e124932fd1f5e320..72743eaa41dbcaad20c1f22b33e644e68ac6cc7b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs @@ -26,7 +26,7 @@ use alloc::{boxed::Box, vec}; use bp_header_chain::ChainWithGrandpa; use bp_messages::{LaneId, UnrewardedRelayersState}; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_messages::{BridgedChainOf, ThisChainOf}; @@ -447,7 +447,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -503,7 +503,7 @@ where ThisChainOf, (), >( - LaneId::default(), + LaneId::new(1, 2), 1u32.into(), AccountId32::from(Alice.public()).into(), unrewarded_relayers.clone(), @@ -551,7 +551,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -603,7 +603,7 @@ where ThisChainOf, (), >( - LaneId::default(), + LaneId::new(1, 2), 1u32.into(), AccountId32::from(Alice.public()).into(), unrewarded_relayers.clone(), diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs index 0988528c3f2a0ae867d188d745a3b1e923ab0060..82edcacdcab57d4cc33ca8b9fea312f2e7596327 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs @@ -28,7 +28,7 @@ use bp_messages::{LaneId, UnrewardedRelayersState}; use bp_polkadot_core::parachains::ParaHash; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::{Chain, Parachain}; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_messages::{BridgedChainOf, ThisChainOf}; @@ -552,7 +552,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -622,7 +622,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), 1, 5, 1_000, @@ -684,7 +684,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -739,7 +739,7 @@ where BridgedChainOf, ThisChainOf, >( - LaneId::default(), + LaneId::new(1, 2), 1, 5, 1_000, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index c4b5e5583baa0d9a22307e7f1fcb52ca5e95b0e9..c343e9b3e09a382065e7aed1be275abf9c029f43 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs @@ -23,11 +23,13 @@ use bp_messages::{LaneId, MessageNonce}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_relayers::RewardsAccountParams; use bp_runtime::Chain; +use bp_xcm_bridge_hub::BridgeLocations; use codec::Decode; use core::marker::PhantomData; use frame_support::{ assert_ok, - traits::{OnFinalize, OnInitialize, PalletInfoAccess}, + dispatch::GetDispatchInfo, + traits::{fungible::Mutate, OnFinalize, OnInitialize, PalletInfoAccess}, }; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader}; @@ -40,6 +42,7 @@ use sp_core::Get; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::TrailingZeroInput, AccountId32}; use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; /// Verify that the transaction has succeeded. #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -157,8 +160,8 @@ where fn verify_outcome(&self) { assert_eq!( pallet_bridge_messages::InboundLanes::::get(self.lane) - .last_delivered_nonce(), - self.expected_nonce, + .map(|d| d.last_delivered_nonce()), + Some(self.expected_nonce), ); } } @@ -382,3 +385,139 @@ fn execute_and_verify_calls( verifier.verify_outcome(); } } + +/// Helper function to open the bridge/lane for `source` and `destination` while ensuring all +/// required balances are placed into the SA of the source. +pub fn ensure_opened_bridge(source: Location, destination: InteriorLocation) -> (BridgeLocations, LaneId) +where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + LocationToAccountId: ConvertLocation>, +TokenLocation: Get{ + // construct expected bridge configuration + let locations = + pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ) + .expect("valid bridge locations"); + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_none()); + + // required balance: ED + fee + BridgeDeposit + let bridge_deposit = + >::BridgeDeposit::get( + ); + // random high enough value for `BuyExecution` fees + let buy_execution_fee_amount = 5_000_000_000_000_u128; + let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into(); + let balance_needed = ::ExistentialDeposit::get() + + buy_execution_fee_amount.into() + + bridge_deposit.into(); + + // SA of source location needs to have some required balance + let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location"); + let _ = >::mint_into(&source_account_id, balance_needed) + .expect("mint_into passes"); + + // open bridge with `Transact` call + let open_bridge_call = RuntimeCallOf::::from(BridgeXcmOverBridgeCall::< + Runtime, + XcmOverBridgePalletInstance, + >::open_bridge { + bridge_destination_universal_location: Box::new(destination.into()), + }); + + // execute XCM as source origin would do with `Transact -> Origin::Xcm` + assert_ok!(RuntimeHelper::::execute_as_origin_xcm( + open_bridge_call, + source.clone(), + buy_execution_fee + ) + .ensure_complete()); + + let bridge = pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id(), + ) + .expect("opened bridge"); + + // check state + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_try_state() + ); + + // return locations + (*locations, bridge.lane_id) +} + +/// Helper function to close the bridge/lane for `source` and `destination`. +pub fn close_bridge(source: Location, destination: InteriorLocation) +where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + LocationToAccountId: ConvertLocation>, +TokenLocation: Get{ + // construct expected bridge configuration + let locations = + pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ) + .expect("valid bridge locations"); + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_some()); + + // required balance: ED + fee + BridgeDeposit + let bridge_deposit = + >::BridgeDeposit::get( + ); + // random high enough value for `BuyExecution` fees + let buy_execution_fee_amount = 2_500_000_000_000_u128; + let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into(); + let balance_needed = ::ExistentialDeposit::get() + + buy_execution_fee_amount.into() + + bridge_deposit.into(); + + // SA of source location needs to have some required balance + let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location"); + let _ = >::mint_into(&source_account_id, balance_needed) + .expect("mint_into passes"); + + // close bridge with `Transact` call + let close_bridge_call = RuntimeCallOf::::from(BridgeXcmOverBridgeCall::< + Runtime, + XcmOverBridgePalletInstance, + >::close_bridge { + bridge_destination_universal_location: Box::new(destination.into()), + may_prune_messages: 16, + }); + + // execute XCM as source origin would do with `Transact -> Origin::Xcm` + assert_ok!(RuntimeHelper::::execute_as_origin_xcm( + close_bridge_call, + source.clone(), + buy_execution_fee + ) + .ensure_complete()); + + // bridge is closed + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_none()); + + // check state + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_try_state() + ); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs index a36a74dbbbc3ab4ec8413f1b92ede16289600413..de117982b26fbd9696620d9c4ccdb726a17ebede 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs @@ -29,17 +29,15 @@ use crate::{test_cases::bridges_prelude::*, test_data}; use asset_test_utils::BasicParachainRuntime; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, MessagesOperatingMode, OutboundLaneData, + LaneId, LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData, }; use bp_runtime::BasicOperatingMode; -use bridge_runtime_common::messages_xcm_extension::{ - XcmAsPlainPayload, XcmBlobMessageDispatchResult, -}; +use bp_xcm_bridge_hub::{Bridge, BridgeState, XcmAsPlainPayload}; use codec::Encode; use frame_support::{ assert_ok, dispatch::GetDispatchInfo, - traits::{Get, OnFinalize, OnInitialize, OriginTrait}, + traits::{Contains, Get, OnFinalize, OnInitialize, OriginTrait}, }; use frame_system::pallet_prelude::BlockNumberFor; use parachains_common::AccountId; @@ -51,17 +49,23 @@ use sp_runtime::{traits::Zero, AccountId32}; use xcm::{latest::prelude::*, AlwaysLatest}; use xcm_builder::DispatchBlobError; use xcm_executor::{ - traits::{TransactAsset, WeightBounds}, + traits::{ConvertLocation, TransactAsset, WeightBounds}, XcmExecutor, }; /// Common bridges exports. pub(crate) mod bridges_prelude { + pub use bp_parachains::{RelayBlockHash, RelayBlockNumber}; pub use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; - pub use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; + pub use pallet_bridge_messages::{ + Call as BridgeMessagesCall, Config as BridgeMessagesConfig, LanesManagerError, + }; pub use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockNumber, + Call as BridgeParachainsCall, Config as BridgeParachainsConfig, + }; + pub use pallet_xcm_bridge_hub::{ + Call as BridgeXcmOverBridgeCall, Config as BridgeXcmOverBridgeConfig, LanesManagerOf, + XcmBlobMessageDispatchResult, }; } @@ -339,7 +343,12 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< pallet_bridge_messages::OutboundLanes::::try_get( expected_lane_id ), - Err(()) + Ok(OutboundLaneData { + state: LaneState::Opened, + oldest_unpruned_nonce: 1, + latest_received_nonce: 0, + latest_generated_nonce: 0 + }) ); // prepare `ExportMessage` @@ -390,6 +399,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< expected_lane_id ), Ok(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 0, latest_generated_nonce: 1, @@ -429,7 +439,7 @@ pub fn message_dispatch_routing_works< unwrap_cumulus_pallet_xcmp_queue_event: Box< dyn Fn(Vec) -> Option>, >, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn(), ) where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config @@ -457,8 +467,9 @@ pub fn message_dispatch_routing_works< assert_ne!(runtime_para_id, sibling_parachain_id); run_test::(collator_session_key, runtime_para_id, vec![], || { - let expected_lane_id = prepare_configuration(); + prepare_configuration(); + let dummy_lane_id = LaneId::new(1, 2); let mut alice = [0u8; 32]; alice[0] = 1; @@ -475,7 +486,7 @@ pub fn message_dispatch_routing_works< >((RuntimeNetwork::get(), Here)); let result = <>::MessageDispatch>::dispatch( - test_data::dispatch_message(expected_lane_id, 1, bridging_message), + test_data::dispatch_message(dummy_lane_id, 1, bridging_message), ); assert_eq!( format!("{:?}", result.dispatch_level_result), @@ -503,7 +514,7 @@ pub fn message_dispatch_routing_works< let result = <>::MessageDispatch>::dispatch( DispatchMessage { - key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + key: MessageKey { lane_id: dummy_lane_id, nonce: 1 }, data: DispatchMessageData { payload: Ok(bridging_message.clone()) }, }, ); @@ -535,7 +546,7 @@ pub fn message_dispatch_routing_works< let result = <>::MessageDispatch>::dispatch( DispatchMessage { - key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + key: MessageKey { lane_id: dummy_lane_id, nonce: 1 }, data: DispatchMessageData { payload: Ok(bridging_message) }, }, ); @@ -641,3 +652,126 @@ where estimated_fee.into() } + +/// Test-case makes sure that `Runtime` can open/close bridges. +pub fn open_and_close_bridge_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + source: Location, + destination: InteriorLocation, +) where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + <>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<::AccountId>, + LocationToAccountId: ConvertLocation>, + TokenLocation: Get, +{ + run_test::(collator_session_key, runtime_para_id, vec![], || { + // construct expected bridge configuration + let locations = pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ).expect("valid bridge locations"); + let expected_lane_id = + locations.calculate_lane_id(xcm::latest::VERSION).expect("valid laneId"); + let lanes_manager = LanesManagerOf::::new(); + + let expected_deposit = if >::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location() + ) { + Zero::zero() + } else { + >::BridgeDeposit::get() + }; + + // check bridge/lane DOES not exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + None + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + + // open bridge with Transact call from sibling + assert_eq!( + helpers::ensure_opened_bridge::< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation, + >(source.clone(), destination.clone()) + .0 + .bridge_id(), + locations.bridge_id() + ); + + // check bridge/lane DOES exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + Some(Bridge { + bridge_origin_relative_location: Box::new(source.clone().into()), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into() + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into() + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location(&source) + .expect("valid location") + .into(), + deposit: expected_deposit, + lane_id: expected_lane_id + }) + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(|lane| lane.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(|lane| lane.state()), + Ok(LaneState::Opened) + ); + + // close bridge with Transact call from sibling + helpers::close_bridge::< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation, + >(source.clone(), destination); + + // check bridge/lane DOES not exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + None + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + }); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs index 5c1dc492a9d2999e6473ca98ece70ffd4d7de905..2940c4e00f4258818b2e6c960a0e89c758f7afab 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs @@ -20,12 +20,12 @@ use crate::test_data::prepare_inbound_xcm; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, MessageNonce, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, LaneState, + MessageNonce, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams}; use bp_test_utils::make_default_justification; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::{BridgedChain, BridgedHeader}; use sp_runtime::traits::Header as HeaderT; @@ -225,6 +225,7 @@ where prepare_message_delivery_storage_proof::( lane_id, InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer_id_at_this_chain, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs index b99c275bd0df94856eedcc1cc008c1d7a0b1a673..aefbc0dbd0a786dfc3873445fdf53a2c1991fc3c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs @@ -20,17 +20,17 @@ use super::{from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepa use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, LaneState, UnrewardedRelayersState, Weight, }; +use bp_parachains::{RelayBlockHash, RelayBlockNumber}; use bp_runtime::{ AccountIdOf, BlockNumberOf, Chain, HeaderOf, Parachain, UnverifiedStorageProofParams, }; use bp_test_utils::prepare_parachain_heads_proof; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::BridgedHeader; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockNumber}; use sp_runtime::traits::Header as HeaderT; use xcm::latest::prelude::*; @@ -292,6 +292,7 @@ where prepare_message_delivery_storage_proof::( lane_id, InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer_id_at_this_chain.into(), diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml index 43fc9083937c351d0df2190c7593bf1e8126e800..e98508ea02e63f7497f4263b4aea33d08f7e45c8 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml @@ -243,4 +243,4 @@ std = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 21206d26dd573cc1473f3ba569433e9b7e61e7af..f22feb70382aceb55d7b59523477a21a25b48a71 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -552,6 +552,9 @@ impl pallet_collective::Config for Runtime { type SetMembersOrigin = EnsureRoot; type WeightInfo = weights::pallet_collective::WeightInfo; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } pub const MAX_FELLOWS: u32 = ALLIANCE_MAX_MEMBERS; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs index 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/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs index ae4fe9e84337b833c5188452ceea3e2769048d02..08b1d192b0be18c73b5cad3cc26ead88bdbacb64 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -48,6 +48,7 @@ use xcm_builder::{ use xcm_executor::XcmExecutor; parameter_types! { + pub const RootLocation: Location = Location::here(); pub const WndLocation: Location = Location::parent(); pub const RelayNetwork: Option = Some(NetworkId::Westend); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); @@ -139,6 +140,13 @@ impl Contains for ParentOrParentsPlurality { } } +pub struct LocalPlurality; +impl Contains for LocalPlurality { + fn contains(loc: &Location) -> bool { + matches!(loc.unpack(), (0, [Plurality { .. }])) + } +} + pub type Barrier = TrailingSetTopicAsId< DenyThenTry< DenyReserveTransferToRelayChain, @@ -173,6 +181,8 @@ pub type Barrier = TrailingSetTopicAsId< pub type WaivedLocations = ( RelayOrOtherSystemParachains, Equals, + Equals, + LocalPlurality, ); /// Cases where a remote origin is accepted as trusted Teleporter for a given asset: 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..47ba8f7e97ae38762865680399aedebc565afc35 100644 --- a/cumulus/parachains/runtimes/constants/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -171,7 +171,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -182,5 +182,6 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2, EthereumNetwork::get()); } } diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index 1fcebb3f16a96d2db051ab1d0108d44abc394f4f..dfa75b8d3cf3acf1bdf5e04adaf7c369c2ff3983 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -203,4 +203,4 @@ try-runtime = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index bf173fb618afc91601b11f2a31a93a8a928be15b..55770515d73fbda8a65568fcd32466edb957d28e 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -148,7 +148,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 7, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index 2920bc428d90b202d1035cb3cbe309dd99097013..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", @@ -209,4 +213,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 25324bf1776420bed76df472739f55cce4295ccc..0c9f9461f7f4c1d8cea9e38cb315f678efd40af1 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -36,6 +36,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -43,7 +44,9 @@ use frame_support::{ dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, + traits::{ + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, + }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; @@ -65,9 +68,9 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, + ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -150,7 +153,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -438,6 +441,138 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Broker pallet calls. + Broker, + /// Proxy for renewing coretime. + CoretimeRenewer, + /// Proxy able to purchase on-demand coretime credits. + OnDemandPurchaser, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `purchase`, `renew`, `transfer` and `purchase_credit` are pretty self explanatory. + RuntimeCall::Broker(pallet_broker::Call::purchase { .. }) | + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Broker(pallet_broker::Call::transfer { .. }) | + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + // `pool` doesn't transfer, but it defines the account to be paid for contributions + RuntimeCall::Broker(pallet_broker::Call::pool { .. }) | + // `assign` is essentially a transfer of a region NFT + RuntimeCall::Broker(pallet_broker::Call::assign { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Broker => { + matches!( + c, + RuntimeCall::Broker { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::CoretimeRenewer => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::OnDemandPurchaser => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Broker, ProxyType::CoretimeRenewer) => true, + (ProxyType::Broker, ProxyType::OnDemandPurchaser) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8; . + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16 + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -481,6 +616,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Broker: pallet_broker = 50, @@ -504,6 +640,7 @@ mod benches { [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_utility, Utility] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs index f1050b3ae636261ff21674c3bb34c05bf6d232c5..ab3d6704c937377d59c5041bb29095bc50ba38c8 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs new file mode 100644 index 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/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 73a71980530707930681ebc82a5cd563372eb24e..c8dbdadf7b15dfcd2ea86326fcff2495a2087a3a 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 26_642_000 picoseconds. - Weight::from_parts(27_583_000, 3593) + // Minimum execution time: 29_812_000 picoseconds. + Weight::from_parts(30_526_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 35_124_000 picoseconds. - Weight::from_parts(36_510_000, 6196) + // Minimum execution time: 39_430_000 picoseconds. + Weight::from_parts(39_968_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 55_950_000 picoseconds. - Weight::from_parts(57_207_000, 6196) + // Minimum execution time: 65_555_000 picoseconds. + Weight::from_parts(67_161_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_747_000 picoseconds. - Weight::from_parts(24_424_000, 3571) + // Minimum execution time: 30_491_000 picoseconds. + Weight::from_parts(31_991_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_853_000 picoseconds. - Weight::from_parts(1_998_000, 0) + // Minimum execution time: 2_568_000 picoseconds. + Weight::from_parts(2_703_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_164_000 picoseconds. - Weight::from_parts(19_643_000, 3593) + // Minimum execution time: 22_159_000 picoseconds. + Weight::from_parts(22_517_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 48_708_000 picoseconds. - Weight::from_parts(49_610_000, 3593) + // Minimum execution time: 57_126_000 picoseconds. + Weight::from_parts(58_830_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,8 +180,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 20_586_000 picoseconds. - Weight::from_parts(21_147_000, 3571) + // Minimum execution time: 26_589_000 picoseconds. + Weight::from_parts(27_285_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index 07a4332800d7f22451caa43ff6668c57ec4fabd7..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", @@ -206,4 +210,4 @@ metadata-hash = ["substrate-wasm-builder/metadata-hash"] # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index a3051e4bf2713a4082f19df12082a4fb532b85bb..614eae895a74b0b109ee0475693c689ad125a81d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -36,6 +36,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -43,7 +44,9 @@ use frame_support::{ dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, + traits::{ + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, + }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; @@ -65,9 +68,9 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, + ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -149,7 +152,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -438,6 +441,138 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Broker pallet calls. + Broker, + /// Proxy for renewing coretime. + CoretimeRenewer, + /// Proxy able to purchase on-demand coretime credits. + OnDemandPurchaser, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `purchase`, `renew`, `transfer` and `purchase_credit` are pretty self explanatory. + RuntimeCall::Broker(pallet_broker::Call::purchase { .. }) | + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Broker(pallet_broker::Call::transfer { .. }) | + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + // `pool` doesn't transfer, but it defines the account to be paid for contributions + RuntimeCall::Broker(pallet_broker::Call::pool { .. }) | + // `assign` is essentially a transfer of a region NFT + RuntimeCall::Broker(pallet_broker::Call::assign { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Broker => { + matches!( + c, + RuntimeCall::Broker { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::CoretimeRenewer => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::OnDemandPurchaser => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Broker, ProxyType::CoretimeRenewer) => true, + (ProxyType::Broker, ProxyType::OnDemandPurchaser) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8; . + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16 + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -475,6 +610,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Broker: pallet_broker = 50, @@ -495,6 +631,7 @@ mod benches { [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_utility, Utility] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs index f1050b3ae636261ff21674c3bb34c05bf6d232c5..ab3d6704c937377d59c5041bb29095bc50ba38c8 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs new file mode 100644 index 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/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index ddfc599fa579dd7ae4b83d0a94b0e7092fbdc9c1..935636651eb9dcb5ce0580978d9f1340313ef289 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 26_842_000 picoseconds. - Weight::from_parts(27_606_000, 3593) + // Minimum execution time: 29_866_000 picoseconds. + Weight::from_parts(30_363_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 35_076_000 picoseconds. - Weight::from_parts(36_109_000, 6196) + // Minimum execution time: 39_434_000 picoseconds. + Weight::from_parts(40_274_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 56_951_000 picoseconds. - Weight::from_parts(58_286_000, 6196) + // Minimum execution time: 66_303_000 picoseconds. + Weight::from_parts(68_294_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_796_000 picoseconds. - Weight::from_parts(24_692_000, 3571) + // Minimum execution time: 30_523_000 picoseconds. + Weight::from_parts(31_289_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_142_000, 0) + // Minimum execution time: 2_517_000 picoseconds. + Weight::from_parts(2_634_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_572_000 picoseconds. - Weight::from_parts(20_017_000, 3593) + // Minimum execution time: 22_151_000 picoseconds. + Weight::from_parts(22_907_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 49_336_000 picoseconds. - Weight::from_parts(50_507_000, 3593) + // Minimum execution time: 57_763_000 picoseconds. + Weight::from_parts(58_941_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,8 +180,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 21_230_000 picoseconds. - Weight::from_parts(21_870_000, 3571) + // Minimum execution time: 26_322_000 picoseconds. + Weight::from_parts(27_197_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml index d20b62a557b950e080ae7eca38526d6af9846861..09b4ef679d244696504761cb4d30216fef9c7807 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/Cargo.toml @@ -136,4 +136,4 @@ try-runtime = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["sp-api/disable-logging"] +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 942e11e0b2574e0d7016f452f6808e8ddf633479..abf13a596a7dadb24b4e782012a5f0659026096d 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -106,7 +106,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index a732bec2352d2dfd6fe7e842cb6a5d3206291d7d..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", @@ -191,3 +195,8 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] + +# A feature that should be enabled when the runtime should be built for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller, like logging for example. +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 77bfb99669c6c2f58d5e6d3398b2d805b4a2ac17..9b251a90d6784312b736aedbd1f8e5b88e3ecf75 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -25,6 +25,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -33,7 +34,8 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, @@ -57,11 +59,11 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; +pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -138,7 +140,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -401,6 +403,119 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Identity pallet calls. + Identity, + /// Proxy for identity registrars. + IdentityJudgement, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `request_judgement` puts up a deposit to transfer to a registrar + RuntimeCall::Identity(pallet_identity::Call::request_judgement { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Identity => { + matches!( + c, + RuntimeCall::Identity { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) | + RuntimeCall::Utility(..) | + RuntimeCall::Multisig { .. } + ), + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Identity, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8. + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16. + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -446,6 +561,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Identity: pallet_identity = 50, @@ -464,6 +580,7 @@ mod benches { [pallet_identity, Identity] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs index 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/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs index 11c1bad9aa17866d882f4410d76065a18ab4ef28..58007173ae1d505ad5f831e8f4962d2c4e491615 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -60,10 +60,8 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn withdraw_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::withdraw_asset()) } - // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &Assets) -> Weight { - // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 - Weight::from_parts(1_000_000_000_u64, 0) + fn reserve_asset_deposited(assets: &Assets) -> Weight { + assets.weigh_assets(XcmFungibleWeight::::reserve_asset_deposited()) } fn receive_teleported_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::receive_teleported_asset()) @@ -114,12 +112,8 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } - fn deposit_asset(assets: &AssetFilter, _dest: &Location) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(1_000_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::deposit_asset()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::deposit_asset()) } fn deposit_reserve_asset(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::deposit_reserve_asset()) @@ -132,7 +126,7 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { _reserve: &Location, _xcm: &Xcm<()>, ) -> Weight { - assets.weigh_assets(XcmGeneric::::initiate_reserve_withdraw()) + assets.weigh_assets(XcmFungibleWeight::::initiate_reserve_withdraw()) } fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 2364798596d50f396a3a732c1e9dc1ad6c48a5bf..4dd44e66dd5e3cf0e545fac692cf27257cb075f4 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::fungible +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,110 +48,140 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::fungible`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn withdraw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_309_000 picoseconds. - Weight::from_parts(23_777_000, 3593) + // Minimum execution time: 30_428_000 picoseconds. + Weight::from_parts(31_184_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn transfer_asset() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 48_808_000 picoseconds. - Weight::from_parts(49_427_000, 6196) + // Minimum execution time: 41_912_000 picoseconds. + Weight::from_parts(43_346_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 71_204_000 picoseconds. - Weight::from_parts(72_121_000, 6196) + // Minimum execution time: 67_706_000 picoseconds. + Weight::from_parts(69_671_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: `Benchmark::Override` (r:0 w:0) + // Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub fn reserve_asset_deposited() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + } + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 29_790_000 picoseconds. + Weight::from_parts(30_655_000, 3535) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_559_000 picoseconds. - Weight::from_parts(3_616_000, 0) + // Minimum execution time: 2_438_000 picoseconds. + Weight::from_parts(2_597_000, 0) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn deposit_asset() -> Weight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 25_042_000 picoseconds. - Weight::from_parts(25_630_000, 3593) + // Minimum execution time: 24_040_000 picoseconds. + Weight::from_parts(24_538_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 49_030_000 picoseconds. - Weight::from_parts(49_828_000, 3593) + // Minimum execution time: 58_275_000 picoseconds. + Weight::from_parts(59_899_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 27_142_000 picoseconds. - Weight::from_parts(27_416_000, 3535) + // Minimum execution time: 25_638_000 picoseconds. + Weight::from_parts(26_514_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index a50c8860c48f84197843a9fd4d44ddb39d742095..729a3211704198bf5c59e1e2ac47e03f88fb0340 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::generic +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,24 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_holding() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_210_000 picoseconds. - Weight::from_parts(30_864_000, 3535) + // Minimum execution time: 29_430_000 picoseconds. + Weight::from_parts(30_111_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -72,97 +73,97 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_808_000 picoseconds. - Weight::from_parts(2_848_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(672_000, 0) } - // Storage: PolkadotXcm Queries (r:1 w:0) - // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::Queries` (r:1 w:0) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn query_response() -> Weight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 10_353_000 picoseconds. - Weight::from_parts(10_569_000, 3497) + // Minimum execution time: 7_445_000 picoseconds. + Weight::from_parts(7_623_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_074_000 picoseconds. - Weight::from_parts(12_280_000, 0) + // Minimum execution time: 6_749_000 picoseconds. + Weight::from_parts(7_073_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_080_000 picoseconds. - Weight::from_parts(3_161_000, 0) + // Minimum execution time: 1_275_000 picoseconds. + Weight::from_parts(1_409_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_649_000 picoseconds. - Weight::from_parts(2_732_000, 0) + // Minimum execution time: 670_000 picoseconds. + Weight::from_parts(709_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_652_000 picoseconds. - Weight::from_parts(2_749_000, 0) + // Minimum execution time: 635_000 picoseconds. + Weight::from_parts(723_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_642_000 picoseconds. - Weight::from_parts(2_704_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(699_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_438_000 picoseconds. - Weight::from_parts(3_508_000, 0) + // Minimum execution time: 678_000 picoseconds. + Weight::from_parts(728_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_626_000 picoseconds. - Weight::from_parts(2_701_000, 0) + // Minimum execution time: 657_000 picoseconds. + Weight::from_parts(703_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_error() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_737_000 picoseconds. - Weight::from_parts(25_106_000, 3535) + // Minimum execution time: 25_795_000 picoseconds. + Weight::from_parts(26_415_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: PolkadotXcm AssetTraps (r:1 w:1) - // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + // Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn claim_asset() -> Weight { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 14_712_000 picoseconds. - Weight::from_parts(14_976_000, 3555) + // Minimum execution time: 10_792_000 picoseconds. + Weight::from_parts(11_061_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -170,114 +171,93 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_689_000 picoseconds. - Weight::from_parts(2_739_000, 0) + // Minimum execution time: 624_000 picoseconds. + Weight::from_parts(682_000, 0) } - // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 26_478_000 picoseconds. - Weight::from_parts(26_695_000, 3503) + // Minimum execution time: 23_906_000 picoseconds. + Weight::from_parts(24_740_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_811_000 picoseconds. - Weight::from_parts(5_062_000, 0) + // Minimum execution time: 2_621_000 picoseconds. + Weight::from_parts(2_788_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - pub fn initiate_reserve_withdraw() -> Weight { - // Proof Size summary in bytes: - // Measured: `70` - // Estimated: `3535` - // Minimum execution time: 26_945_000 picoseconds. - Weight::from_parts(28_093_000, 3535) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) - } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_144_000 picoseconds. - Weight::from_parts(4_217_000, 0) + // Minimum execution time: 954_000 picoseconds. + Weight::from_parts(1_046_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_726_000 picoseconds. - Weight::from_parts(2_802_000, 0) + // Minimum execution time: 742_000 picoseconds. + Weight::from_parts(790_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_719_000 picoseconds. - Weight::from_parts(2_790_000, 0) + // Minimum execution time: 664_000 picoseconds. + Weight::from_parts(722_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_660_000 picoseconds. - Weight::from_parts(2_742_000, 0) + // Minimum execution time: 619_000 picoseconds. + Weight::from_parts(672_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_874_000 picoseconds. - Weight::from_parts(2_940_000, 0) + // Minimum execution time: 798_000 picoseconds. + Weight::from_parts(851_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn query_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 27_235_000 picoseconds. - Weight::from_parts(27_811_000, 3535) + // Minimum execution time: 29_580_000 picoseconds. + Weight::from_parts(31_100_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -285,27 +265,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_807_000 picoseconds. - Weight::from_parts(4_918_000, 0) + // Minimum execution time: 3_150_000 picoseconds. + Weight::from_parts(3_326_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_698_000 picoseconds. - Weight::from_parts(25_077_000, 3535) + // Minimum execution time: 26_152_000 picoseconds. + Weight::from_parts(26_635_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -313,35 +293,35 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_613_000 picoseconds. - Weight::from_parts(2_703_000, 0) + // Minimum execution time: 693_000 picoseconds. + Weight::from_parts(724_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_602_000 picoseconds. - Weight::from_parts(2_661_000, 0) + // Minimum execution time: 632_000 picoseconds. + Weight::from_parts(678_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_557_000 picoseconds. - Weight::from_parts(2_655_000, 0) + // Minimum execution time: 646_000 picoseconds. + Weight::from_parts(694_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_724_000 picoseconds. - Weight::from_parts(2_760_000, 0) + // Minimum execution time: 622_000 picoseconds. + Weight::from_parts(656_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_764_000 picoseconds. - Weight::from_parts(2_872_000, 0) + // Minimum execution time: 639_000 picoseconds. + Weight::from_parts(679_000, 0) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index 20c7e691ebc88622dc57b4a026533fae67cf3044..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", @@ -191,3 +195,8 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] + +# A feature that should be enabled when the runtime should be built for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller, like logging for example. +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 3343d2be749d6570915afc60fe40b8a1bd5eaf77..07bfba92c933c89713473b6cdafa2597763de3ed 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -25,6 +25,7 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -33,7 +34,8 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, @@ -57,11 +59,11 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; +pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -138,7 +140,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -401,6 +403,119 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Identity pallet calls. + Identity, + /// Proxy for identity registrars. + IdentityJudgement, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `request_judgement` puts up a deposit to transfer to a registrar + RuntimeCall::Identity(pallet_identity::Call::request_judgement { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Identity => { + matches!( + c, + RuntimeCall::Identity { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) | + RuntimeCall::Utility(..) | + RuntimeCall::Multisig { .. } + ), + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Identity, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8. + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16. + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -446,6 +561,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Identity: pallet_identity = 50, @@ -464,6 +580,7 @@ mod benches { [pallet_identity, Identity] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs index 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/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs index b1fc7ad8ed832ec6aa9a65c5e4ad3334ff3225d1..b44e8d4b61b8fe57ea4191b891da6dcbaa962813 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -60,10 +60,8 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn withdraw_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::withdraw_asset()) } - // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &Assets) -> Weight { - // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 - Weight::from_parts(1_000_000_000_u64, 0) + fn reserve_asset_deposited(assets: &Assets) -> Weight { + assets.weigh_assets(XcmFungibleWeight::::reserve_asset_deposited()) } fn receive_teleported_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::receive_teleported_asset()) @@ -114,12 +112,8 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } - fn deposit_asset(assets: &AssetFilter, _dest: &Location) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(1_000_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::deposit_asset()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::deposit_asset()) } fn deposit_reserve_asset(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::deposit_reserve_asset()) @@ -132,13 +126,10 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { _reserve: &Location, _xcm: &Xcm<()>, ) -> Weight { - assets.weigh_assets(XcmGeneric::::initiate_reserve_withdraw()) + assets.weigh_assets(XcmFungibleWeight::::initiate_reserve_withdraw()) } fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(200_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 92d08a24618075d21614ae53ed17e3618fc2f8ea..8f6bfde986bb36c4fd3c90bdfb767727133f8c35 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::fungible +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,110 +48,140 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::fungible`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn withdraw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_363_000 picoseconds. - Weight::from_parts(23_663_000, 3593) + // Minimum execution time: 30_040_000 picoseconds. + Weight::from_parts(30_758_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn transfer_asset() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 49_093_000 picoseconds. - Weight::from_parts(49_719_000, 6196) + // Minimum execution time: 42_135_000 picoseconds. + Weight::from_parts(42_970_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: System Account (r:2 w:2) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 74_134_000 picoseconds. - Weight::from_parts(74_719_000, 6196) + // Minimum execution time: 67_385_000 picoseconds. + Weight::from_parts(69_776_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: `Benchmark::Override` (r:0 w:0) + // Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub fn reserve_asset_deposited() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + } + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 29_804_000 picoseconds. + Weight::from_parts(30_662_000, 3535) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_726_000 picoseconds. - Weight::from_parts(3_881_000, 0) + // Minimum execution time: 2_358_000 picoseconds. + Weight::from_parts(2_497_000, 0) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn deposit_asset() -> Weight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 25_903_000 picoseconds. - Weight::from_parts(26_150_000, 3593) + // Minimum execution time: 23_732_000 picoseconds. + Weight::from_parts(24_098_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 51_084_000 picoseconds. - Weight::from_parts(51_859_000, 3593) + // Minimum execution time: 58_449_000 picoseconds. + Weight::from_parts(60_235_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 28_038_000 picoseconds. - Weight::from_parts(28_438_000, 3535) + // Minimum execution time: 25_708_000 picoseconds. + Weight::from_parts(26_495_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 861f038199596ef9813275e58914f252d8842a87..1377d31f2db7df67b58a699253af03498f7f6741 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::generic +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,24 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_holding() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_819_000 picoseconds. - Weight::from_parts(31_157_000, 3535) + // Minimum execution time: 29_537_000 picoseconds. + Weight::from_parts(30_513_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -72,97 +73,97 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_869_000 picoseconds. - Weight::from_parts(2_920_000, 0) + // Minimum execution time: 683_000 picoseconds. + Weight::from_parts(738_000, 0) } - // Storage: PolkadotXcm Queries (r:1 w:0) - // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::Queries` (r:1 w:0) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn query_response() -> Weight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 10_268_000 picoseconds. - Weight::from_parts(10_496_000, 3497) + // Minimum execution time: 7_498_000 picoseconds. + Weight::from_parts(7_904_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_990_000 picoseconds. - Weight::from_parts(12_206_000, 0) + // Minimum execution time: 7_029_000 picoseconds. + Weight::from_parts(7_325_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_170_000 picoseconds. - Weight::from_parts(3_308_000, 0) + // Minimum execution time: 1_343_000 picoseconds. + Weight::from_parts(1_410_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_650_000 picoseconds. - Weight::from_parts(2_783_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(734_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_681_000 picoseconds. - Weight::from_parts(2_829_000, 0) + // Minimum execution time: 690_000 picoseconds. + Weight::from_parts(740_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_622_000 picoseconds. - Weight::from_parts(2_688_000, 0) + // Minimum execution time: 667_000 picoseconds. + Weight::from_parts(697_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_385_000 picoseconds. - Weight::from_parts(3_538_000, 0) + // Minimum execution time: 692_000 picoseconds. + Weight::from_parts(743_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_630_000 picoseconds. - Weight::from_parts(2_720_000, 0) + // Minimum execution time: 670_000 picoseconds. + Weight::from_parts(712_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_error() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_446_000 picoseconds. - Weight::from_parts(24_854_000, 3535) + // Minimum execution time: 26_405_000 picoseconds. + Weight::from_parts(26_877_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: PolkadotXcm AssetTraps (r:1 w:1) - // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + // Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn claim_asset() -> Weight { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 14_713_000 picoseconds. - Weight::from_parts(15_010_000, 3555) + // Minimum execution time: 10_953_000 picoseconds. + Weight::from_parts(11_345_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -170,114 +171,93 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_702_000 picoseconds. - Weight::from_parts(2_744_000, 0) + // Minimum execution time: 644_000 picoseconds. + Weight::from_parts(693_000, 0) } - // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_955_000 picoseconds. - Weight::from_parts(26_632_000, 3503) + // Minimum execution time: 24_157_000 picoseconds. + Weight::from_parts(24_980_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_965_000 picoseconds. - Weight::from_parts(5_168_000, 0) + // Minimum execution time: 2_767_000 picoseconds. + Weight::from_parts(2_844_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - pub fn initiate_reserve_withdraw() -> Weight { - // Proof Size summary in bytes: - // Measured: `70` - // Estimated: `3535` - // Minimum execution time: 27_707_000 picoseconds. - Weight::from_parts(28_081_000, 3535) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) - } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_215_000 picoseconds. - Weight::from_parts(4_362_000, 0) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_141_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_843_000 picoseconds. - Weight::from_parts(2_957_000, 0) + // Minimum execution time: 776_000 picoseconds. + Weight::from_parts(829_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_751_000 picoseconds. - Weight::from_parts(2_809_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(740_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_674_000 picoseconds. - Weight::from_parts(2_737_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(684_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_891_000 picoseconds. - Weight::from_parts(2_952_000, 0) + // Minimum execution time: 825_000 picoseconds. + Weight::from_parts(853_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn query_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 28_600_000 picoseconds. - Weight::from_parts(29_001_000, 3535) + // Minimum execution time: 30_222_000 picoseconds. + Weight::from_parts(31_110_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -285,27 +265,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_748_000 picoseconds. - Weight::from_parts(4_813_000, 0) + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_325_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 25_483_000 picoseconds. - Weight::from_parts(25_737_000, 3535) + // Minimum execution time: 26_548_000 picoseconds. + Weight::from_parts(26_911_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -313,35 +293,35 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_755_000 picoseconds. - Weight::from_parts(2_817_000, 0) + // Minimum execution time: 684_000 picoseconds. + Weight::from_parts(726_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_700_000 picoseconds. - Weight::from_parts(2_773_000, 0) + // Minimum execution time: 649_000 picoseconds. + Weight::from_parts(700_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_670_000 picoseconds. - Weight::from_parts(2_711_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(686_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_710_000 picoseconds. - Weight::from_parts(2_762_000, 0) + // Minimum execution time: 652_000 picoseconds. + Weight::from_parts(703_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_839_000 picoseconds. - Weight::from_parts(2_931_000, 0) + // Minimum execution time: 673_000 picoseconds. + Weight::from_parts(742_000, 0) } } diff --git a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs index 1fe72604d3731eed1dee3d27477e0e829cccfba0..f126ee861fa7193a26c127016f3cce9b19ad0781 100644 --- a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs @@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 0, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs index 1dfbe2b6c41c840846e1d64a4d87e7c9c1b0c7cf..fac2d1312c0fef13bfd6442e5ec23add613a9ca2 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/lib.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/lib.rs @@ -89,7 +89,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 0, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 940aa1b734dfc856b5a72cf83cc14b5f8980bc5e..fe75b2b6e72f2254867c94253c38f2ebef718a92 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -22,7 +22,7 @@ use cumulus_primitives_core::{ use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use frame_support::{ - dispatch::{DispatchResult, RawOrigin}, + dispatch::{DispatchResult, GetDispatchInfo, RawOrigin}, inherent::{InherentData, ProvideInherent}, pallet_prelude::Get, traits::{OnFinalize, OnInitialize, OriginTrait, UnfilteredDispatchable}, @@ -450,6 +450,7 @@ impl< require_weight_at_most, call: call.into(), }, + ExpectTransactStatus(MaybeErrorCode::Success), ]); // execute xcm as parent origin @@ -462,6 +463,38 @@ impl< Weight::zero(), ) } + + pub fn execute_as_origin_xcm( + call: Call, + origin: Location, + buy_execution_fee: Asset, + ) -> Outcome { + // prepare `Transact` xcm + let xcm = Xcm(vec![ + WithdrawAsset(buy_execution_fee.clone().into()), + BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: call.get_dispatch_info().weight, + call: call.encode().into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + + // execute xcm as parent origin + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + <::XcmExecutor>::prepare_and_execute( + origin.clone(), + xcm, + &mut hash, + Self::xcm_max_weight(if origin == Location::parent() { + XcmReceivedFrom::Parent + } else { + XcmReceivedFrom::Sibling + }), + Weight::zero(), + ) + } } pub enum XcmReceivedFrom { diff --git a/cumulus/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/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index bf39c02a3f59460156c37e6c652bc69fa7237a2d..266894c3e4edabb139a288963ea04fd50a7139e5 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -35,6 +35,10 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use assets_common::{ + local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, + AssetIdForTrustBackedAssetsConvert, +}; use codec::Encode; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; @@ -42,10 +46,13 @@ use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, + ord_parameter_types, pallet_prelude::Weight, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, Everything, TransformOrigin, + tokens::{fungible, fungibles, imbalance::ResolveAssetTo}, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Everything, + TransformOrigin, }, weights::{ constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFee as _, @@ -55,7 +62,7 @@ use frame_support::{ }; use frame_system::{ limits::{BlockLength, BlockWeights}, - EnsureRoot, EnsureSigned, + EnsureRoot, EnsureSigned, EnsureSignedBy, }; use parachains_common::{ impls::{AssetsToBlockAuthor, NonZeroIssuance}, @@ -67,7 +74,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, + traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; @@ -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. @@ -442,7 +449,9 @@ parameter_types! { // pub type AssetsForceOrigin = // EnsureOneOf, EnsureXcm>>; -impl pallet_assets::Config for Runtime { +pub type TrustBackedAssetsInstance = pallet_assets::Instance1; + +impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type AssetId = AssetId; @@ -500,6 +509,106 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; } +parameter_types! { + pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); + pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); +} + +ord_parameter_types! { + pub const AssetConversionOrigin: sp_runtime::AccountId32 = + AccountIdConversion::::into_account_truncating(&AssetConversionPalletId::get()); +} + +pub type AssetsForceOrigin = EnsureRoot; + +pub type PoolAssetsInstance = pallet_assets::Instance3; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type RemoveItemsLimit = ConstU32<1000>; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = + AsEnsureOriginWithArg>; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = ConstU128<0>; + type AssetAccountDeposit = ConstU128<0>; + type MetadataDepositBase = ConstU128<0>; + type MetadataDepositPerByte = ConstU128<0>; + type ApprovalDeposit = ConstU128<0>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +/// Union fungibles implementation for `Assets` and `ForeignAssets`. +pub type LocalAndForeignAssets = fungibles::UnionOf< + Assets, + ForeignAssets, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert< + xcm_config::TrustBackedAssetsPalletLocation, + xcm::latest::Location, + >, + parachains_common::AssetIdForTrustBackedAssets, + xcm::latest::Location, + >, + xcm::latest::Location, + AccountId, +>; + +/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. +pub type NativeAndAssets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + xcm::latest::Location, + AccountId, +>; + +pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< + AssetConversionPalletId, + (xcm::latest::Location, xcm::latest::Location), +>; + +impl pallet_asset_conversion::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type HigherPrecisionBalance = sp_core::U256; + type AssetKind = xcm::latest::Location; + type Assets = NativeAndAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = pallet_asset_conversion::WithFirstAsset< + xcm_config::RelayLocation, + AccountId, + Self::AssetKind, + PoolIdToAccountId, + >; + type PoolAssetId = u32; + type PoolAssets = PoolAssets; + type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam + type PoolSetupFeeAsset = xcm_config::RelayLocation; + type PoolSetupFeeTarget = ResolveAssetTo; + type LiquidityWithdrawalFee = LiquidityWithdrawalFee; + type LPFee = ConstU32<3>; + type PalletId = AssetConversionPalletId; + type MaxSwapPathLength = ConstU32<3>; + type MintMinLiquidity = ConstU128<100>; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< + xcm_config::RelayLocation, + parachain_info::Pallet, + xcm_config::TrustBackedAssetsPalletIndex, + xcm::latest::Location, + >; +} + parameter_types! { pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); @@ -642,9 +751,9 @@ impl pallet_asset_tx_payment::Config for Runtime { Balances, Runtime, ConvertInto, - pallet_assets::Instance1, + TrustBackedAssetsInstance, >, - AssetsToBlockAuthor, + AssetsToBlockAuthor, >; } @@ -685,6 +794,8 @@ construct_runtime!( // The main stage. Assets: pallet_assets:: = 50, ForeignAssets: pallet_assets:: = 51, + PoolAssets: pallet_assets:: = 52, + AssetConversion: pallet_asset_conversion = 53, Sudo: pallet_sudo = 255, } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index eca7c7bbc3c2daa4291ba84a11e67b335377e4df..99aadb33b840130f36dbc3ffca80a6a710403675 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -24,15 +24,19 @@ //! soon. use super::{ AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Authorship, Balance, - Balances, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, ParachainInfo, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, - XcmpQueue, + Balances, CollatorSelection, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + WeightToFee, XcmpQueue, }; use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee}; +use assets_common::TrustBackedAssetsAsLocation; use core::marker::PhantomData; use frame_support::{ parameter_types, - traits::{ConstU32, Contains, ContainsPair, Everything, EverythingBut, Get, Nothing}, + traits::{ + tokens::imbalance::ResolveAssetTo, ConstU32, Contains, ContainsPair, Everything, + EverythingBut, Get, Nothing, PalletInfoAccess, + }, weights::Weight, }; use frame_system::EnsureRoot; @@ -49,15 +53,15 @@ use xcm_builder::{ FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignSignedViaLocation, StartsWith, + TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); - // Local native currency which is stored in `pallet_balances`` + // Local native currency which is stored in `pallet_balances` pub const PenpalNativeCurrency: Location = Location::here(); // The Penpal runtime is utilized for testing with various environment setups. // This storage item allows us to customize the `NetworkId` where Penpal is deployed. @@ -70,6 +74,10 @@ parameter_types! { Parachain(ParachainInfo::parachain_id().into()) ].into(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); + pub StakingPot: AccountId = CollatorSelection::account_id(); + pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; + pub TrustBackedAssetsPalletLocation: Location = + PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used @@ -265,6 +273,8 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const ASSETS_PALLET_ID: u8 = 50; pub const ASSET_HUB_ID: u32 = 1000; +pub const USDT_ASSET_ID: u128 = 1984; + parameter_types! { /// The location that this chain recognizes as the Relay network's Asset Hub. pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]); @@ -282,6 +292,10 @@ parameter_types! { 1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())] ); + pub UsdtFromAssetHub: Location = Location::new( + 1, + [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ASSET_ID)], + ); /// The Penpal runtime is utilized for testing with various environment setups. /// This storage item provides the opportunity to customize testing scenarios @@ -312,6 +326,28 @@ pub type TrustedReserves = ( pub type TrustedTeleporters = (AssetFromChain,); +/// `AssetId`/`Balance` converter for `TrustBackedAssets`. +pub type TrustBackedAssetsConvertedConcreteId = + assets_common::TrustBackedAssetsConvertedConcreteId; + +/// Asset converter for pool assets. +/// Used to convert assets in pools to the asset required for fee payment. +/// The pool must be between the first asset and the one required for fee payment. +/// This type allows paying fees with any asset in a pool with the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::latest::Location, + >, + ForeignAssetsConvertedConcreteId, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -327,18 +363,21 @@ impl xcm_executor::Config for XcmConfig { type Weigher = FixedWeightBounds; type Trader = ( UsingComponents>, - // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated - // `pallet_assets` instance - `ForeignAssets`. - cumulus_primitives_utility::TakeFirstAssetTrader< + cumulus_primitives_utility::SwapFirstAssetTrader< + RelayLocation, + crate::AssetConversion, + WeightToFee, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::latest::Location, + >, + ForeignAssetsConvertedConcreteId, + ), + ResolveAssetTo, AccountId, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, - ForeignAssetsConvertedConcreteId, - ForeignAssets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - ForeignFungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, - >, >, ); type ResponseHandler = PolkadotXcm; @@ -348,7 +387,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< (), SendXcmFeeToAccount, diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index a0ad248bb7048f9a19cec45f97ad4242c9455f83..9c905c8762778447809acb3f7592585223aca865 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -134,3 +134,8 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] + +# A feature that should be enabled when the runtime should be built for on-chain +# deployment. This will disable stuff that shouldn't be part of the on-chain wasm +# to make it smaller, like logging for example. +on-chain-release-build = [] diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index dff7046f1972614d30210b6ac6d21c24e0350aea..34646f84aedbb7ed0448c1e3c5ae0f2d5ca28b5f 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -113,7 +113,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, - state_version: 0, + system_version: 0, }; pub const MILLISECS_PER_BLOCK: u64 = 6000; diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 5d6548447463a3f960890689890537c2381975dc..383e0f158bf49b30e743dc0fe530f5e84560fdf0 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -2,9 +2,9 @@ name = "polkadot-parachain-bin" version = "4.0.0" authors.workspace = true -build = "build.rs" edition.workspace = true -description = "Runs a polkadot parachain node which could be a collator." +build = "build.rs" +description = "Runs a polkadot parachain node" license = "Apache-2.0" [lints] @@ -15,19 +15,14 @@ name = "polkadot-parachain" path = "src/main.rs" [dependencies] -async-trait = { workspace = true } -clap = { features = ["derive"], workspace = true } -codec = { workspace = true, default-features = true } color-eyre = { workspace = true } -color-print = { workspace = true } -futures = { workspace = true } hex-literal = { workspace = true, default-features = true } log = { workspace = true, default-features = true } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } -docify = { workspace = true } # Local +polkadot-parachain-lib = { features = ["rococo-native", "westend-native"], workspace = true } rococo-parachain-runtime = { workspace = true } shell-runtime = { workspace = true } glutton-westend-runtime = { workspace = true } @@ -41,7 +36,6 @@ coretime-rococo-runtime = { workspace = true } coretime-westend-runtime = { workspace = true } bridge-hub-westend-runtime = { workspace = true, default-features = true } penpal-runtime = { workspace = true } -jsonrpsee = { features = ["server"], workspace = true } people-rococo-runtime = { workspace = true } people-westend-runtime = { workspace = true } parachains-common = { workspace = true, default-features = true } @@ -51,83 +45,32 @@ testnet-parachains-constants = { features = [ ], workspace = true } # Substrate -frame-benchmarking = { workspace = true, default-features = true } -frame-benchmarking-cli = { workspace = true, default-features = true } sp-runtime = { workspace = true } -sp-io = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } -sp-session = { workspace = true, default-features = true } -frame-try-runtime = { optional = true, workspace = true, default-features = true } -sc-consensus = { workspace = true, default-features = true } -sp-tracing = { workspace = true, default-features = true } -frame-support = { workspace = true, default-features = true } sc-cli = { workspace = true, default-features = true } -sc-client-api = { workspace = true, default-features = true } -sc-executor = { workspace = true, default-features = true } sc-service = { workspace = true, default-features = true } -sc-telemetry = { workspace = true, default-features = true } -sc-transaction-pool = { workspace = true, default-features = true } -sp-transaction-pool = { workspace = true, default-features = true } -sc-network = { workspace = true, default-features = true } -sc-network-sync = { workspace = true, default-features = true } -sc-basic-authorship = { workspace = true, default-features = true } -sp-timestamp = { workspace = true, default-features = true } -sp-blockchain = { workspace = true, default-features = true } -sp-genesis-builder = { workspace = true } -sp-block-builder = { workspace = true, default-features = true } -sp-keystore = { workspace = true, default-features = true } sc-chain-spec = { workspace = true, default-features = true } -sc-rpc = { workspace = true, default-features = true } -sp-version = { workspace = true, default-features = true } -sc-tracing = { workspace = true, default-features = true } -sp-offchain = { workspace = true, default-features = true } -frame-system-rpc-runtime-api = { workspace = true, default-features = true } -pallet-transaction-payment = { workspace = true, default-features = true } -pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } -sp-std = { workspace = true, default-features = true } -sp-inherents = { workspace = true, default-features = true } -sp-api = { workspace = true, default-features = true } -sp-consensus-aura = { workspace = true, default-features = true } -sc-sysinfo = { workspace = true, default-features = true } -prometheus-endpoint = { workspace = true, default-features = true } -sc-transaction-pool-api = { workspace = true, default-features = true } -substrate-frame-rpc-system = { workspace = true, default-features = true } -pallet-transaction-payment-rpc = { workspace = true, default-features = true } -substrate-state-trie-migration-rpc = { workspace = true, default-features = true } # Polkadot -# Use rococo-native as this is currently the default "local" relay chain -polkadot-cli = { features = ["rococo-native", "westend-native"], workspace = true, default-features = true } -polkadot-primitives = { workspace = true, default-features = true } polkadot-service = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } # Cumulus -cumulus-client-cli = { workspace = true, default-features = true } -cumulus-client-collator = { workspace = true, default-features = true } -cumulus-client-consensus-aura = { workspace = true, default-features = true } -cumulus-client-consensus-relay-chain = { workspace = true, default-features = true } -cumulus-client-consensus-common = { workspace = true, default-features = true } -cumulus-client-consensus-proposer = { workspace = true, default-features = true } -cumulus-client-parachain-inherent = { workspace = true, default-features = true } -cumulus-client-service = { workspace = true, default-features = true } -cumulus-primitives-aura = { workspace = true, default-features = true } cumulus-primitives-core = { workspace = true, default-features = true } -cumulus-relay-chain-interface = { workspace = true, default-features = true } [build-dependencies] substrate-build-script-utils = { workspace = true, default-features = true } -[dev-dependencies] -assert_cmd = { workspace = true } -nix = { features = ["signal"], workspace = true } -tempfile = { workspace = true } -tokio = { version = "1.32.0", features = ["macros", "parking_lot", "time"] } -wait-timeout = { workspace = true } - [features] default = [] runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-lib/runtime-benchmarks", + "polkadot-service/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "asset-hub-rococo-runtime/runtime-benchmarks", "asset-hub-westend-runtime/runtime-benchmarks", "bridge-hub-rococo-runtime/runtime-benchmarks", @@ -136,23 +79,17 @@ runtime-benchmarks = [ "contracts-rococo-runtime/runtime-benchmarks", "coretime-rococo-runtime/runtime-benchmarks", "coretime-westend-runtime/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "frame-benchmarking-cli/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", "glutton-westend-runtime/runtime-benchmarks", - "parachains-common/runtime-benchmarks", "penpal-runtime/runtime-benchmarks", "people-rococo-runtime/runtime-benchmarks", "people-westend-runtime/runtime-benchmarks", - "polkadot-cli/runtime-benchmarks", - "polkadot-primitives/runtime-benchmarks", - "polkadot-service/runtime-benchmarks", "rococo-parachain-runtime/runtime-benchmarks", - "sc-service/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", ] try-runtime = [ + "polkadot-parachain-lib/try-runtime", + "polkadot-service/try-runtime", + "sp-runtime/try-runtime", + "asset-hub-rococo-runtime/try-runtime", "asset-hub-westend-runtime/try-runtime", "bridge-hub-rococo-runtime/try-runtime", @@ -161,17 +98,11 @@ try-runtime = [ "contracts-rococo-runtime/try-runtime", "coretime-rococo-runtime/try-runtime", "coretime-westend-runtime/try-runtime", - "frame-support/try-runtime", - "frame-try-runtime/try-runtime", "glutton-westend-runtime/try-runtime", - "pallet-transaction-payment/try-runtime", "penpal-runtime/try-runtime", "people-rococo-runtime/try-runtime", "people-westend-runtime/try-runtime", - "polkadot-cli/try-runtime", - "polkadot-service/try-runtime", "shell-runtime/try-runtime", - "sp-runtime/try-runtime", ] fast-runtime = [ "bridge-hub-rococo-runtime/fast-runtime", diff --git a/cumulus/polkadot-parachain/chain-specs/contracts-rococo.json b/cumulus/polkadot-parachain/chain-specs/contracts-rococo.json deleted file mode 120000 index b9f8e8f31e84587c8bb148b96eefff3d7f87d682..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/chain-specs/contracts-rococo.json +++ /dev/null @@ -1 +0,0 @@ -../../parachains/chain-specs/contracts-rococo.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json b/cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json new file mode 120000 index 0000000000000000000000000000000000000000..f6f2bc6869177d6a0b57dab1df23784b88733b01 --- /dev/null +++ b/cumulus/polkadot-parachain/chain-specs/coretime-polkadot.json @@ -0,0 +1 @@ +../../parachains/chain-specs/coretime-polkadot.json \ No newline at end of file diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..066cbfae53ae7021664cb60305d6eb5b38d94f6a --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml @@ -0,0 +1,119 @@ +[package] +name = "polkadot-parachain-lib" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +description = "Helper library that can be used to build a parachain node" +license = "Apache-2.0" + +[lints] +workspace = true + +[lib] +path = "src/lib.rs" + +[dependencies] +async-trait = { workspace = true } +clap = { features = ["derive"], workspace = true } +codec = { workspace = true, default-features = true } +color-print = { workspace = true } +futures = { workspace = true } +log = { workspace = true, default-features = true } +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } +docify = { workspace = true } + +# Local +jsonrpsee = { features = ["server"], workspace = true } +parachains-common = { workspace = true, default-features = true } + +# Substrate +frame-benchmarking = { optional = true, workspace = true, default-features = true } +frame-benchmarking-cli = { workspace = true, default-features = true } +sp-runtime = { workspace = true } +sp-core = { workspace = true, default-features = true } +sp-session = { workspace = true, default-features = true } +frame-try-runtime = { optional = true, workspace = true, default-features = true } +sc-consensus = { workspace = true, default-features = true } +frame-support = { optional = true, workspace = true, default-features = true } +sc-cli = { workspace = true, default-features = true } +sc-client-api = { workspace = true, default-features = true } +sc-client-db = { workspace = true, default-features = true } +sc-executor = { workspace = true, default-features = true } +sc-service = { workspace = true, default-features = true } +sc-telemetry = { workspace = true, default-features = true } +sc-transaction-pool = { workspace = true, default-features = true } +sp-transaction-pool = { workspace = true, default-features = true } +sc-network = { workspace = true, default-features = true } +sc-basic-authorship = { workspace = true, default-features = true } +sp-timestamp = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true } +sp-block-builder = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } +sc-chain-spec = { workspace = true, default-features = true } +sc-rpc = { workspace = true, default-features = true } +sp-version = { workspace = true, default-features = true } +sp-weights = { workspace = true, default-features = true } +sc-tracing = { workspace = true, default-features = true } +frame-system-rpc-runtime-api = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } +sp-inherents = { workspace = true, default-features = true } +sp-api = { workspace = true, default-features = true } +sp-consensus-aura = { workspace = true, default-features = true } +sc-sysinfo = { workspace = true, default-features = true } +prometheus-endpoint = { workspace = true, default-features = true } +substrate-frame-rpc-system = { workspace = true, default-features = true } +pallet-transaction-payment-rpc = { workspace = true, default-features = true } +substrate-state-trie-migration-rpc = { workspace = true, default-features = true } + +# Polkadot +polkadot-cli = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } + +# Cumulus +cumulus-client-cli = { workspace = true, default-features = true } +cumulus-client-collator = { workspace = true, default-features = true } +cumulus-client-consensus-aura = { workspace = true, default-features = true } +cumulus-client-consensus-relay-chain = { workspace = true, default-features = true } +cumulus-client-consensus-common = { workspace = true, default-features = true } +cumulus-client-consensus-proposer = { workspace = true, default-features = true } +cumulus-client-parachain-inherent = { workspace = true, default-features = true } +cumulus-client-service = { workspace = true, default-features = true } +cumulus-primitives-aura = { workspace = true, default-features = true } +cumulus-primitives-core = { workspace = true, default-features = true } +cumulus-relay-chain-interface = { workspace = true, default-features = true } + +[dev-dependencies] +assert_cmd = { workspace = true } +nix = { features = ["signal"], workspace = true } +tokio = { version = "1.32.0", features = ["macros", "parking_lot", "time"] } +wait-timeout = { workspace = true } + +[features] +default = [] +rococo-native = [ + "polkadot-cli/rococo-native", +] +westend-native = [ + "polkadot-cli/westend-native", +] +runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "sc-client-db/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-transaction-payment/try-runtime", + "polkadot-cli/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs new file mode 100644 index 0000000000000000000000000000000000000000..349dc01d8a4f25e16fe2a4f19ed772096cd981dd --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs @@ -0,0 +1,395 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::{ + chain_spec::DiskChainSpecLoader, + common::{ + chain_spec::{Extensions, LoadSpec}, + NodeExtraArgs, + }, +}; +use clap::{Command, CommandFactory, FromArgMatches}; +use sc_chain_spec::ChainSpec; +use sc_cli::{ + CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, + RpcEndpoint, SharedParams, SubstrateCli, +}; +use sc_service::{config::PrometheusConfig, BasePath}; +use std::{fmt::Debug, marker::PhantomData, path::PathBuf}; + +/// Trait that can be used to customize some of the customer-facing info related to the node binary +/// that is being built using this library. +/// +/// The related info is shown to the customer as part of logs or help messages. +/// It does not impact functionality. +pub trait CliConfig { + /// The version of the resulting node binary. + fn impl_version() -> String; + + /// The description of the resulting node binary. + fn description(executable_name: String) -> String { + format!( + "The command-line arguments provided first will be passed to the parachain node, \n\ + and the arguments provided after -- will be passed to the relay chain node. \n\ + \n\ + Example: \n\ + \n\ + {} [parachain-args] -- [relay-chain-args]", + executable_name + ) + } + + /// The author of the resulting node binary. + fn author() -> String; + + /// The support URL for the resulting node binary. + fn support_url() -> String; + + /// The starting copyright year of the resulting node binary. + fn copyright_start_year() -> u16; +} + +/// Sub-commands supported by the collator. +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + /// Key management CLI utilities + #[command(subcommand)] + Key(sc_cli::KeySubcommand), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Export the genesis state of the parachain. + #[command(alias = "export-genesis-state")] + ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), + + /// Export the genesis wasm of the parachain. + ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand), + + /// Sub-commands concerned with benchmarking. + /// The pallet benchmarking moved to the `pallet` sub-command. + #[command(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} + +#[derive(clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[arg(skip)] + pub(crate) chain_spec_loader: Option>, + + #[command(subcommand)] + pub subcommand: Option, + + #[command(flatten)] + pub run: cumulus_client_cli::RunCmd, + + /// EXPERIMENTAL: Use slot-based collator which can handle elastic scaling. + /// + /// Use with care, this flag is unstable and subject to change. + #[arg(long)] + pub experimental_use_slot_based: bool, + + /// Disable automatic hardware benchmarks. + /// + /// By default these benchmarks are automatically ran at startup and measure + /// the CPU speed, the memory bandwidth and the disk speed. + /// + /// The results are then printed out in the logs, and also sent as part of + /// telemetry, if telemetry is enabled. + #[arg(long)] + pub no_hardware_benchmarks: bool, + + /// Export all `PoVs` build by this collator to the given folder. + /// + /// This is useful for debugging issues that are occurring while validating these `PoVs` on the + /// relay chain. + #[arg(long)] + pub export_pov_to_path: Option, + + /// Relay chain arguments + #[arg(raw = true)] + pub relay_chain_args: Vec, + + #[arg(skip)] + pub(crate) _phantom: PhantomData, +} + +impl Cli { + pub(crate) fn node_extra_args(&self) -> NodeExtraArgs { + NodeExtraArgs { + use_slot_based_consensus: self.experimental_use_slot_based, + export_pov: self.export_pov_to_path.clone(), + } + } +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + Self::executable_name() + } + + fn impl_version() -> String { + Config::impl_version() + } + + fn description() -> String { + Config::description(Self::executable_name()) + } + + fn author() -> String { + Config::author() + } + + fn support_url() -> String { + Config::support_url() + } + + fn copyright_start_year() -> i32 { + Config::copyright_start_year() as i32 + } + + fn load_spec(&self, id: &str) -> Result, String> { + match &self.chain_spec_loader { + Some(chain_spec_loader) => chain_spec_loader.load_spec(id), + None => DiskChainSpecLoader.load_spec(id), + } + } +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain cli object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: Option, + + _phantom: PhantomData, +} + +impl RelayChainCli { + fn polkadot_cmd() -> Command { + let help_template = color_print::cformat!( + "The arguments that are passed to the relay chain node. \n\ + \n\ + RELAY_CHAIN_ARGS: \n\ + {{options}}", + ); + + polkadot_cli::RunCmd::command() + .no_binary_name(true) + .help_template(help_template) + } + + /// Parse the relay chain CLI parameters using the parachain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let polkadot_cmd = Self::polkadot_cmd(); + let matches = polkadot_cmd.get_matches_from(relay_chain_args); + let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit()); + + let extension = Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + + let base_path = para_config.base_path.path().join("polkadot"); + Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() } + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + Cli::::impl_name() + } + + fn impl_version() -> String { + Cli::::impl_version() + } + + fn description() -> String { + Cli::::description() + } + + fn author() -> String { + Cli::::author() + } + + fn support_url() -> String { + Cli::::support_url() + } + + fn copyright_start_year() -> i32 { + Cli::::copyright_start_year() + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id) + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_listen_port() -> u16 { + 9945 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> sc_cli::Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result>> { + self.base.base.rpc_addr(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> sc_cli::Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + ) -> sc_cli::Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> sc_cli::Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) + } + + fn role(&self, is_dev: bool) -> sc_cli::Result { + self.base.base.role(is_dev) + } + + fn transaction_pool( + &self, + is_dev: bool, + ) -> sc_cli::Result { + self.base.base.transaction_pool(is_dev) + } + + fn trie_cache_maximum_size(&self) -> sc_cli::Result> { + self.base.base.trie_cache_maximum_size() + } + + fn rpc_methods(&self) -> sc_cli::Result { + self.base.base.rpc_methods() + } + + fn rpc_max_connections(&self) -> sc_cli::Result { + self.base.base.rpc_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> sc_cli::Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> sc_cli::Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> sc_cli::Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> sc_cli::Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> sc_cli::Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> sc_cli::Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> sc_cli::Result> { + self.base.base.telemetry_endpoints(chain_spec) + } + + fn node_name(&self) -> sc_cli::Result { + self.base.base.node_name() + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs new file mode 100644 index 0000000000000000000000000000000000000000..43fb551f80d29e62749d371b59e34d96f14bbd1f --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs @@ -0,0 +1,318 @@ +// 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::{ + cli::{Cli, RelayChainCli, Subcommand}, + common::{ + chain_spec::{Extensions, LoadSpec}, + runtime::{ + AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT, + RuntimeResolver, + }, + spec::DynNodeSpec, + types::Block, + NodeBlock, NodeExtraArgs, + }, + fake_runtime_api, + runtime::BlockNumber, + service::ShellNode, +}; +#[cfg(feature = "runtime-benchmarks")] +use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; +use cumulus_primitives_core::ParaId; +use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; +use log::info; +use sc_cli::{Result, SubstrateCli}; +use sp_runtime::traits::AccountIdConversion; +#[cfg(feature = "runtime-benchmarks")] +use sp_runtime::traits::HashingFor; +use std::panic::{RefUnwindSafe, UnwindSafe}; + +/// Structure that can be used in order to provide customizers for different functionalities of the +/// node binary that is being built using this library. +pub struct RunConfig { + /// A custom chain spec loader. + pub chain_spec_loader: Box, + /// A custom runtime resolver. + pub runtime_resolver: Box, +} + +pub fn new_aura_node_spec( + aura_id: AuraConsensusId, + extra_args: &NodeExtraArgs, +) -> Box +where + Block: NodeBlock + UnwindSafe + RefUnwindSafe, + Block::BoundedHeader: UnwindSafe + RefUnwindSafe, +{ + match aura_id { + AuraConsensusId::Sr25519 => crate::service::new_aura_node_spec::< + Block, + fake_runtime_api::aura_sr25519::RuntimeApi, + sp_consensus_aura::sr25519::AuthorityId, + >(extra_args), + AuraConsensusId::Ed25519 => crate::service::new_aura_node_spec::< + Block, + fake_runtime_api::aura_ed25519::RuntimeApi, + sp_consensus_aura::ed25519::AuthorityId, + >(extra_args), + } +} + +fn new_node_spec( + config: &sc_service::Configuration, + runtime_resolver: &Box, + extra_args: &NodeExtraArgs, +) -> std::result::Result, sc_cli::Error> { + let runtime = runtime_resolver.runtime(config.chain_spec.as_ref())?; + + Ok(match runtime { + Runtime::Shell => Box::new(ShellNode), + Runtime::Omni(block_number, consensus) => match (block_number, consensus) { + (BlockNumber::U32, Consensus::Aura(aura_id)) => + new_aura_node_spec::>(aura_id, extra_args), + (BlockNumber::U64, Consensus::Aura(aura_id)) => + new_aura_node_spec::>(aura_id, extra_args), + }, + }) +} + +/// Parse command line arguments into service configuration. +pub fn run(cmd_config: RunConfig) -> Result<()> { + let mut cli = Cli::::from_args(); + cli.chain_spec_loader = Some(cmd_config.chain_spec_loader); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_check_block_cmd(config, cmd) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_export_blocks_cmd(config, cmd) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_export_state_cmd(config, cmd) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_import_blocks_cmd(config, cmd) + }) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.prepare_revert_cmd(config, cmd) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + let polkadot_cli = + RelayChainCli::::new(runner.config(), cli.relay_chain_args.iter()); + + runner.sync_run(|config| { + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + cmd.run(config, polkadot_config) + }) + }, + Some(Subcommand::ExportGenesisHead(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| { + let node = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + node.run_export_genesis_head_cmd(config, cmd) + }) + }, + Some(Subcommand::ExportGenesisWasm(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|_config| { + let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; + cmd.run(&*spec) + }) + }, + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + + // Switch on the concrete benchmark sub-command- + match cmd { + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Pallet(cmd) => runner.sync_run(|config| { + cmd.run_with_spec::>, ReclaimHostFunctions>(Some( + config.chain_spec, + )) + }), + BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { + let node = new_node_spec( + &config, + &cmd_config.runtime_resolver, + &cli.node_extra_args(), + )?; + node.run_benchmark_block_cmd(config, cmd) + }), + #[cfg(feature = "runtime-benchmarks")] + BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { + let node = new_node_spec( + &config, + &cmd_config.runtime_resolver, + &cli.node_extra_args(), + )?; + node.run_benchmark_storage_cmd(config, cmd) + }), + BenchmarkCmd::Machine(cmd) => + runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())), + #[allow(unreachable_patterns)] + _ => Err("Benchmarking sub-command unsupported or compilation feature missing. \ + Make sure to compile with --features=runtime-benchmarks \ + to enable all supported benchmarks." + .into()), + } + }, + Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let polkadot_cli = + RelayChainCli::::new(runner.config(), cli.relay_chain_args.iter()); + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + // If Statemint (Statemine, Westmint, Rockmine) DB exists and we're using the + // asset-hub chain spec, then rename the base path to the new chain ID. In the case + // that both file paths exist, the node will exit, as the user must decide (by + // deleting one path) the information that they want to use as their DB. + let old_name = match config.chain_spec.id() { + "asset-hub-polkadot" => Some("statemint"), + "asset-hub-kusama" => Some("statemine"), + "asset-hub-westend" => Some("westmint"), + "asset-hub-rococo" => Some("rockmine"), + _ => None, + }; + + if let Some(old_name) = old_name { + let new_path = config.base_path.config_dir(config.chain_spec.id()); + let old_path = config.base_path.config_dir(old_name); + + if old_path.exists() && new_path.exists() { + return Err(format!( + "Found legacy {} path {} and new Asset Hub path {}. \ + Delete one path such that only one exists.", + old_name, + old_path.display(), + new_path.display() + ) + .into()); + } + + if old_path.exists() { + std::fs::rename(old_path.clone(), new_path.clone())?; + info!( + "{} was renamed to Asset Hub. The filepath with associated data on disk \ + has been renamed from {} to {}.", + old_name, + old_path.display(), + new_path.display() + ); + } + } + + let hwbench = (!cli.no_hardware_benchmarks) + .then_some(config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench( + Some(database_path), + &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, + ); + + let tokio_handle = config.tokio_handle.clone(); + let polkadot_config = + SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + info!("🪪 Parachain id: {:?}", id); + info!("🧾 Parachain Account: {}", parachain_account); + info!("✍️ Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + start_node( + config, + &cmd_config.runtime_resolver, + polkadot_config, + collator_options, + id, + cli.node_extra_args(), + hwbench, + ) + .await + }) + }, + } +} + +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node( + config: sc_service::Configuration, + runtime_resolver: &Box, + polkadot_config: sc_service::Configuration, + collator_options: cumulus_client_cli::CollatorOptions, + id: ParaId, + extra_args: NodeExtraArgs, + hwbench: Option, +) -> Result { + let node_spec = new_node_spec(&config, runtime_resolver, &extra_args)?; + node_spec + .start_node(config, polkadot_config, collator_options, id, hwbench, extra_args) + .await + .map_err(Into::into) +} diff --git a/cumulus/polkadot-parachain/src/common/aura.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs similarity index 95% rename from cumulus/polkadot-parachain/src/common/aura.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs index 9f72d847926f3a7be480911bdb273e33bf6c0afb..9e8837de7f878c0a1566ac5f8aac84ef68b63f71 100644 --- a/cumulus/polkadot-parachain/src/common/aura.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs @@ -18,9 +18,11 @@ use codec::Codec; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::BlockT; use sp_consensus_aura::AuraApi; -use sp_runtime::app_crypto::{AppCrypto, AppPair, AppSignature, Pair}; +use sp_runtime::{ + app_crypto::{AppCrypto, AppPair, AppSignature, Pair}, + traits::Block as BlockT, +}; /// Convenience trait for defining the basic bounds of an `AuraId`. pub trait AuraIdT: AppCrypto + Codec + Send { diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs new file mode 100644 index 0000000000000000000000000000000000000000..974d6ef2b611275735c657674193ef0eae76c9ae --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs @@ -0,0 +1,77 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Chain spec primitives. + +pub use sc_chain_spec::ChainSpec; +use sc_chain_spec::ChainSpecExtension; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; + +/// Helper trait used for loading/building a chain spec starting from the chain ID. +pub trait LoadSpec { + /// Load/Build a chain spec starting from the chain ID. + fn load_spec(&self, id: &str) -> Result, String>; +} + +/// Default implementation for `LoadSpec` that just reads a chain spec from the disk. +pub struct DiskChainSpecLoader; + +impl LoadSpec for DiskChainSpecLoader { + fn load_spec(&self, path: &str) -> Result, String> { + Ok(Box::new(GenericChainSpec::from_json_file(path.into())?)) + } +} + +/// Generic extensions for Parachain ChainSpecs. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecExtension)] +pub struct Extensions { + /// The relay chain of the Parachain. + #[serde(alias = "relayChain", alias = "RelayChain")] + pub relay_chain: String, + /// The id of the Parachain. + #[serde(alias = "paraId", alias = "ParaId")] + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +/// Generic chain spec for all polkadot-parachain runtimes +pub type GenericChainSpec = sc_service::GenericChainSpec; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_decode_extension_camel_and_snake_case() { + let camel_case = r#"{"relayChain":"relay","paraId":1}"#; + let snake_case = r#"{"relay_chain":"relay","para_id":1}"#; + let pascal_case = r#"{"RelayChain":"relay","ParaId":1}"#; + + let camel_case_extension: Extensions = serde_json::from_str(camel_case).unwrap(); + let snake_case_extension: Extensions = serde_json::from_str(snake_case).unwrap(); + let pascal_case_extension: Extensions = serde_json::from_str(pascal_case).unwrap(); + + assert_eq!(camel_case_extension, snake_case_extension); + assert_eq!(snake_case_extension, pascal_case_extension); + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs new file mode 100644 index 0000000000000000000000000000000000000000..e2826826d40ed556973ab057865f706a09a6f4ae --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs @@ -0,0 +1,161 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::common::spec::NodeSpec; +use cumulus_client_cli::ExportGenesisHeadCommand; +use frame_benchmarking_cli::BlockCmd; +#[cfg(any(feature = "runtime-benchmarks"))] +use frame_benchmarking_cli::StorageCmd; +use sc_cli::{CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; +use sc_service::{Configuration, TaskManager}; +use std::{future::Future, pin::Pin}; + +type SyncCmdResult = sc_cli::Result<()>; + +type AsyncCmdResult<'a> = + sc_cli::Result<(Pin + 'a>>, TaskManager)>; + +pub trait NodeCommandRunner { + fn prepare_check_block_cmd( + self: Box, + config: Configuration, + cmd: &CheckBlockCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_export_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ExportBlocksCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_export_state_cmd( + self: Box, + config: Configuration, + cmd: &ExportStateCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_import_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ImportBlocksCmd, + ) -> AsyncCmdResult<'_>; + + fn prepare_revert_cmd( + self: Box, + config: Configuration, + cmd: &RevertCmd, + ) -> AsyncCmdResult<'_>; + + fn run_export_genesis_head_cmd( + self: Box, + config: Configuration, + cmd: &ExportGenesisHeadCommand, + ) -> SyncCmdResult; + + fn run_benchmark_block_cmd( + self: Box, + config: Configuration, + cmd: &BlockCmd, + ) -> SyncCmdResult; + + #[cfg(any(feature = "runtime-benchmarks"))] + fn run_benchmark_storage_cmd( + self: Box, + config: Configuration, + cmd: &StorageCmd, + ) -> SyncCmdResult; +} + +impl NodeCommandRunner for T +where + T: NodeSpec, +{ + fn prepare_check_block_cmd( + self: Box, + config: Configuration, + cmd: &CheckBlockCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) + } + + fn prepare_export_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ExportBlocksCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, config.database)), partial.task_manager)) + } + + fn prepare_export_state_cmd( + self: Box, + config: Configuration, + cmd: &ExportStateCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, config.chain_spec)), partial.task_manager)) + } + + fn prepare_import_blocks_cmd( + self: Box, + config: Configuration, + cmd: &ImportBlocksCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) + } + + fn prepare_revert_cmd( + self: Box, + config: Configuration, + cmd: &RevertCmd, + ) -> AsyncCmdResult<'_> { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + Ok((Box::pin(cmd.run(partial.client, partial.backend, None)), partial.task_manager)) + } + + fn run_export_genesis_head_cmd( + self: Box, + config: Configuration, + cmd: &ExportGenesisHeadCommand, + ) -> SyncCmdResult { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + cmd.run(partial.client) + } + + fn run_benchmark_block_cmd( + self: Box, + config: Configuration, + cmd: &BlockCmd, + ) -> SyncCmdResult { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + cmd.run(partial.client) + } + + #[cfg(any(feature = "runtime-benchmarks"))] + fn run_benchmark_storage_cmd( + self: Box, + config: Configuration, + cmd: &StorageCmd, + ) -> SyncCmdResult { + let partial = T::new_partial(&config).map_err(sc_cli::Error::Service)?; + let db = partial.backend.expose_db(); + let storage = partial.backend.expose_storage(); + + cmd.run(config, partial.client, db, storage) + } +} diff --git a/cumulus/polkadot-parachain/src/common/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs similarity index 71% rename from cumulus/polkadot-parachain/src/common/mod.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs index d7718931b8726af15f9348c22596df9406f99c75..907f09263fc1cc4acd1e33a6a4c564ea7060b8b2 100644 --- a/cumulus/polkadot-parachain/src/common/mod.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs @@ -18,15 +18,45 @@ #![warn(missing_docs)] -pub mod aura; +pub(crate) mod aura; +pub mod chain_spec; +pub mod command; +pub mod rpc; +pub mod runtime; +pub mod spec; +pub mod types; use cumulus_primitives_core::CollectCollationInfo; +use sc_client_db::DbHash; use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata}; use sp_block_builder::BlockBuilder; -use sp_runtime::traits::Block as BlockT; +use sp_runtime::{ + traits::{Block as BlockT, BlockNumber, Header as HeaderT, NumberFor}, + OpaqueExtrinsic, +}; use sp_session::SessionKeys; use sp_transaction_pool::runtime_api::TaggedTransactionQueue; -use std::path::PathBuf; +use std::{fmt::Debug, path::PathBuf, str::FromStr}; + +pub trait NodeBlock: + BlockT + + for<'de> serde::Deserialize<'de> +{ + type BoundedFromStrErr: Debug; + type BoundedNumber: FromStr + BlockNumber; + type BoundedHeader: HeaderT + Unpin; +} + +impl NodeBlock for T +where + T: BlockT + for<'de> serde::Deserialize<'de>, + ::Header: Unpin, + as FromStr>::Err: Debug, +{ + type BoundedFromStrErr = as FromStr>::Err; + type BoundedNumber = NumberFor; + type BoundedHeader = ::Header; +} /// Convenience trait that defines the basic bounds for the `RuntimeApi` of a parachain node. pub trait NodeRuntimeApi: diff --git a/cumulus/polkadot-parachain/src/rpc.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs similarity index 59% rename from cumulus/polkadot-parachain/src/rpc.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs index 283a73d931d769fbd7b521c6f8a4a7558fc48be0..a4e157e87216e12f88b178740478f396424bdde3 100644 --- a/cumulus/polkadot-parachain/src/rpc.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs @@ -18,16 +18,13 @@ #![warn(missing_docs)] -use crate::{ - common::ConstructNodeRuntimeApi, - service::{ParachainBackend, ParachainClient}, +use crate::common::{ + types::{AccountId, Balance, Nonce, ParachainBackend, ParachainClient}, + ConstructNodeRuntimeApi, }; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; -use parachains_common::{AccountId, Balance, Block, Nonce}; -use sc_rpc::{ - dev::{Dev, DevApiServer}, - DenyUnsafe, -}; +use sc_rpc::dev::{Dev, DevApiServer}; +use sp_runtime::traits::Block as BlockT; use std::{marker::PhantomData, sync::Arc}; use substrate_frame_rpc_system::{System, SystemApiServer}; use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer}; @@ -37,60 +34,59 @@ pub type RpcExtension = jsonrpsee::RpcModule<()>; pub(crate) trait BuildRpcExtensions { fn build_rpc_extensions( - deny_unsafe: DenyUnsafe, client: Arc, backend: Arc, pool: Arc, ) -> sc_service::error::Result; } -pub(crate) struct BuildEmptyRpcExtensions(PhantomData); +pub(crate) struct BuildEmptyRpcExtensions(PhantomData<(Block, RuntimeApi)>); -impl +impl BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > for BuildEmptyRpcExtensions + ParachainClient, + ParachainBackend, + sc_transaction_pool::FullPool>, + > for BuildEmptyRpcExtensions where - RuntimeApi: ConstructNodeRuntimeApi> + Send + Sync + 'static, + RuntimeApi: + ConstructNodeRuntimeApi> + Send + Sync + 'static, { fn build_rpc_extensions( - _deny_unsafe: DenyUnsafe, - _client: Arc>, - _backend: Arc, - _pool: Arc>>, + _client: Arc>, + _backend: Arc>, + _pool: Arc>>, ) -> sc_service::error::Result { Ok(RpcExtension::new(())) } } -pub(crate) struct BuildParachainRpcExtensions(PhantomData); +pub(crate) struct BuildParachainRpcExtensions(PhantomData<(Block, RuntimeApi)>); -impl +impl BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > for BuildParachainRpcExtensions + ParachainClient, + ParachainBackend, + sc_transaction_pool::FullPool>, + > for BuildParachainRpcExtensions where - RuntimeApi: ConstructNodeRuntimeApi> + Send + Sync + 'static, + RuntimeApi: + ConstructNodeRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + substrate_frame_rpc_system::AccountNonceApi, { fn build_rpc_extensions( - deny_unsafe: DenyUnsafe, - client: Arc>, - backend: Arc, - pool: Arc>>, + client: Arc>, + backend: Arc>, + pool: Arc>>, ) -> sc_service::error::Result { let build = || -> Result> { let mut module = RpcExtension::new(()); - module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client.clone()).into_rpc())?; - module.merge(StateMigration::new(client.clone(), backend, deny_unsafe).into_rpc())?; - module.merge(Dev::new(client, deny_unsafe).into_rpc())?; + module.merge(StateMigration::new(client.clone(), backend).into_rpc())?; + module.merge(Dev::new(client).into_rpc())?; Ok(module) }; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..bddbb0a85d0365feb4a337673aab65081b676098 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs @@ -0,0 +1,70 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Runtime parameters. + +use sc_chain_spec::ChainSpec; + +/// The Aura ID used by the Aura consensus +#[derive(PartialEq)] +pub enum AuraConsensusId { + /// Ed25519 + Ed25519, + /// Sr25519 + Sr25519, +} + +/// The choice of consensus for the parachain omni-node. +#[derive(PartialEq)] +pub enum Consensus { + /// Aura consensus. + Aura(AuraConsensusId), +} + +/// The choice of block number for the parachain omni-node. +#[derive(PartialEq)] +pub enum BlockNumber { + /// u32 + U32, + /// u64 + U64, +} + +/// Helper enum listing the supported Runtime types +#[derive(PartialEq)] +pub enum Runtime { + /// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be + /// an omni-node, and simply run a node with the given consensus algorithm. + Omni(BlockNumber, Consensus), + /// Shell + Shell, +} + +/// Helper trait used for extracting the Runtime variant from the chain spec ID. +pub trait RuntimeResolver { + /// Extract the Runtime variant from the chain spec ID. + fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result; +} + +/// Default implementation for `RuntimeResolver` that just returns +/// `Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519))`. +pub struct DefaultRuntimeResolver; + +impl RuntimeResolver for DefaultRuntimeResolver { + fn runtime(&self, _chain_spec: &dyn ChainSpec) -> sc_cli::Result { + Ok(Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519))) + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs new file mode 100644 index 0000000000000000000000000000000000000000..0c0230296eb8fcff5bb11e417de6691f05c12fcb --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs @@ -0,0 +1,395 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::common::{ + command::NodeCommandRunner, + rpc::BuildRpcExtensions, + types::{ + ParachainBackend, ParachainBlockImport, ParachainClient, ParachainHostFunctions, + ParachainService, + }, + ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs, +}; +use cumulus_client_cli::CollatorOptions; +use cumulus_client_service::{ + build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks, + BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, +}; +use cumulus_primitives_core::{BlockT, ParaId}; +use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; +use parachains_common::Hash; +use polkadot_primitives::CollatorPair; +use prometheus_endpoint::Registry; +use sc_consensus::DefaultImportQueue; +use sc_executor::{HeapAllocStrategy, DEFAULT_HEAP_ALLOC_STRATEGY}; +use sc_network::{config::FullNetworkConfiguration, NetworkBackend, NetworkBlock}; +use sc_service::{Configuration, ImportQueue, PartialComponents, TaskManager}; +use sc_sysinfo::HwBench; +use sc_telemetry::{TelemetryHandle, TelemetryWorker}; +use sc_transaction_pool::FullPool; +use sp_keystore::KeystorePtr; +use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; + +pub(crate) trait BuildImportQueue { + fn build_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry_handle: Option, + task_manager: &TaskManager, + ) -> sc_service::error::Result>; +} + +pub(crate) trait StartConsensus +where + RuntimeApi: ConstructNodeRuntimeApi>, +{ + fn start_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + backend: Arc>, + node_extra_args: NodeExtraArgs, + ) -> Result<(), sc_service::Error>; +} + +/// Checks that the hardware meets the requirements and print a warning otherwise. +fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { + // Polkadot para-chains should generally use these requirements to ensure that the relay-chain + // will not take longer than expected to import its blocks. + if let Err(err) = + frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench, 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", + err + ); + } +} + +pub(crate) trait NodeSpec { + type Block: NodeBlock; + + type RuntimeApi: ConstructNodeRuntimeApi< + Self::Block, + ParachainClient, + >; + + type BuildImportQueue: BuildImportQueue; + + type BuildRpcExtensions: BuildRpcExtensions< + ParachainClient, + ParachainBackend, + FullPool>, + >; + + type StartConsensus: StartConsensus; + + const SYBIL_RESISTANCE: CollatorSybilResistance; + + /// Starts a `ServiceBuilder` for a full service. + /// + /// Use this macro if you don't actually need the full service, but just the builder in order to + /// be able to perform chain operations. + fn new_partial( + config: &Configuration, + ) -> sc_service::error::Result> { + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let heap_pages = + config.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.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(); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts_record_import::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + true, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); + + let import_queue = Self::BuildImportQueue::build_import_queue( + client.clone(), + block_import.clone(), + config, + telemetry.as_ref().map(|telemetry| telemetry.handle()), + &task_manager, + )?; + + Ok(PartialComponents { + backend, + client, + import_queue, + keystore_container, + task_manager, + transaction_pool, + select_chain: (), + other: (block_import, telemetry, telemetry_worker_handle), + }) + } + + /// Start a node with the given parachain spec. + /// + /// This is the actual implementation that is abstract over the executor and the runtime api. + fn start_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, + node_extra_args: NodeExtraArgs, + ) -> Pin>>> + where + Net: NetworkBackend, + { + Box::pin(async move { + let parachain_config = prepare_node_config(parachain_config); + + let params = Self::new_partial(¶chain_config)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + + let client = params.client.clone(); + let backend = params.backend.clone(); + + let mut task_manager = params.task_manager; + let (relay_chain_interface, collator_key) = build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options.clone(), + hwbench.clone(), + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue_service = params.import_queue.service(); + let net_config = FullNetworkConfiguration::<_, _, Net>::new( + ¶chain_config.network, + prometheus_registry.clone(), + ); + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + build_network(BuildNetworkParams { + parachain_config: ¶chain_config, + net_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + para_id, + spawn_handle: task_manager.spawn_handle(), + relay_chain_interface: relay_chain_interface.clone(), + import_queue: params.import_queue, + sybil_resistance_level: Self::SYBIL_RESISTANCE, + }) + .await?; + + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |_| { + Self::BuildRpcExtensions::build_rpc_extensions( + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + ) + }) + }; + + sc_service::spawn_tasks(sc_service::SpawnTasksParams { + rpc_builder, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.keystore(), + backend: backend.clone(), + network: network.clone(), + sync_service: sync_service.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if let Some(hwbench) = hwbench { + sc_sysinfo::print_hwbench(&hwbench); + if validator { + warn_if_slow_hardware(&hwbench); + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), + ); + } + } + + let announce_block = { + let sync_service = sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + start_relay_chain_tasks(StartRelayChainTasksParams { + client: client.clone(), + announce_block: announce_block.clone(), + para_id, + relay_chain_interface: relay_chain_interface.clone(), + task_manager: &mut task_manager, + da_recovery_profile: if validator { + DARecoveryProfile::Collator + } else { + DARecoveryProfile::FullNode + }, + import_queue: import_queue_service, + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle.clone()), + sync_service, + })?; + + if validator { + Self::StartConsensus::start_consensus( + client.clone(), + block_import, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + params.keystore_container.keystore(), + relay_chain_slot_duration, + para_id, + collator_key.expect("Command line arguments do not allow this. qed"), + overseer_handle, + announce_block, + backend.clone(), + node_extra_args, + )?; + } + + start_network.start_network(); + + Ok(task_manager) + }) + } +} + +pub(crate) trait DynNodeSpec: NodeCommandRunner { + fn start_node( + self: Box, + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, + node_extra_args: NodeExtraArgs, + ) -> Pin>>>; +} + +impl DynNodeSpec for T +where + T: NodeSpec + NodeCommandRunner, +{ + fn start_node( + self: Box, + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + para_id: ParaId, + hwbench: Option, + node_extra_args: NodeExtraArgs, + ) -> Pin>>> { + match parachain_config.network.network_backend { + sc_network::config::NetworkBackendType::Libp2p => + ::start_node::>( + parachain_config, + polkadot_config, + collator_options, + para_id, + hwbench, + node_extra_args, + ), + sc_network::config::NetworkBackendType::Litep2p => + ::start_node::( + parachain_config, + polkadot_config, + collator_options, + para_id, + hwbench, + node_extra_args, + ), + } + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs new file mode 100644 index 0000000000000000000000000000000000000000..9cfdcb22451c722edf8a839e4d7232c1bb245dc0 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs @@ -0,0 +1,56 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; +use cumulus_primitives_core::relay_chain::UncheckedExtrinsic; +use sc_consensus::DefaultImportQueue; +use sc_executor::WasmExecutor; +use sc_service::{PartialComponents, TFullBackend, TFullClient}; +use sc_telemetry::{Telemetry, TelemetryWorkerHandle}; +use sc_transaction_pool::FullPool; +use sp_runtime::{generic, traits::BlakeTwo256}; +use std::sync::Arc; + +pub use parachains_common::{AccountId, Balance, Hash, Nonce}; + +type Header = generic::Header; +pub type Block = generic::Block, UncheckedExtrinsic>; + +#[cfg(not(feature = "runtime-benchmarks"))] +pub type ParachainHostFunctions = cumulus_client_service::ParachainHostFunctions; +#[cfg(feature = "runtime-benchmarks")] +pub type ParachainHostFunctions = ( + cumulus_client_service::ParachainHostFunctions, + frame_benchmarking::benchmarking::HostFunctions, +); + +pub type ParachainClient = + TFullClient>; + +pub type ParachainBackend = TFullBackend; + +pub type ParachainBlockImport = + TParachainBlockImport>, ParachainBackend>; + +/// Assembly of PartialComponents (enough to run chain ops subcommands) +pub type ParachainService = PartialComponents< + ParachainClient, + ParachainBackend, + (), + DefaultImportQueue, + FullPool>, + (ParachainBlockImport, Option, Option), +>; diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs similarity index 67% rename from cumulus/polkadot-parachain/src/fake_runtime_api/mod.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs index 29e2736b06ff3a1092211224abd73092bd7ee46c..02aa867d70fecd45cb73e38eed734da8400eccda 100644 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/mod.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs @@ -17,5 +17,19 @@ //! In an ideal world this would be one runtime which would simplify the code massively. //! This is not an ideal world - Polkadot Asset Hub has a different key type. -pub mod asset_hub_polkadot_aura; -pub mod aura; +mod utils; + +use utils::{impl_node_runtime_apis, imports::*}; + +type CustomBlock = crate::common::types::Block; +pub mod aura_sr25519 { + use super::*; + struct FakeRuntime; + impl_node_runtime_apis!(FakeRuntime, CustomBlock, sp_consensus_aura::sr25519::AuthorityId); +} + +pub mod aura_ed25519 { + use super::*; + struct FakeRuntime; + impl_node_runtime_apis!(FakeRuntime, CustomBlock, sp_consensus_aura::ed25519::AuthorityId); +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..442b87b5d77523150b4464739c23bbb7b80522e5 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs @@ -0,0 +1,220 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +pub(crate) mod imports { + pub use parachains_common::{AccountId, Balance, Nonce}; + pub use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; + pub use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, + }; + pub use sp_weights::Weight; +} + +macro_rules! impl_node_runtime_apis { + ($runtime: ty, $block: tt, $aura_id: ty) => { + sp_api::impl_runtime_apis! { + impl sp_api::Core<$block> for $runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + + fn execute_block(_: $block) { + unimplemented!() + } + + fn initialize_block( + _: &<$block as BlockT>::Header + ) -> sp_runtime::ExtrinsicInclusionMode { + unimplemented!() + } + } + + impl sp_api::Metadata<$block> for $runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + + fn metadata_at_version(_: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> Vec { + unimplemented!() + } + } + + impl sp_consensus_aura::AuraApi<$block, $aura_id> for $runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + unimplemented!() + } + + fn authorities() -> Vec<$aura_id> { + unimplemented!() + } + } + + impl cumulus_primitives_aura::AuraUnincludedSegmentApi<$block> for $runtime { + fn can_build_upon( + _: <$block as BlockT>::Hash, + _: cumulus_primitives_aura::Slot, + ) -> bool { + unimplemented!() + } + } + + impl sp_block_builder::BlockBuilder<$block> for $runtime { + fn apply_extrinsic(_: <$block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { + unimplemented!() + } + + fn finalize_block() -> <$block as BlockT>::Header { + unimplemented!() + } + + fn inherent_extrinsics( + _: sp_inherents::InherentData + ) -> Vec<<$block as BlockT>::Extrinsic> { + unimplemented!() + } + + fn check_inherents( + _: $block, + _: sp_inherents::InherentData + ) -> sp_inherents::CheckInherentsResult { + unimplemented!() + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<$block> for $runtime { + fn validate_transaction( + _: TransactionSource, + _: <$block as BlockT>::Extrinsic, + _: <$block as BlockT>::Hash, + ) -> TransactionValidity { + unimplemented!() + } + } + + impl sp_session::SessionKeys<$block> for $runtime { + fn generate_session_keys(_: Option>) -> Vec { + unimplemented!() + } + + fn decode_session_keys( + _: Vec, + ) -> Option, KeyTypeId)>> { + unimplemented!() + } + } + + impl + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + $block, + Balance, + > for $runtime + { + fn query_info( + _: <$block as BlockT>::Extrinsic, + _: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + unimplemented!() + } + fn query_fee_details( + _: <$block as BlockT>::Extrinsic, + _: u32, + ) -> pallet_transaction_payment::FeeDetails { + unimplemented!() + } + fn query_weight_to_fee(_: Weight) -> Balance { + unimplemented!() + } + fn query_length_to_fee(_: u32) -> Balance { + unimplemented!() + } + } + + impl cumulus_primitives_core::CollectCollationInfo<$block> for $runtime { + fn collect_collation_info( + _: &<$block as BlockT>::Header + ) -> cumulus_primitives_core::CollationInfo { + unimplemented!() + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime<$block> for $runtime { + fn on_runtime_upgrade( + _: frame_try_runtime::UpgradeCheckSelect + ) -> (Weight, Weight) { + unimplemented!() + } + + fn execute_block( + _: $block, + _: bool, + _: bool, + _: frame_try_runtime::TryStateSelect, + ) -> Weight { + unimplemented!() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi< + $block, + AccountId, + Nonce + > for $runtime { + fn account_nonce(_: AccountId) -> Nonce { + unimplemented!() + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark<$block> for $runtime { + fn benchmark_metadata(_: bool) -> ( + Vec, + Vec, + ) { + unimplemented!() + } + + fn dispatch_benchmark( + _: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + unimplemented!() + } + } + + impl sp_genesis_builder::GenesisBuilder<$block> for $runtime { + fn build_state(_: Vec) -> sp_genesis_builder::Result { + unimplemented!() + } + + fn get_preset(_id: &Option) -> Option> { + unimplemented!() + } + + fn preset_names() -> Vec { + unimplemented!() + } + } + } + }; +} + +pub(crate) use impl_node_runtime_apis; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..6aa2f656a48ba626c432871f85d44c2965c462a3 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs @@ -0,0 +1,52 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Helper library that can be used to run a parachain node. +//! +//! ## Overview +//! +//! This library can be used to run a parachain node while also customizing the chain specs +//! that are supported by default by the `--chain-spec` argument of the node's `CLI` +//! and the parameters of the runtime that is associated with each of these chain specs. +//! +//! ## API +//! +//! The library exposes the possibility to provide a [`RunConfig`]. Through this structure +//! 2 optional configurations can be provided: +//! - a chain spec loader (an implementation of [`chain_spec::LoadSpec`]): this can be used for +//! providing the chain specs that are supported by default by the `--chain-spec` argument of the +//! node's `CLI` and the actual chain config associated with each one. +//! - a runtime resolver (an implementation of [`runtime::RuntimeResolver`]): this can be used for +//! providing the parameters of the runtime that is associated with each of the chain specs +//! +//! Apart from this, a [`CliConfig`] can also be provided, that can be used to customize some +//! user-facing binary author, support url, etc. +//! +//! ## Examples +//! +//! For an example, see the `polkadot-parachain-bin` crate. + +#![deny(missing_docs)] + +mod cli; +mod command; +mod common; +mod fake_runtime_api; +mod service; + +pub use cli::CliConfig; +pub use command::{run, RunConfig}; +pub use common::{chain_spec, runtime}; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs new file mode 100644 index 0000000000000000000000000000000000000000..303ec1e3b298bf551bfbdbd118bb077fbb2dfa77 --- /dev/null +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs @@ -0,0 +1,552 @@ +// 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::{ + aura::{AuraIdT, AuraRuntimeApi}, + rpc::{BuildEmptyRpcExtensions, BuildParachainRpcExtensions}, + spec::{BuildImportQueue, DynNodeSpec, NodeSpec, StartConsensus}, + types::{ + AccountId, Balance, Block, Hash, Nonce, ParachainBackend, ParachainBlockImport, + ParachainClient, + }, + ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs, + }, + fake_runtime_api::aura_sr25519::RuntimeApi as FakeRuntimeApi, +}; +use cumulus_client_collator::service::{ + CollatorService, ServiceInterface as CollatorServiceInterface, +}; +use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams}; +#[docify::export(slot_based_colator_import)] +use cumulus_client_consensus_aura::collators::slot_based::{ + self as slot_based, Params as SlotBasedParams, +}; +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}; +use futures::prelude::*; +use polkadot_primitives::CollatorPair; +use prometheus_endpoint::Registry; +use sc_client_api::BlockchainEvents; +use sc_client_db::DbHash; +use sc_consensus::{ + import_queue::{BasicQueue, Verifier as VerifierT}, + BlockImportParams, DefaultImportQueue, +}; +use sc_service::{Configuration, Error, TaskManager}; +use sc_telemetry::TelemetryHandle; +use sc_transaction_pool::FullPool; +use sp_api::ProvideRuntimeApi; +use sp_inherents::CreateInherentDataProviders; +use sp_keystore::KeystorePtr; +use sp_runtime::{ + app_crypto::AppCrypto, + traits::{Block as BlockT, Header as HeaderT}, +}; +use std::{marker::PhantomData, sync::Arc, time::Duration}; + +/// Build the import queue for the shell runtime. +pub(crate) struct BuildShellImportQueue; + +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>, + relay_chain_verifier: Box>, + _phantom: PhantomData, +} + +#[async_trait::async_trait] +impl VerifierT for Verifier +where + Client: ProvideRuntimeApi + Send + Sync, + Client::Api: AuraRuntimeApi, + AuraId: AuraIdT + Sync, +{ + async fn verify( + &self, + block_import: BlockImportParams, + ) -> Result, String> { + if self.client.runtime_api().has_aura_api(*block_import.header.parent_hash()) { + self.aura_verifier.verify(block_import).await + } else { + self.relay_chain_verifier.verify(block_import).await + } + } +} + +/// Build the import queue for parachain runtimes that started with relay chain consensus and +/// switched to aura. +pub(crate) struct BuildRelayToAuraImportQueue( + PhantomData<(Block, RuntimeApi, AuraId)>, +); + +impl BuildImportQueue + for BuildRelayToAuraImportQueue +where + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi, + AuraId: AuraIdT + Sync, +{ + fn build_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry_handle: Option, + task_manager: &TaskManager, + ) -> sc_service::error::Result> { + let verifier_client = client.clone(); + + let aura_verifier = cumulus_client_consensus_aura::build_verifier::< + ::Pair, + _, + _, + _, + >(cumulus_client_consensus_aura::BuildVerifierParams { + client: verifier_client.clone(), + create_inherent_data_providers: move |parent_hash, _| { + let cidp_client = verifier_client.clone(); + async move { + let slot_duration = cumulus_client_consensus_aura::slot_duration_at( + &*cidp_client, + parent_hash, + )?; + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + } + }, + telemetry: telemetry_handle, + }); + + let relay_chain_verifier = + Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })); + + let verifier = Verifier { + client, + relay_chain_verifier, + aura_verifier: Box::new(aura_verifier), + _phantom: PhantomData, + }; + + let registry = config.prometheus_registry(); + let spawner = task_manager.spawn_essential_handle(); + + Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)) + } +} + +/// Uses the lookahead collator to support async backing. +/// +/// Start an aura powered parachain node. Some system chains use this. +pub(crate) struct AuraNode( + pub PhantomData<(Block, RuntimeApi, AuraId, StartConsensus)>, +); + +impl Default + for AuraNode +{ + fn default() -> Self { + Self(Default::default()) + } +} + +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 Block = Block; + type RuntimeApi = RuntimeApi; + type BuildImportQueue = BuildRelayToAuraImportQueue; + type BuildRpcExtensions = BuildParachainRpcExtensions; + type StartConsensus = StartConsensus; + const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Resistant; +} + +pub fn new_aura_node_spec( + extra_args: &NodeExtraArgs, +) -> Box +where + Block: NodeBlock, + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + substrate_frame_rpc_system::AccountNonceApi, + AuraId: AuraIdT + Sync, +{ + if extra_args.use_slot_based_consensus { + Box::new(AuraNode::< + Block, + RuntimeApi, + AuraId, + StartSlotBasedAuraConsensus, + >::default()) + } else { + Box::new(AuraNode::< + Block, + RuntimeApi, + AuraId, + StartLookaheadAuraConsensus, + >::default()) + } +} + +/// 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)>, +); + +impl, RuntimeApi, AuraId> + StartSlotBasedAuraConsensus +where + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi, + AuraId: AuraIdT + Sync, +{ + #[docify::export_content] + fn launch_slot_based_collator( + params: SlotBasedParams< + ParachainBlockImport, + CIDP, + ParachainClient, + ParachainBackend, + Arc, + CHP, + Proposer, + CS, + >, + task_manager: &TaskManager, + ) where + CIDP: CreateInherentDataProviders + 'static, + CIDP::InherentDataProviders: Send, + CHP: cumulus_client_consensus_common::ValidationCodeHashProvider + Send + 'static, + Proposer: ProposerInterface + Send + Sync + 'static, + CS: CollatorServiceInterface + Send + Sync + Clone + 'static, + { + let (collation_future, block_builder_future) = + slot_based::run::::Pair, _, _, _, _, _, _, _, _>(params); + + task_manager.spawn_essential_handle().spawn( + "collation-task", + Some("parachain-block-authoring"), + collation_future, + ); + task_manager.spawn_essential_handle().spawn( + "block-builder-task", + Some("parachain-block-authoring"), + block_builder_future, + ); + } +} + +impl, RuntimeApi, AuraId> StartConsensus + for StartSlotBasedAuraConsensus +where + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi, + AuraId: AuraIdT + Sync, +{ + fn start_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + _overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + backend: Arc>, + _node_extra_args: NodeExtraArgs, + ) -> Result<(), Error> { + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let proposer = Proposer::new(proposer_factory); + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); + + let client_for_aura = client.clone(); + let params = SlotBasedParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend.clone(), + relay_client: relay_chain_interface, + code_hash_provider: move |block_hash| { + client_for_aura.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + }, + keystore, + collator_key, + para_id, + relay_chain_slot_duration, + proposer, + collator_service, + authoring_duration: Duration::from_millis(2000), + reinitialize: false, + slot_drift: Duration::from_secs(1), + }; + + // We have a separate function only to be able to use `docify::export` on this piece of + // code. + Self::launch_slot_based_collator(params, task_manager); + + Ok(()) + } +} + +/// Wait for the Aura runtime API to appear on chain. +/// This is useful for chains that started out without Aura. Components that +/// are depending on Aura functionality will wait until Aura appears in the runtime. +async fn wait_for_aura( + client: Arc>, +) where + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi, + AuraId: AuraIdT + Sync, +{ + let finalized_hash = client.chain_info().finalized_hash; + if client.runtime_api().has_aura_api(finalized_hash) { + return; + }; + + let mut stream = client.finality_notification_stream(); + while let Some(notification) = stream.next().await { + if client.runtime_api().has_aura_api(notification.hash) { + return; + } + } +} + +/// Start consensus using the lookahead aura collator. +pub(crate) struct StartLookaheadAuraConsensus( + PhantomData<(Block, RuntimeApi, AuraId)>, +); + +impl, RuntimeApi, AuraId> StartConsensus + for StartLookaheadAuraConsensus +where + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi, + AuraId: AuraIdT + Sync, +{ + fn start_consensus( + client: Arc>, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, + backend: Arc>, + node_extra_args: NodeExtraArgs, + ) -> Result<(), Error> { + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + let collator_service = CollatorService::new( + client.clone(), + Arc::new(task_manager.spawn_handle()), + announce_block, + client.clone(), + ); + + let params = aura::ParamsWithExport { + export_pov: node_extra_args.export_pov, + params: AuraParams { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend, + relay_client: relay_chain_interface, + code_hash_provider: { + let client = client.clone(); + move |block_hash| { + client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) + } + }, + keystore, + collator_key, + para_id, + overseer_handle, + relay_chain_slot_duration, + proposer: Proposer::new(proposer_factory), + collator_service, + authoring_duration: Duration::from_millis(2000), + reinitialize: false, + }, + }; + + let fut = async move { + wait_for_aura(client).await; + aura::run_with_export::::Pair, _, _, _, _, _, _, _, _>( + params, + ) + .await; + }; + task_manager.spawn_essential_handle().spawn("aura", None, fut); + + Ok(()) + } +} diff --git a/cumulus/polkadot-parachain/tests/benchmark_storage_works.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/benchmark_storage_works.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs diff --git a/cumulus/polkadot-parachain/tests/common.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/common.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs diff --git a/cumulus/polkadot-parachain/tests/polkadot_argument_parsing.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/polkadot_argument_parsing.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs diff --git a/cumulus/polkadot-parachain/tests/polkadot_mdns_issue.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/polkadot_mdns_issue.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs diff --git a/cumulus/polkadot-parachain/tests/purge_chain_works.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/purge_chain_works.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs diff --git a/cumulus/polkadot-parachain/tests/running_the_node_and_interrupt.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs similarity index 100% rename from cumulus/polkadot-parachain/tests/running_the_node_and_interrupt.rs rename to cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index af5bccdc416f4b49714222de3ebde7fe545feef8..233ae9866966961049ff845f652a965e8dc4d1c0 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -14,25 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use hex_literal::hex; use parachains_common::{AccountId, AuraId, Balance as AssetHubBalance}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; 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. /// @@ -229,21 +219,7 @@ fn asset_hub_rococo_like_development_config( .with_name(name) .with_id(chain_id) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, - para_id.into(), - )) + .with_genesis_config_preset_name("development") .with_properties(properties) .build() } @@ -274,35 +250,7 @@ fn asset_hub_rococo_like_local_config( .with_name(name) .with_id(chain_id) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, - para_id.into(), - )) + .with_genesis_config_preset_name("local") .with_properties(properties) .build() } @@ -319,81 +267,7 @@ pub fn asset_hub_rococo_genesis_config() -> GenericChainSpec { .with_name("Rococo Asset Hub") .with_id("asset-hub-rococo") .with_chain_type(ChainType::Live) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![ - // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG - ( - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") - .unchecked_into(), - ), - // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri - ( - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") - .unchecked_into(), - ), - // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c - ( - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") - .unchecked_into(), - ), - // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt - ( - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") - .unchecked_into(), - ), - ], - Vec::new(), - ASSET_HUB_ROCOCO_ED * 524_288, - para_id.into(), - )) + .with_genesis_config_preset_name("genesis") .with_properties(properties) .build() } - -fn asset_hub_rococo_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - endowment: AssetHubBalance, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": asset_hub_rococo_runtime::BalancesConfig { - balances: endowed_accounts - .iter() - .cloned() - .map(|k| (k, endowment)) - .collect(), - }, - "parachainInfo": asset_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() - }, - "collatorSelection": asset_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, - ..Default::default() - }, - "session": asset_hub_rococo_runtime::SessionConfig { - keys: invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - asset_hub_rococo_session_keys(aura), // session keys - ) - }) - .collect(), - ..Default::default() - }, - "polkadotXcm": asset_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - } - }) -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 3b7376d045b8bdd31cfe632bbfcd8c000fc1fdec..754bd851b40ad326aa1e199152b458153b525202 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, GenericChainSpec}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed}; use cumulus_primitives_core::ParaId; use parachains_common::Balance as BridgeHubBalance; +use polkadot_parachain_lib::chain_spec::GenericChainSpec; use sc_chain_spec::ChainSpec; use sp_core::sr25519; use std::str::FromStr; @@ -129,8 +130,9 @@ fn ensure_id(id: &str) -> Result<&str, String> { /// Sub-module for Rococo setup pub mod rococo { use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; - use crate::chain_spec::{Extensions, GenericChainSpec, SAFE_XCM_VERSION}; + use crate::chain_spec::SAFE_XCM_VERSION; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use super::BridgeHubBalance; @@ -254,8 +256,9 @@ pub mod kusama { /// Sub-module for Westend setup. pub mod westend { use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; - use crate::chain_spec::{Extensions, GenericChainSpec, SAFE_XCM_VERSION}; + use crate::chain_spec::SAFE_XCM_VERSION; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use super::BridgeHubBalance; diff --git a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs index c0a9f195d89bc1a3b56001e8e60ad1834660959c..865a2a917086ea212aeb8faa4c8fc11ee95457d3 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs @@ -14,12 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId, Balance as CollectivesBalance}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs deleted file mode 100644 index 4e89b81d1be40fb5b0bc5d5f689a8883ba612fa1..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; -use cumulus_primitives_core::ParaId; -use hex_literal::hex; -use parachains_common::{AccountId, AuraId}; -use sc_service::ChainType; -use sp_core::{crypto::UncheckedInto, sr25519}; - -/// No relay chain suffix because the id is the same over all relay chains. -const CONTRACTS_PARACHAIN_ID: u32 = 1002; - -/// The existential deposit is determined by the runtime "contracts-rococo". -const CONTRACTS_ROCOCO_ED: contracts_rococo_runtime::Balance = - testnet_parachains_constants::rococo::currency::EXISTENTIAL_DEPOSIT; - -pub fn contracts_rococo_development_config() -> GenericChainSpec { - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - - GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { - relay_chain: "rococo-local".into(), // You MUST set this to the correct network! - para_id: CONTRACTS_PARACHAIN_ID, - }, - ) - .with_name("Contracts on Rococo Development") - .with_id("contracts-rococo-dev") - .with_chain_type(ChainType::Development) - .with_genesis_config_patch(contracts_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_boot_nodes(Vec::new()) - .build() -} - -pub fn contracts_rococo_local_config() -> GenericChainSpec { - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - - GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { - relay_chain: "rococo-local".into(), // You MUST set this to the correct network! - para_id: CONTRACTS_PARACHAIN_ID, - }, - ) - .with_name("Contracts on Rococo") - .with_id("contracts-rococo-local") - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(contracts_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_properties(properties) - .build() -} - -pub fn contracts_rococo_config() -> GenericChainSpec { - // Give your base currency a unit name and decimal places - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - - GenericChainSpec::builder( - contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "rococo".into(), para_id: CONTRACTS_PARACHAIN_ID } - ) - .with_name("Contracts on Rococo") - .with_id("contracts-rococo") - .with_chain_type(ChainType::Live) - .with_genesis_config_patch(contracts_rococo_genesis( - vec![ - // 5GKFbTTgrVS4Vz1UWWHPqMZQNFWZtqo7H2KpCDyYhEL3aS26 - ( - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .into(), - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .unchecked_into(), - ), - // 5EPRJHm2GpABVWcwnAujcrhnrjFZyDGd5TwKFzkBoGgdRyv2 - ( - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .into(), - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .unchecked_into(), - ), - // 5GH62vrJrVZxLREcHzm2PR5uTLAT5RQMJitoztCGyaP4o3uM - ( - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .into(), - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .unchecked_into(), - ), - // 5FHfoJDLdjRYX5KXLRqMDYBbWrwHLMtti21uK4QByUoUAbJF - ( - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .into(), - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .unchecked_into(), - ), - ], - // Warning: The configuration for a production chain should not contain - // any endowed accounts here, otherwise it'll be minting extra native tokens - // from the relay chain on the parachain. - vec![ - // NOTE: Remove endowed accounts if deployed on other relay chains. - // Endowed accounts - hex!["baa78c7154c7f82d6d377177e20bcab65d327eca0086513f9964f5a0f6bdad56"].into(), - // AccountId of an account which `ink-waterfall` uses for automated testing - hex!["0e47e2344d523c3cc5c34394b0d58b9a4200e813a038e6c5a6163cc07d70b069"].into(), - ], - CONTRACTS_PARACHAIN_ID.into(), - )) - .with_boot_nodes(vec![ - "/dns/contracts-collator-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk" - .parse() - .expect("MultiaddrWithPeerId"), - "/dns/contracts-collator-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX" - .parse() - .expect("MultiaddrWithPeerId"), - ]) - .with_properties(properties) - .build() -} - -fn contracts_rococo_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - id: ParaId, -) -> serde_json::Value { - serde_json::json!( { - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": CONTRACTS_ROCOCO_ED * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - contracts_rococo_runtime::SessionKeys { aura }, // session keys - ) - }) - .collect::>(), - }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "sudo": { - "key": Some(sp_runtime::AccountId32::from(hex![ - "2681a28014e7d3a5bfb32a003b3571f53c408acbc28d351d6bf58f5028c4ef14" - ])), - }, - }) -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs index fe60b09fd8b21715286f5fd40018be06ba978c06..fec3f56e6d3520ddcee8985f6be388117df5756e 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::GenericChainSpec; use cumulus_primitives_core::ParaId; +use polkadot_parachain_lib::chain_spec::GenericChainSpec; use sc_chain_spec::{ChainSpec, ChainType}; use std::{borrow::Cow, str::FromStr}; @@ -107,8 +107,9 @@ impl CoretimeRuntimeType { CoretimeRuntimeType::Kusama => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/coretime-kusama.json")[..], )?)), - CoretimeRuntimeType::Polkadot => - todo!("Generate chain-spec: ../../chain-specs/coretime-polkadot.json"), + CoretimeRuntimeType::Polkadot => Ok(Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/coretime-polkadot.json")[..], + )?)), CoretimeRuntimeType::Rococo => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/coretime-rococo.json")[..], )?)), @@ -144,11 +145,12 @@ pub fn chain_type_name(chain_type: &ChainType) -> Cow { /// Sub-module for Rococo setup. pub mod rococo { - use super::{chain_type_name, CoretimeRuntimeType, GenericChainSpec, ParaId}; + use super::{chain_type_name, CoretimeRuntimeType, ParaId}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId, Balance}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; @@ -243,9 +245,10 @@ pub mod rococo { pub mod westend { use super::{chain_type_name, CoretimeRuntimeType, GenericChainSpec, ParaId}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId, Balance}; + use polkadot_parachain_lib::chain_spec::Extensions; use sp_core::sr25519; pub(crate) const CORETIME_WESTEND: &str = "coretime-westend"; diff --git a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs index 77a4123b13ee11b0f158332c26a1695cfe37a668..136411b93e8bcc73a3bbf72510abb1f13ae4d20b 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, Extensions, GenericChainSpec}; +use crate::chain_spec::get_account_id_from_seed; use cumulus_primitives_core::ParaId; use parachains_common::AuraId; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/mod.rs b/cumulus/polkadot-parachain/src/chain_spec/mod.rs index 19047b073b057a06f19e86faf935ecb4fc3c96b5..82aec951704f1c3e7dac7cf6b0636e8b72e758bb 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/mod.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/mod.rs @@ -14,16 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, Signature}; -use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; -use serde::{Deserialize, Serialize}; +use polkadot_parachain_lib::{ + chain_spec::{GenericChainSpec, LoadSpec}, + runtime::{ + AuraConsensusId, 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; pub mod collectives; -pub mod contracts; pub mod coretime; pub mod glutton; pub mod penpal; @@ -35,27 +40,6 @@ pub mod shell; /// The default XCM version to set in genesis config. const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; -/// Generic extensions for Parachain ChainSpecs. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] -pub struct Extensions { - /// The relay chain of the Parachain. - #[serde(alias = "relayChain", alias = "RelayChain")] - pub relay_chain: String, - /// The id of the Parachain. - #[serde(alias = "paraId", alias = "ParaId")] - pub para_id: u32, -} - -impl Extensions { - /// Try to get the extension from the given `ChainSpec`. - pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { - sc_chain_spec::get_extension(chain_spec.extensions()) - } -} - -/// Generic chain spec for all polkadot-parachain runtimes -pub type GenericChainSpec = sc_service::GenericChainSpec; - /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) @@ -80,21 +64,316 @@ pub fn get_collator_keys_from_seed(seed: &str) -> (seed) } +/// Extracts the normalized chain id and parachain id from the input chain id. +/// (H/T to Phala for the idea) +/// E.g. "penpal-kusama-2004" yields ("penpal-kusama", Some(2004)) +fn extract_parachain_id<'a>( + id: &'a str, + para_prefixes: &[&str], +) -> (&'a str, &'a str, Option) { + for para_prefix in para_prefixes { + if let Some(suffix) = id.strip_prefix(para_prefix) { + let para_id: u32 = suffix.parse().expect("Invalid parachain-id suffix"); + return (&id[..para_prefix.len() - 1], id, Some(para_id.into())); + } + } + + (id, id, None) +} + +#[derive(Debug)] +pub(crate) struct ChainSpecLoader; + +impl LoadSpec for ChainSpecLoader { + fn load_spec(&self, id: &str) -> Result, String> { + Ok(match id { + // - Default-like + "staging" => Box::new(rococo_parachain::staging_rococo_parachain_local_config()), + "tick" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/tick.json")[..], + )?), + "trick" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/trick.json")[..], + )?), + "track" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/track.json")[..], + )?), + + // -- Starters + "shell" => Box::new(shell::get_shell_chain_spec()), + "seedling" => Box::new(seedling::get_seedling_chain_spec()), + + // -- Asset Hub Polkadot + "asset-hub-polkadot" | "statemint" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-polkadot.json")[..], + )?), + + // -- Asset Hub Kusama + "asset-hub-kusama" | "statemine" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-kusama.json")[..], + )?), + + // -- Asset Hub Rococo + "asset-hub-rococo-dev" => Box::new(asset_hubs::asset_hub_rococo_development_config()), + "asset-hub-rococo-local" => Box::new(asset_hubs::asset_hub_rococo_local_config()), + // the chain spec as used for generating the upgrade genesis values + "asset-hub-rococo-genesis" => Box::new(asset_hubs::asset_hub_rococo_genesis_config()), + "asset-hub-rococo" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-rococo.json")[..], + )?), + + // -- Asset Hub Westend + "asset-hub-westend-dev" | "westmint-dev" => + Box::new(asset_hubs::asset_hub_westend_development_config()), + "asset-hub-westend-local" | "westmint-local" => + Box::new(asset_hubs::asset_hub_westend_local_config()), + // the chain spec as used for generating the upgrade genesis values + "asset-hub-westend-genesis" | "westmint-genesis" => + Box::new(asset_hubs::asset_hub_westend_config()), + // the shell-based chain spec as used for syncing + "asset-hub-westend" | "westmint" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/asset-hub-westend.json")[..], + )?), + + // -- Polkadot Collectives + "collectives-polkadot" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/collectives-polkadot.json")[..], + )?), + + // -- Westend Collectives + "collectives-westend-dev" => + Box::new(collectives::collectives_westend_development_config()), + "collectives-westend-local" => + Box::new(collectives::collectives_westend_local_config()), + "collectives-westend" => Box::new(GenericChainSpec::from_json_bytes( + &include_bytes!("../../chain-specs/collectives-westend.json")[..], + )?), + + // -- BridgeHub + bridge_like_id + if bridge_like_id.starts_with(bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) => + bridge_like_id + .parse::() + .expect("invalid value") + .load_config()?, + + // -- Coretime + coretime_like_id + if coretime_like_id.starts_with(coretime::CoretimeRuntimeType::ID_PREFIX) => + coretime_like_id + .parse::() + .expect("invalid value") + .load_config()?, + + // -- Penpal + id if id.starts_with("penpal-rococo") => { + let (_, _, para_id) = extract_parachain_id(&id, &["penpal-rococo-"]); + Box::new(penpal::get_penpal_chain_spec( + para_id.expect("Must specify parachain id"), + "rococo-local", + )) + }, + id if id.starts_with("penpal-westend") => { + let (_, _, para_id) = extract_parachain_id(&id, &["penpal-westend-"]); + Box::new(penpal::get_penpal_chain_spec( + para_id.expect("Must specify parachain id"), + "westend-local", + )) + }, + + // -- Glutton Westend + id if id.starts_with("glutton-westend-dev") => { + let (_, _, para_id) = extract_parachain_id(&id, &["glutton-westend-dev-"]); + Box::new(glutton::glutton_westend_development_config( + para_id.expect("Must specify parachain id"), + )) + }, + id if id.starts_with("glutton-westend-local") => { + let (_, _, para_id) = extract_parachain_id(&id, &["glutton-westend-local-"]); + Box::new(glutton::glutton_westend_local_config( + para_id.expect("Must specify parachain id"), + )) + }, + // the chain spec as used for generating the upgrade genesis values + id if id.starts_with("glutton-westend-genesis") => { + let (_, _, para_id) = extract_parachain_id(&id, &["glutton-westend-genesis-"]); + Box::new(glutton::glutton_westend_config( + para_id.expect("Must specify parachain id"), + )) + }, + + // -- People + people_like_id if people_like_id.starts_with(people::PeopleRuntimeType::ID_PREFIX) => + people_like_id + .parse::() + .expect("invalid value") + .load_config()?, + + // -- Fallback (generic chainspec) + "" => { + log::warn!("No ChainSpec.id specified, so using default one, based on rococo-parachain runtime"); + Box::new(rococo_parachain::rococo_parachain_local_config()) + }, + + // -- Loading a specific spec from disk + path => Box::new(GenericChainSpec::from_json_file(path.into())?), + }) + } +} + +/// Helper enum that is used for better distinction of different parachain/runtime configuration +/// (it is based/calculated on ChainSpec's ID attribute) +#[derive(Debug, PartialEq)] +enum LegacyRuntime { + Omni, + Shell, + Seedling, + AssetHubPolkadot, + AssetHub, + Penpal, + Collectives, + Glutton, + BridgeHub(bridge_hubs::BridgeHubRuntimeType), + Coretime(coretime::CoretimeRuntimeType), + People(people::PeopleRuntimeType), +} + +impl LegacyRuntime { + fn from_id(id: &str) -> LegacyRuntime { + let id = id.replace('_', "-"); + + if id.starts_with("shell") { + LegacyRuntime::Shell + } else if id.starts_with("seedling") { + LegacyRuntime::Seedling + } else if id.starts_with("asset-hub-polkadot") | id.starts_with("statemint") { + LegacyRuntime::AssetHubPolkadot + } else if id.starts_with("asset-hub-kusama") | + id.starts_with("statemine") | + id.starts_with("asset-hub-rococo") | + id.starts_with("rockmine") | + id.starts_with("asset-hub-westend") | + id.starts_with("westmint") + { + LegacyRuntime::AssetHub + } else if id.starts_with("penpal") { + LegacyRuntime::Penpal + } else if id.starts_with("collectives-polkadot") || id.starts_with("collectives-westend") { + LegacyRuntime::Collectives + } else if id.starts_with(bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) { + LegacyRuntime::BridgeHub( + id.parse::().expect("Invalid value"), + ) + } else if id.starts_with(coretime::CoretimeRuntimeType::ID_PREFIX) { + LegacyRuntime::Coretime( + id.parse::().expect("Invalid value"), + ) + } else if id.starts_with("glutton") { + LegacyRuntime::Glutton + } else if id.starts_with(people::PeopleRuntimeType::ID_PREFIX) { + LegacyRuntime::People(id.parse::().expect("Invalid value")) + } else { + log::warn!( + "No specific runtime was recognized for ChainSpec's id: '{}', \ + so Runtime::Omni(Consensus::Aura) will be used", + id + ); + LegacyRuntime::Omni + } + } +} + +#[derive(Debug)] +pub(crate) struct RuntimeResolver; + +impl RuntimeResolverT for RuntimeResolver { + fn runtime(&self, chain_spec: &dyn ChainSpec) -> sc_cli::Result { + let legacy_runtime = LegacyRuntime::from_id(chain_spec.id()); + Ok(match legacy_runtime { + LegacyRuntime::AssetHubPolkadot => + Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Ed25519)), + LegacyRuntime::AssetHub | + LegacyRuntime::BridgeHub(_) | + LegacyRuntime::Collectives | + LegacyRuntime::Coretime(_) | + LegacyRuntime::People(_) | + LegacyRuntime::Glutton | + LegacyRuntime::Penpal | + LegacyRuntime::Omni => + Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519)), + LegacyRuntime::Shell | LegacyRuntime::Seedling => Runtime::Shell, + }) + } +} + #[cfg(test)] mod tests { use super::*; + use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup, ChainType, Extension}; + use serde::{Deserialize, Serialize}; + use sp_core::sr25519; + + #[derive( + Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, + )] + #[serde(deny_unknown_fields)] + pub struct Extensions1 { + pub attribute1: String, + pub attribute2: u32, + } + + #[derive( + Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, + )] + #[serde(deny_unknown_fields)] + pub struct Extensions2 { + pub attribute_x: String, + pub attribute_y: String, + pub attribute_z: u32, + } + + pub type DummyChainSpec = sc_service::GenericChainSpec; + + pub fn create_default_with_extensions( + id: &str, + extension: E, + ) -> DummyChainSpec { + DummyChainSpec::builder( + rococo_parachain_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + extension, + ) + .with_name("Dummy local testnet") + .with_id(id) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(crate::chain_spec::rococo_parachain::testnet_genesis( + get_account_id_from_seed::("Alice"), + vec![ + get_from_seed::("Alice"), + get_from_seed::("Bob"), + ], + vec![get_account_id_from_seed::("Alice")], + 1000.into(), + )) + .build() + } #[test] - fn can_decode_extension_camel_and_snake_case() { - let camel_case = r#"{"relayChain":"relay","paraId":1}"#; - let snake_case = r#"{"relay_chain":"relay","para_id":1}"#; - let pascal_case = r#"{"RelayChain":"relay","ParaId":1}"#; + fn test_legacy_runtime_for_different_chain_specs() { + let chain_spec = create_default_with_extensions("shell-1", Extensions1::default()); + assert_eq!(LegacyRuntime::Shell, LegacyRuntime::from_id(chain_spec.id())); + + let chain_spec = create_default_with_extensions("shell-2", Extensions2::default()); + assert_eq!(LegacyRuntime::Shell, LegacyRuntime::from_id(chain_spec.id())); + + let chain_spec = create_default_with_extensions("seedling", Extensions2::default()); + assert_eq!(LegacyRuntime::Seedling, LegacyRuntime::from_id(chain_spec.id())); - let camel_case_extension: Extensions = serde_json::from_str(camel_case).unwrap(); - let snake_case_extension: Extensions = serde_json::from_str(snake_case).unwrap(); - let pascal_case_extension: Extensions = serde_json::from_str(pascal_case).unwrap(); + let chain_spec = + create_default_with_extensions("penpal-rococo-1000", Extensions2::default()); + assert_eq!(LegacyRuntime::Penpal, LegacyRuntime::from_id(chain_spec.id())); - assert_eq!(camel_case_extension, snake_case_extension); - assert_eq!(snake_case_extension, pascal_case_extension); + let chain_spec = crate::chain_spec::rococo_parachain::rococo_parachain_local_config(); + assert_eq!(LegacyRuntime::Omni, LegacyRuntime::from_id(chain_spec.id())); } } diff --git a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs index cb1cb632d63843681a121261be4b83bf5fb88e32..5645bf06b67b22ae9f799fb1796131a4ebca0950 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs @@ -14,12 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, -}; +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/people.rs b/cumulus/polkadot-parachain/src/chain_spec/people.rs index 6100a15018cb17e82401924f6c210e9481815480..3c1150d95422b7016c86dfcf3f4c48ac8f60f0b1 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/people.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/people.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::GenericChainSpec; use cumulus_primitives_core::ParaId; use parachains_common::Balance as PeopleBalance; +use polkadot_parachain_lib::chain_spec::GenericChainSpec; use sc_chain_spec::ChainSpec; use std::str::FromStr; @@ -121,10 +121,10 @@ fn ensure_id(id: &str) -> Result<&str, String> { pub mod rococo { use super::{ParaId, PeopleBalance}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; @@ -231,10 +231,10 @@ pub mod rococo { pub mod westend { use super::{ParaId, PeopleBalance}; use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, GenericChainSpec, - SAFE_XCM_VERSION, + get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId}; + use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs index 0434e5f7be8fb3ffb5f93cbb639f5c90469cf763..9f4a162e67f8d74f1df7a59dc3c1f1b10e10e9a1 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs @@ -16,10 +16,11 @@ //! ChainSpecs dedicated to Rococo parachain setups (for testing and example purposes) -use crate::chain_spec::{get_from_seed, Extensions, GenericChainSpec, SAFE_XCM_VERSION}; +use crate::chain_spec::{get_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use hex_literal::hex; use parachains_common::AccountId; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use polkadot_service::chain_spec::get_account_id_from_seed; use rococo_parachain_runtime::AuraId; use sc_chain_spec::ChainType; diff --git a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs index 32d51622054575d103cb5c3684a216a9dbde6556..a104b58db5d2f03ac5c67ed39ab7246b48df9f7f 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, Extensions, GenericChainSpec}; +use crate::chain_spec::get_account_id_from_seed; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId}; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/shell.rs b/cumulus/polkadot-parachain/src/chain_spec/shell.rs index e0a9875fb96f28870a726073d3c8b8cb249628a9..0a7816ab31932998e17e1d0ce4e04c5c6659e877 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/shell.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/shell.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{Extensions, GenericChainSpec}; use cumulus_primitives_core::ParaId; use parachains_common::AuraId; +use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use super::get_collator_keys_from_seed; diff --git a/cumulus/polkadot-parachain/src/cli.rs b/cumulus/polkadot-parachain/src/cli.rs deleted file mode 100644 index a5fe33dffc9672682e0585194981e42141803de6..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/cli.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -use crate::common::NodeExtraArgs; -use clap::{Command, CommandFactory, FromArgMatches}; -use sc_cli::SubstrateCli; -use std::path::PathBuf; - -/// Sub-commands supported by the collator. -#[derive(Debug, clap::Subcommand)] -pub enum Subcommand { - /// Key management CLI utilities - #[command(subcommand)] - Key(sc_cli::KeySubcommand), - - /// Build a chain specification. - BuildSpec(sc_cli::BuildSpecCmd), - - /// Validate blocks. - CheckBlock(sc_cli::CheckBlockCmd), - - /// Export blocks. - ExportBlocks(sc_cli::ExportBlocksCmd), - - /// Export the state of a given block into a chain spec. - ExportState(sc_cli::ExportStateCmd), - - /// Import blocks. - ImportBlocks(sc_cli::ImportBlocksCmd), - - /// Revert the chain to a previous state. - Revert(sc_cli::RevertCmd), - - /// Remove the whole chain. - PurgeChain(cumulus_client_cli::PurgeChainCmd), - - /// Export the genesis state of the parachain. - #[command(alias = "export-genesis-state")] - ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand), - - /// Export the genesis wasm of the parachain. - ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand), - - /// Sub-commands concerned with benchmarking. - /// The pallet benchmarking moved to the `pallet` sub-command. - #[command(subcommand)] - Benchmark(frame_benchmarking_cli::BenchmarkCmd), -} - -#[derive(Debug, clap::Parser)] -#[command( - propagate_version = true, - args_conflicts_with_subcommands = true, - subcommand_negates_reqs = true, - after_help = crate::examples(Self::executable_name()) -)] -pub struct Cli { - #[command(subcommand)] - pub subcommand: Option, - - #[command(flatten)] - pub run: cumulus_client_cli::RunCmd, - - /// EXPERIMENTAL: Use slot-based collator which can handle elastic scaling. - /// - /// Use with care, this flag is unstable and subject to change. - #[arg(long)] - pub experimental_use_slot_based: bool, - - /// Disable automatic hardware benchmarks. - /// - /// By default these benchmarks are automatically ran at startup and measure - /// the CPU speed, the memory bandwidth and the disk speed. - /// - /// The results are then printed out in the logs, and also sent as part of - /// telemetry, if telemetry is enabled. - #[arg(long)] - pub no_hardware_benchmarks: bool, - - /// Export all `PoVs` build by this collator to the given folder. - /// - /// This is useful for debugging issues that are occurring while validating these `PoVs` on the - /// relay chain. - #[arg(long)] - pub export_pov_to_path: Option, - - /// Relay chain arguments - #[arg(raw = true)] - pub relay_chain_args: Vec, -} - -impl Cli { - pub(crate) fn node_extra_args(&self) -> NodeExtraArgs { - NodeExtraArgs { - use_slot_based_consensus: self.experimental_use_slot_based, - export_pov: self.export_pov_to_path.clone(), - } - } -} - -#[derive(Debug)] -pub struct RelayChainCli { - /// The actual relay chain cli object. - pub base: polkadot_cli::RunCmd, - - /// Optional chain id that should be passed to the relay chain. - pub chain_id: Option, - - /// The base path that should be used by the relay chain. - pub base_path: Option, -} - -impl RelayChainCli { - fn polkadot_cmd() -> Command { - let help_template = color_print::cformat!( - "The arguments that are passed to the relay chain node. \n\ - \n\ - RELAY_CHAIN_ARGS: \n\ - {{options}}", - ); - - polkadot_cli::RunCmd::command() - .no_binary_name(true) - .help_template(help_template) - } - - /// Parse the relay chain CLI parameters using the parachain `Configuration`. - pub fn new<'a>( - para_config: &sc_service::Configuration, - relay_chain_args: impl Iterator, - ) -> Self { - let polkadot_cmd = Self::polkadot_cmd(); - let matches = polkadot_cmd.get_matches_from(relay_chain_args); - let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit()); - - let extension = crate::chain_spec::Extensions::try_get(&*para_config.chain_spec); - let chain_id = extension.map(|e| e.relay_chain.clone()); - - let base_path = para_config.base_path.path().join("polkadot"); - Self { base, chain_id, base_path: Some(base_path) } - } -} diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs deleted file mode 100644 index 3df90c889e8e8f2b73979c26895f8207e7613563..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/command.rs +++ /dev/null @@ -1,856 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -#[cfg(feature = "runtime-benchmarks")] -use crate::service::Block; -use crate::{ - chain_spec::{self, GenericChainSpec}, - cli::{Cli, RelayChainCli, Subcommand}, - common::NodeExtraArgs, - fake_runtime_api::{ - asset_hub_polkadot_aura::RuntimeApi as AssetHubPolkadotRuntimeApi, - aura::RuntimeApi as AuraRuntimeApi, - }, - service::{new_aura_node_spec, DynNodeSpec, ShellNode}, -}; -#[cfg(feature = "runtime-benchmarks")] -use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; -use cumulus_primitives_core::ParaId; -use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; -use log::info; -use parachains_common::{AssetHubPolkadotAuraId, AuraId}; -use sc_cli::{ - ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, - NetworkParams, Result, SharedParams, SubstrateCli, -}; -use sc_service::config::{BasePath, PrometheusConfig}; -use sp_runtime::traits::AccountIdConversion; -#[cfg(feature = "runtime-benchmarks")] -use sp_runtime::traits::HashingFor; -use std::{net::SocketAddr, path::PathBuf}; - -/// The choice of consensus for the parachain omni-node. -#[derive(PartialEq, Eq, Debug, Default)] -pub enum Consensus { - /// Aura consensus. - #[default] - Aura, - /// Use the relay chain consensus. - // TODO: atm this is just a demonstration, not really reach-able. We can add it to the CLI, - // env, or the chain spec. Or, just don't, and when we properly refactor this mess we will - // re-introduce it. - #[allow(unused)] - Relay, -} - -/// Helper enum that is used for better distinction of different parachain/runtime configuration -/// (it is based/calculated on ChainSpec's ID attribute) -#[derive(Debug, PartialEq)] -enum Runtime { - /// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be - /// an omni-node, and simply run a node with the given consensus algorithm. - Omni(Consensus), - Shell, - Seedling, - AssetHubPolkadot, - AssetHub, - Penpal(ParaId), - ContractsRococo, - Collectives, - Glutton, - BridgeHub(chain_spec::bridge_hubs::BridgeHubRuntimeType), - Coretime(chain_spec::coretime::CoretimeRuntimeType), - People(chain_spec::people::PeopleRuntimeType), -} - -trait RuntimeResolver { - fn runtime(&self) -> Result; -} - -impl RuntimeResolver for dyn ChainSpec { - fn runtime(&self) -> Result { - Ok(runtime(self.id())) - } -} - -/// Implementation, that can resolve [`Runtime`] from any json configuration file -impl RuntimeResolver for PathBuf { - fn runtime(&self) -> Result { - #[derive(Debug, serde::Deserialize)] - struct EmptyChainSpecWithId { - id: String, - } - - let file = std::fs::File::open(self)?; - let reader = std::io::BufReader::new(file); - let chain_spec: EmptyChainSpecWithId = - serde_json::from_reader(reader).map_err(|e| sc_cli::Error::Application(Box::new(e)))?; - - Ok(runtime(&chain_spec.id)) - } -} - -fn runtime(id: &str) -> Runtime { - let id = id.replace('_', "-"); - let (_, id, para_id) = extract_parachain_id(&id); - - if id.starts_with("shell") { - Runtime::Shell - } else if id.starts_with("seedling") { - Runtime::Seedling - } else if id.starts_with("asset-hub-polkadot") | id.starts_with("statemint") { - Runtime::AssetHubPolkadot - } else if id.starts_with("asset-hub-kusama") | - id.starts_with("statemine") | - id.starts_with("asset-hub-rococo") | - id.starts_with("rockmine") | - id.starts_with("asset-hub-westend") | - id.starts_with("westmint") - { - Runtime::AssetHub - } else if id.starts_with("penpal") { - Runtime::Penpal(para_id.unwrap_or(ParaId::new(0))) - } else if id.starts_with("contracts-rococo") { - Runtime::ContractsRococo - } else if id.starts_with("collectives-polkadot") || id.starts_with("collectives-westend") { - Runtime::Collectives - } else if id.starts_with(chain_spec::bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) { - Runtime::BridgeHub( - id.parse::() - .expect("Invalid value"), - ) - } else if id.starts_with(chain_spec::coretime::CoretimeRuntimeType::ID_PREFIX) { - Runtime::Coretime( - id.parse::().expect("Invalid value"), - ) - } else if id.starts_with("glutton") { - Runtime::Glutton - } else if id.starts_with(chain_spec::people::PeopleRuntimeType::ID_PREFIX) { - Runtime::People(id.parse::().expect("Invalid value")) - } else { - log::warn!( - "No specific runtime was recognized for ChainSpec's id: '{}', \ - so Runtime::Omni(Consensus::Aura) will be used", - id - ); - Runtime::Omni(Consensus::Aura) - } -} - -fn load_spec(id: &str) -> std::result::Result, String> { - let (id, _, para_id) = extract_parachain_id(id); - Ok(match id { - // - Default-like - "staging" => - Box::new(chain_spec::rococo_parachain::staging_rococo_parachain_local_config()), - "tick" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/tick.json")[..], - )?), - "trick" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/trick.json")[..], - )?), - "track" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/track.json")[..], - )?), - - // -- Starters - "shell" => Box::new(chain_spec::shell::get_shell_chain_spec()), - "seedling" => Box::new(chain_spec::seedling::get_seedling_chain_spec()), - - // -- Asset Hub Polkadot - "asset-hub-polkadot" | "statemint" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-polkadot.json")[..], - )?), - - // -- Asset Hub Kusama - "asset-hub-kusama" | "statemine" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-kusama.json")[..], - )?), - - // -- Asset Hub Rococo - "asset-hub-rococo-dev" => - Box::new(chain_spec::asset_hubs::asset_hub_rococo_development_config()), - "asset-hub-rococo-local" => - Box::new(chain_spec::asset_hubs::asset_hub_rococo_local_config()), - // the chain spec as used for generating the upgrade genesis values - "asset-hub-rococo-genesis" => - Box::new(chain_spec::asset_hubs::asset_hub_rococo_genesis_config()), - "asset-hub-rococo" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-rococo.json")[..], - )?), - - // -- Asset Hub Westend - "asset-hub-westend-dev" | "westmint-dev" => - Box::new(chain_spec::asset_hubs::asset_hub_westend_development_config()), - "asset-hub-westend-local" | "westmint-local" => - Box::new(chain_spec::asset_hubs::asset_hub_westend_local_config()), - // the chain spec as used for generating the upgrade genesis values - "asset-hub-westend-genesis" | "westmint-genesis" => - Box::new(chain_spec::asset_hubs::asset_hub_westend_config()), - // the shell-based chain spec as used for syncing - "asset-hub-westend" | "westmint" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/asset-hub-westend.json")[..], - )?), - - // -- Polkadot Collectives - "collectives-polkadot" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/collectives-polkadot.json")[..], - )?), - - // -- Westend Collectives - "collectives-westend-dev" => - Box::new(chain_spec::collectives::collectives_westend_development_config()), - "collectives-westend-local" => - Box::new(chain_spec::collectives::collectives_westend_local_config()), - "collectives-westend" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/collectives-westend.json")[..], - )?), - - // -- Contracts on Rococo - "contracts-rococo-dev" => - Box::new(chain_spec::contracts::contracts_rococo_development_config()), - "contracts-rococo-local" => - Box::new(chain_spec::contracts::contracts_rococo_local_config()), - "contracts-rococo-genesis" => Box::new(chain_spec::contracts::contracts_rococo_config()), - "contracts-rococo" => Box::new(GenericChainSpec::from_json_bytes( - &include_bytes!("../chain-specs/contracts-rococo.json")[..], - )?), - - // -- BridgeHub - bridge_like_id - if bridge_like_id - .starts_with(chain_spec::bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) => - bridge_like_id - .parse::() - .expect("invalid value") - .load_config()?, - - // -- Coretime - coretime_like_id - if coretime_like_id - .starts_with(chain_spec::coretime::CoretimeRuntimeType::ID_PREFIX) => - coretime_like_id - .parse::() - .expect("invalid value") - .load_config()?, - - // -- Penpal - "penpal-rococo" => Box::new(chain_spec::penpal::get_penpal_chain_spec( - para_id.expect("Must specify parachain id"), - "rococo-local", - )), - "penpal-westend" => Box::new(chain_spec::penpal::get_penpal_chain_spec( - para_id.expect("Must specify parachain id"), - "westend-local", - )), - - // -- Glutton Westend - "glutton-westend-dev" => Box::new(chain_spec::glutton::glutton_westend_development_config( - para_id.expect("Must specify parachain id"), - )), - "glutton-westend-local" => Box::new(chain_spec::glutton::glutton_westend_local_config( - para_id.expect("Must specify parachain id"), - )), - // the chain spec as used for generating the upgrade genesis values - "glutton-westend-genesis" => Box::new(chain_spec::glutton::glutton_westend_config( - para_id.expect("Must specify parachain id"), - )), - - // -- People - people_like_id - if people_like_id.starts_with(chain_spec::people::PeopleRuntimeType::ID_PREFIX) => - people_like_id - .parse::() - .expect("invalid value") - .load_config()?, - - // -- Fallback (generic chainspec) - "" => { - log::warn!("No ChainSpec.id specified, so using default one, based on rococo-parachain runtime"); - Box::new(chain_spec::rococo_parachain::rococo_parachain_local_config()) - }, - - // -- Loading a specific spec from disk - path => Box::new(GenericChainSpec::from_json_file(path.into())?), - }) -} - -/// Extracts the normalized chain id and parachain id from the input chain id. -/// (H/T to Phala for the idea) -/// E.g. "penpal-kusama-2004" yields ("penpal-kusama", Some(2004)) -fn extract_parachain_id(id: &str) -> (&str, &str, Option) { - let para_prefixes = [ - // Penpal - "penpal-rococo-", - "penpal-westend-", - "penpal-kusama-", - "penpal-polkadot-", - // Glutton Kusama - "glutton-kusama-dev-", - "glutton-kusama-local-", - "glutton-kusama-genesis-", - // Glutton Westend - "glutton-westend-dev-", - "glutton-westend-local-", - "glutton-westend-genesis-", - ]; - - for para_prefix in para_prefixes { - if let Some(suffix) = id.strip_prefix(para_prefix) { - let para_id: u32 = suffix.parse().expect("Invalid parachain-id suffix"); - return (&id[..para_prefix.len() - 1], id, Some(para_id.into())) - } - } - - (id, id, None) -} - -impl SubstrateCli for Cli { - fn impl_name() -> String { - Self::executable_name() - } - - fn impl_version() -> String { - env!("SUBSTRATE_CLI_IMPL_VERSION").into() - } - - fn description() -> String { - format!( - "The command-line arguments provided first will be passed to the parachain node, \n\ - and the arguments provided after -- will be passed to the relay chain node. \n\ - \n\ - Example: \n\ - \n\ - {} [parachain-args] -- [relay-chain-args]", - Self::executable_name() - ) - } - - fn author() -> String { - env!("CARGO_PKG_AUTHORS").into() - } - - fn support_url() -> String { - "https://github.com/paritytech/polkadot-sdk/issues/new".into() - } - - fn copyright_start_year() -> i32 { - 2017 - } - - fn load_spec(&self, id: &str) -> std::result::Result, String> { - load_spec(id) - } -} - -impl SubstrateCli for RelayChainCli { - fn impl_name() -> String { - Cli::impl_name() - } - - fn impl_version() -> String { - Cli::impl_version() - } - - fn description() -> String { - Cli::description() - } - - fn author() -> String { - Cli::author() - } - - fn support_url() -> String { - Cli::support_url() - } - - fn copyright_start_year() -> i32 { - Cli::copyright_start_year() - } - - fn load_spec(&self, id: &str) -> std::result::Result, String> { - polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) - } -} - -fn new_node_spec( - config: &sc_service::Configuration, - extra_args: &NodeExtraArgs, -) -> std::result::Result, sc_cli::Error> { - Ok(match config.chain_spec.runtime()? { - Runtime::AssetHubPolkadot => - new_aura_node_spec::(extra_args), - Runtime::AssetHub | - Runtime::BridgeHub(_) | - Runtime::Collectives | - Runtime::Coretime(_) | - Runtime::People(_) | - Runtime::ContractsRococo | - Runtime::Glutton | - Runtime::Penpal(_) => new_aura_node_spec::(extra_args), - Runtime::Shell | Runtime::Seedling => Box::new(ShellNode), - Runtime::Omni(consensus) => match consensus { - Consensus::Aura => new_aura_node_spec::(extra_args), - Consensus::Relay => Box::new(ShellNode), - }, - }) -} - -/// Parse command line arguments into service configuration. -pub fn run() -> Result<()> { - let cli = Cli::from_args(); - - match &cli.subcommand { - Some(Subcommand::BuildSpec(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) - }, - Some(Subcommand::CheckBlock(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_check_block_cmd(config, cmd) - }) - }, - Some(Subcommand::ExportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_export_blocks_cmd(config, cmd) - }) - }, - Some(Subcommand::ExportState(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_export_state_cmd(config, cmd) - }) - }, - Some(Subcommand::ImportBlocks(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_import_blocks_cmd(config, cmd) - }) - }, - Some(Subcommand::Revert(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.prepare_revert_cmd(config, cmd) - }) - }, - Some(Subcommand::PurgeChain(cmd)) => { - let runner = cli.create_runner(cmd)?; - let polkadot_cli = RelayChainCli::new(runner.config(), cli.relay_chain_args.iter()); - - runner.sync_run(|config| { - let polkadot_config = SubstrateCli::create_configuration( - &polkadot_cli, - &polkadot_cli, - config.tokio_handle.clone(), - ) - .map_err(|err| format!("Relay chain argument error: {}", err))?; - - cmd.run(config, polkadot_config) - }) - }, - Some(Subcommand::ExportGenesisHead(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.run_export_genesis_head_cmd(config, cmd) - }) - }, - Some(Subcommand::ExportGenesisWasm(cmd)) => { - let runner = cli.create_runner(cmd)?; - runner.sync_run(|_config| { - let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?; - cmd.run(&*spec) - }) - }, - Some(Subcommand::Benchmark(cmd)) => { - let runner = cli.create_runner(cmd)?; - - // Switch on the concrete benchmark sub-command- - match cmd { - #[cfg(feature = "runtime-benchmarks")] - BenchmarkCmd::Pallet(cmd) => runner.sync_run(|config| { - cmd.run_with_spec::, ReclaimHostFunctions>(Some( - config.chain_spec, - )) - }), - BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.run_benchmark_block_cmd(config, cmd) - }), - #[cfg(feature = "runtime-benchmarks")] - BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { - let node = new_node_spec(&config, &cli.node_extra_args())?; - node.run_benchmark_storage_cmd(config, cmd) - }), - BenchmarkCmd::Machine(cmd) => - runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())), - #[allow(unreachable_patterns)] - _ => Err("Benchmarking sub-command unsupported or compilation feature missing. \ - Make sure to compile with --features=runtime-benchmarks \ - to enable all supported benchmarks." - .into()), - } - }, - Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?), - None => { - let runner = cli.create_runner(&cli.run.normalize())?; - let polkadot_cli = RelayChainCli::new(runner.config(), cli.relay_chain_args.iter()); - let collator_options = cli.run.collator_options(); - - runner.run_node_until_exit(|config| async move { - // If Statemint (Statemine, Westmint, Rockmine) DB exists and we're using the - // asset-hub chain spec, then rename the base path to the new chain ID. In the case - // that both file paths exist, the node will exit, as the user must decide (by - // deleting one path) the information that they want to use as their DB. - let old_name = match config.chain_spec.id() { - "asset-hub-polkadot" => Some("statemint"), - "asset-hub-kusama" => Some("statemine"), - "asset-hub-westend" => Some("westmint"), - "asset-hub-rococo" => Some("rockmine"), - _ => None, - }; - - if let Some(old_name) = old_name { - let new_path = config.base_path.config_dir(config.chain_spec.id()); - let old_path = config.base_path.config_dir(old_name); - - if old_path.exists() && new_path.exists() { - return Err(format!( - "Found legacy {} path {} and new Asset Hub path {}. \ - Delete one path such that only one exists.", - old_name, - old_path.display(), - new_path.display() - ) - .into()) - } - - if old_path.exists() { - std::fs::rename(old_path.clone(), new_path.clone())?; - info!( - "{} was renamed to Asset Hub. The filepath with associated data on disk \ - has been renamed from {} to {}.", - old_name, - old_path.display(), - new_path.display() - ); - } - } - - let hwbench = (!cli.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) - })) - .flatten(); - - let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) - .map(|e| e.para_id) - .ok_or("Could not find parachain extension in chain-spec.")?; - - let id = ParaId::from(para_id); - - let parachain_account = - AccountIdConversion::::into_account_truncating( - &id, - ); - - let tokio_handle = config.tokio_handle.clone(); - let polkadot_config = - SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) - .map_err(|err| format!("Relay chain argument error: {}", err))?; - - info!("🪪 Parachain id: {:?}", id); - info!("🧾 Parachain Account: {}", parachain_account); - info!("✍️ Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); - - start_node( - config, - polkadot_config, - collator_options, - id, - cli.node_extra_args(), - hwbench, - ) - .await - }) - }, - } -} - -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node( - config: sc_service::Configuration, - polkadot_config: sc_service::Configuration, - collator_options: cumulus_client_cli::CollatorOptions, - id: ParaId, - extra_args: NodeExtraArgs, - hwbench: Option, -) -> Result { - let node_spec = new_node_spec(&config, &extra_args)?; - node_spec - .start_node(config, polkadot_config, collator_options, id, hwbench, extra_args) - .await - .map_err(Into::into) -} - -impl DefaultConfigurationValues for RelayChainCli { - fn p2p_listen_port() -> u16 { - 30334 - } - - fn rpc_listen_port() -> u16 { - 9945 - } - - fn prometheus_listen_port() -> u16 { - 9616 - } -} - -impl CliConfiguration for RelayChainCli { - fn shared_params(&self) -> &SharedParams { - self.base.base.shared_params() - } - - fn import_params(&self) -> Option<&ImportParams> { - self.base.base.import_params() - } - - fn network_params(&self) -> Option<&NetworkParams> { - self.base.base.network_params() - } - - fn keystore_params(&self) -> Option<&KeystoreParams> { - self.base.base.keystore_params() - } - - fn base_path(&self) -> Result> { - Ok(self - .shared_params() - .base_path()? - .or_else(|| self.base_path.clone().map(Into::into))) - } - - fn rpc_addr(&self, default_listen_port: u16) -> Result> { - self.base.base.rpc_addr(default_listen_port) - } - - fn prometheus_config( - &self, - default_listen_port: u16, - chain_spec: &Box, - ) -> Result> { - self.base.base.prometheus_config(default_listen_port, chain_spec) - } - - fn init( - &self, - _support_url: &String, - _impl_version: &String, - _logger_hook: F, - _config: &sc_service::Configuration, - ) -> Result<()> - where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), - { - unreachable!("PolkadotCli is never initialized; qed"); - } - - fn chain_id(&self, is_dev: bool) -> Result { - let chain_id = self.base.base.chain_id(is_dev)?; - - Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) - } - - fn role(&self, is_dev: bool) -> Result { - self.base.base.role(is_dev) - } - - fn transaction_pool(&self, is_dev: bool) -> Result { - self.base.base.transaction_pool(is_dev) - } - - fn trie_cache_maximum_size(&self) -> Result> { - self.base.base.trie_cache_maximum_size() - } - - fn rpc_methods(&self) -> Result { - self.base.base.rpc_methods() - } - - fn rpc_max_connections(&self) -> Result { - self.base.base.rpc_max_connections() - } - - fn rpc_cors(&self, is_dev: bool) -> Result>> { - self.base.base.rpc_cors(is_dev) - } - - fn default_heap_pages(&self) -> Result> { - self.base.base.default_heap_pages() - } - - fn force_authoring(&self) -> Result { - self.base.base.force_authoring() - } - - fn disable_grandpa(&self) -> Result { - self.base.base.disable_grandpa() - } - - fn max_runtime_instances(&self) -> Result> { - self.base.base.max_runtime_instances() - } - - fn announce_block(&self) -> Result { - self.base.base.announce_block() - } - - fn telemetry_endpoints( - &self, - chain_spec: &Box, - ) -> Result> { - self.base.base.telemetry_endpoints(chain_spec) - } - - fn node_name(&self) -> Result { - self.base.base.node_name() - } -} - -#[cfg(test)] -mod tests { - use crate::{ - chain_spec::{get_account_id_from_seed, get_from_seed}, - command::{Consensus, Runtime, RuntimeResolver}, - }; - use sc_chain_spec::{ChainSpec, ChainSpecExtension, ChainSpecGroup, ChainType, Extension}; - use serde::{Deserialize, Serialize}; - use sp_core::sr25519; - use std::path::PathBuf; - use tempfile::TempDir; - - #[derive( - Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, - )] - #[serde(deny_unknown_fields)] - pub struct Extensions1 { - pub attribute1: String, - pub attribute2: u32, - } - - #[derive( - Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, Default, - )] - #[serde(deny_unknown_fields)] - pub struct Extensions2 { - pub attribute_x: String, - pub attribute_y: String, - pub attribute_z: u32, - } - - fn store_configuration(dir: &TempDir, spec: &dyn ChainSpec) -> PathBuf { - let raw_output = true; - let json = sc_service::chain_ops::build_spec(spec, raw_output) - .expect("Failed to build json string"); - let mut cfg_file_path = dir.path().to_path_buf(); - cfg_file_path.push(spec.id()); - cfg_file_path.set_extension("json"); - std::fs::write(&cfg_file_path, json).expect("Failed to write to json file"); - cfg_file_path - } - - pub type DummyChainSpec = sc_service::GenericChainSpec; - - pub fn create_default_with_extensions( - id: &str, - extension: E, - ) -> DummyChainSpec { - DummyChainSpec::builder( - rococo_parachain_runtime::WASM_BINARY - .expect("WASM binary was not built, please build it!"), - extension, - ) - .with_name("Dummy local testnet") - .with_id(id) - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(crate::chain_spec::rococo_parachain::testnet_genesis( - get_account_id_from_seed::("Alice"), - vec![ - get_from_seed::("Alice"), - get_from_seed::("Bob"), - ], - vec![get_account_id_from_seed::("Alice")], - 1000.into(), - )) - .build() - } - - #[test] - fn test_resolve_runtime_for_different_configuration_files() { - let temp_dir = tempfile::tempdir().expect("Failed to access tempdir"); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("shell-1", Extensions1::default()), - ); - assert_eq!(Runtime::Shell, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("shell-2", Extensions2::default()), - ); - assert_eq!(Runtime::Shell, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("seedling", Extensions2::default()), - ); - assert_eq!(Runtime::Seedling, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("penpal-rococo-1000", Extensions2::default()), - ); - assert_eq!(Runtime::Penpal(1000.into()), path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &create_default_with_extensions("penpal-polkadot-2000", Extensions2::default()), - ); - assert_eq!(Runtime::Penpal(2000.into()), path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &crate::chain_spec::contracts::contracts_rococo_local_config(), - ); - assert_eq!(Runtime::ContractsRococo, path.runtime().unwrap()); - - let path = store_configuration( - &temp_dir, - &crate::chain_spec::rococo_parachain::rococo_parachain_local_config(), - ); - assert_eq!(Runtime::Omni(Consensus::Aura), path.runtime().unwrap()); - } -} diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs deleted file mode 100644 index 7d54e9b4be04304f612916efc292b6a2b2fd6ed7..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! These are used to provide a type that implements these runtime APIs without requiring to import -//! the native runtimes. - -use frame_support::weights::Weight; -use parachains_common::{AccountId, AssetHubPolkadotAuraId, Balance, Nonce}; -use polkadot_primitives::Block; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_runtime::{ - traits::Block as BlockT, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, -}; - -pub struct Runtime; - -sp_api::impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> sp_version::RuntimeVersion { - unimplemented!() - } - - fn execute_block(_: Block) { - unimplemented!() - } - - fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - unimplemented!() - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } - - fn metadata_at_version(_: u32) -> Option { - unimplemented!() - } - - fn metadata_versions() -> Vec { - unimplemented!() - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - unimplemented!() - } - - fn authorities() -> Vec { - unimplemented!() - } - } - - impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { - fn can_build_upon( - _: ::Hash, - _: cumulus_primitives_aura::Slot, - ) -> bool { - unimplemented!() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { - unimplemented!() - } - - fn finalize_block() -> ::Header { - unimplemented!() - } - - fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { - unimplemented!() - } - - fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { - unimplemented!() - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - _: TransactionSource, - _: ::Extrinsic, - _: ::Hash, - ) -> TransactionValidity { - unimplemented!() - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - unimplemented!() - } - - fn decode_session_keys( - _: Vec, - ) -> Option, KeyTypeId)>> { - unimplemented!() - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { - fn query_info( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { - unimplemented!() - } - fn query_fee_details( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment::FeeDetails { - unimplemented!() - } - fn query_weight_to_fee(_: Weight) -> Balance { - unimplemented!() - } - fn query_length_to_fee(_: u32) -> Balance { - unimplemented!() - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(_: &::Header) -> cumulus_primitives_core::CollationInfo { - unimplemented!() - } - } - - #[cfg(feature = "try-runtime")] - impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade(_: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { - unimplemented!() - } - - fn execute_block( - _: Block, - _: bool, - _: bool, - _: frame_try_runtime::TryStateSelect, - ) -> Weight { - unimplemented!() - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(_: AccountId) -> Nonce { - unimplemented!() - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark for Runtime { - fn benchmark_metadata(_: bool) -> ( - Vec, - Vec, - ) { - unimplemented!() - } - - fn dispatch_benchmark( - _: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { - unimplemented!() - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(_: Vec) -> sp_genesis_builder::Result { - unimplemented!() - } - - fn get_preset(_id: &Option) -> Option> { - unimplemented!() - } - - fn preset_names() -> Vec { - unimplemented!() - } - } -} diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs deleted file mode 100644 index ca5fc8bdf119b586d1d34f39122af2b6c9801831..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! These are used to provide a type that implements these runtime APIs without requiring to import -//! the native runtimes. - -use frame_support::weights::Weight; -use parachains_common::{AccountId, AuraId, Balance, Nonce}; -use polkadot_primitives::Block; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_runtime::{ - traits::Block as BlockT, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, -}; - -pub struct Runtime; - -sp_api::impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> sp_version::RuntimeVersion { - unimplemented!() - } - - fn execute_block(_: Block) { - unimplemented!() - } - - fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - unimplemented!() - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } - - fn metadata_at_version(_: u32) -> Option { - unimplemented!() - } - - fn metadata_versions() -> Vec { - unimplemented!() - } - } - - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - unimplemented!() - } - - fn authorities() -> Vec { - unimplemented!() - } - } - - impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { - fn can_build_upon( - _: ::Hash, - _: cumulus_primitives_aura::Slot, - ) -> bool { - unimplemented!() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { - unimplemented!() - } - - fn finalize_block() -> ::Header { - unimplemented!() - } - - fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { - unimplemented!() - } - - fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { - unimplemented!() - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - _: TransactionSource, - _: ::Extrinsic, - _: ::Hash, - ) -> TransactionValidity { - unimplemented!() - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - unimplemented!() - } - - fn decode_session_keys( - _: Vec, - ) -> Option, KeyTypeId)>> { - unimplemented!() - } - } - - impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { - fn query_info( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { - unimplemented!() - } - fn query_fee_details( - _: ::Extrinsic, - _: u32, - ) -> pallet_transaction_payment::FeeDetails { - unimplemented!() - } - fn query_weight_to_fee(_: Weight) -> Balance { - unimplemented!() - } - fn query_length_to_fee(_: u32) -> Balance { - unimplemented!() - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(_: &::Header) -> cumulus_primitives_core::CollationInfo { - unimplemented!() - } - } - - #[cfg(feature = "try-runtime")] - impl frame_try_runtime::TryRuntime for Runtime { - fn on_runtime_upgrade(_: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { - unimplemented!() - } - - fn execute_block( - _: Block, - _: bool, - _: bool, - _: frame_try_runtime::TryStateSelect, - ) -> Weight { - unimplemented!() - } - } - - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { - fn account_nonce(_: AccountId) -> Nonce { - unimplemented!() - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark for Runtime { - fn benchmark_metadata(_: bool) -> ( - Vec, - Vec, - ) { - unimplemented!() - } - - fn dispatch_benchmark( - _: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { - unimplemented!() - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(_: Vec) -> sp_genesis_builder::Result { - unimplemented!() - } - - fn get_preset(_id: &Option) -> Option> { - unimplemented!() - } - - fn preset_names() -> Vec { - unimplemented!() - } - } -} diff --git a/cumulus/polkadot-parachain/src/main.rs b/cumulus/polkadot-parachain/src/main.rs index 84e41fc347d9fadb1431b20baa0e2cf95eea6b90..f2dce552c51a1b5ed95e31f516f25cf978adc67e 100644 --- a/cumulus/polkadot-parachain/src/main.rs +++ b/cumulus/polkadot-parachain/src/main.rs @@ -19,38 +19,36 @@ #![warn(missing_docs)] #![warn(unused_extern_crates)] -pub(crate) fn examples(executable_name: String) -> String { - color_print::cformat!( - r#"Examples: +mod chain_spec; - {0} --chain para.json --sync warp -- --chain relay.json --sync warp - Launch a warp-syncing full node of a given para's chain-spec, and a given relay's chain-spec. +use polkadot_parachain_lib::{run, CliConfig as CliConfigT, RunConfig}; - The above approach is the most flexible, and the most forward-compatible way to spawn an omni-node. +struct CliConfig; - You can find the chain-spec of some networks in: - https://paritytech.github.io/chainspecs +impl CliConfigT for CliConfig { + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } - {0} --chain asset-hub-polkadot --sync warp -- --chain polkadot --sync warp - Launch a warp-syncing full node of the Asset Hub parachain on the Polkadot Relay Chain. + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } - {0} --chain asset-hub-kusama --sync warp --relay-chain-rpc-url ws://rpc.example.com -- --chain kusama - Launch a warp-syncing full node of the Asset Hub parachain on the Kusama Relay Chain. - Uses ws://rpc.example.com as remote relay chain node. - "#, - executable_name, - ) -} + fn support_url() -> String { + "https://github.com/paritytech/polkadot-sdk/issues/new".into() + } -mod chain_spec; -mod cli; -mod command; -mod common; -mod fake_runtime_api; -mod rpc; -mod service; + fn copyright_start_year() -> u16 { + 2017 + } +} fn main() -> color_eyre::eyre::Result<()> { color_eyre::install()?; - Ok(command::run()?) + + let config = RunConfig { + chain_spec_loader: Box::new(chain_spec::ChainSpecLoader), + runtime_resolver: Box::new(chain_spec::RuntimeResolver), + }; + Ok(run::(config)?) } diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/src/service.rs deleted file mode 100644 index 80698a2d71154bf08091226a446329afc9bcef1a..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/service.rs +++ /dev/null @@ -1,1040 +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_client_cli::{CollatorOptions, ExportGenesisHeadCommand}; -use cumulus_client_collator::service::{ - CollatorService, ServiceInterface as CollatorServiceInterface, -}; -use cumulus_client_consensus_aura::collators::lookahead::{self as aura, Params as AuraParams}; -#[docify::export(slot_based_colator_import)] -use cumulus_client_consensus_aura::collators::slot_based::{ - self as slot_based, Params as SlotBasedParams, -}; -use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; -use cumulus_client_consensus_proposer::{Proposer, ProposerInterface}; -use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier; -#[allow(deprecated)] -use cumulus_client_service::old_consensus; -use cumulus_client_service::{ - build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks, - BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams, -}; -use cumulus_primitives_core::{relay_chain::ValidationCode, ParaId}; -use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; - -use crate::{ - common::{ - aura::{AuraIdT, AuraRuntimeApi}, - ConstructNodeRuntimeApi, NodeExtraArgs, - }, - fake_runtime_api::aura::RuntimeApi as FakeRuntimeApi, - rpc::BuildRpcExtensions, -}; -pub use parachains_common::{AccountId, Balance, Block, Hash, Nonce}; - -use crate::rpc::{BuildEmptyRpcExtensions, BuildParachainRpcExtensions}; -use frame_benchmarking_cli::BlockCmd; -#[cfg(any(feature = "runtime-benchmarks"))] -use frame_benchmarking_cli::StorageCmd; -use futures::prelude::*; -use polkadot_primitives::CollatorPair; -use prometheus_endpoint::Registry; -use sc_cli::{CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd}; -use sc_client_api::BlockchainEvents; -use sc_consensus::{ - import_queue::{BasicQueue, Verifier as VerifierT}, - BlockImportParams, DefaultImportQueue, ImportQueue, -}; -use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; -use sc_network::{config::FullNetworkConfiguration, service::traits::NetworkBackend, NetworkBlock}; -use sc_service::{Configuration, Error, PartialComponents, TFullBackend, TFullClient, TaskManager}; -use sc_sysinfo::HwBench; -use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; -use sc_transaction_pool::FullPool; -use sp_api::ProvideRuntimeApi; -use sp_inherents::CreateInherentDataProviders; -use sp_keystore::KeystorePtr; -use sp_runtime::{app_crypto::AppCrypto, traits::Header as HeaderT}; -use std::{marker::PhantomData, pin::Pin, sync::Arc, time::Duration}; - -#[cfg(not(feature = "runtime-benchmarks"))] -type HostFunctions = cumulus_client_service::ParachainHostFunctions; - -#[cfg(feature = "runtime-benchmarks")] -type HostFunctions = ( - cumulus_client_service::ParachainHostFunctions, - frame_benchmarking::benchmarking::HostFunctions, -); - -pub type ParachainClient = TFullClient>; - -pub type ParachainBackend = TFullBackend; - -type ParachainBlockImport = - TParachainBlockImport>, ParachainBackend>; - -/// Assembly of PartialComponents (enough to run chain ops subcommands) -pub type Service = PartialComponents< - ParachainClient, - ParachainBackend, - (), - sc_consensus::DefaultImportQueue, - sc_transaction_pool::FullPool>, - (ParachainBlockImport, Option, Option), ->; - -pub(crate) trait BuildImportQueue { - fn build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry_handle: Option, - task_manager: &TaskManager, - ) -> sc_service::error::Result>; -} - -pub(crate) trait StartConsensus -where - RuntimeApi: ConstructNodeRuntimeApi>, -{ - fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc>>, - keystore: KeystorePtr, - relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, - backend: Arc, - node_extra_args: NodeExtraArgs, - ) -> Result<(), sc_service::Error>; -} - -pub(crate) trait NodeSpec { - type RuntimeApi: ConstructNodeRuntimeApi>; - - type BuildImportQueue: BuildImportQueue + 'static; - - type BuildRpcExtensions: BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > + 'static; - - type StartConsensus: StartConsensus + 'static; - - const SYBIL_RESISTANCE: CollatorSybilResistance; - - /// Starts a `ServiceBuilder` for a full service. - /// - /// Use this macro if you don't actually need the full service, but just the builder in order to - /// be able to perform chain operations. - fn new_partial(config: &Configuration) -> sc_service::error::Result> { - let telemetry = config - .telemetry_endpoints - .clone() - .filter(|x| !x.is_empty()) - .map(|endpoints| -> Result<_, sc_telemetry::Error> { - let worker = TelemetryWorker::new(16)?; - let telemetry = worker.handle().new_telemetry(endpoints); - Ok((worker, telemetry)) - }) - .transpose()?; - - let heap_pages = config.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { - HeapAllocStrategy::Static { extra_pages: h as _ } - }); - - let executor = sc_executor::WasmExecutor::::builder() - .with_execution_method(config.wasm_method) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) - .with_onchain_heap_alloc_strategy(heap_pages) - .with_offchain_heap_alloc_strategy(heap_pages) - .build(); - - let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts_record_import::( - config, - telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, - true, - )?; - let client = Arc::new(client); - - let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); - - let telemetry = telemetry.map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", None, worker.run()); - telemetry - }); - - let transaction_pool = sc_transaction_pool::BasicPool::new_full( - config.transaction_pool.clone(), - config.role.is_authority().into(), - config.prometheus_registry(), - task_manager.spawn_essential_handle(), - client.clone(), - ); - - let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); - - let import_queue = Self::BuildImportQueue::build_import_queue( - client.clone(), - block_import.clone(), - config, - telemetry.as_ref().map(|telemetry| telemetry.handle()), - &task_manager, - )?; - - Ok(PartialComponents { - backend, - client, - import_queue, - keystore_container, - task_manager, - transaction_pool, - select_chain: (), - other: (block_import, telemetry, telemetry_worker_handle), - }) - } - - /// Start a node with the given parachain spec. - /// - /// This is the actual implementation that is abstract over the executor and the runtime api. - fn start_node( - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, - node_extra_args: NodeExtraArgs, - ) -> Pin>>> - where - Net: NetworkBackend, - { - Box::pin(async move { - let parachain_config = prepare_node_config(parachain_config); - - let params = Self::new_partial(¶chain_config)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; - - let client = params.client.clone(); - let backend = params.backend.clone(); - - let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( - polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), - ) - .await - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - let validator = parachain_config.role.is_authority(); - let prometheus_registry = parachain_config.prometheus_registry().cloned(); - let transaction_pool = params.transaction_pool.clone(); - let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::<_, _, Net>::new( - ¶chain_config.network, - prometheus_registry.clone(), - ); - - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = - build_network(BuildNetworkParams { - parachain_config: ¶chain_config, - net_config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - para_id, - spawn_handle: task_manager.spawn_handle(), - relay_chain_interface: relay_chain_interface.clone(), - import_queue: params.import_queue, - sybil_resistance_level: Self::SYBIL_RESISTANCE, - }) - .await?; - - let rpc_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); - let backend_for_rpc = backend.clone(); - - Box::new(move |deny_unsafe, _| { - Self::BuildRpcExtensions::build_rpc_extensions( - deny_unsafe, - client.clone(), - backend_for_rpc.clone(), - transaction_pool.clone(), - ) - }) - }; - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - rpc_builder, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - config: parachain_config, - keystore: params.keystore_container.keystore(), - backend: backend.clone(), - network: network.clone(), - sync_service: sync_service.clone(), - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - if let Some(hwbench) = hwbench { - sc_sysinfo::print_hwbench(&hwbench); - if validator { - warn_if_slow_hardware(&hwbench); - } - - if let Some(ref mut telemetry) = telemetry { - let telemetry_handle = telemetry.handle(); - task_manager.spawn_handle().spawn( - "telemetry_hwbench", - None, - sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), - ); - } - } - - let announce_block = { - let sync_service = sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let relay_chain_slot_duration = Duration::from_secs(6); - - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), - para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service, - })?; - - if validator { - Self::StartConsensus::start_consensus( - client.clone(), - block_import, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|t| t.handle()), - &task_manager, - relay_chain_interface.clone(), - transaction_pool, - params.keystore_container.keystore(), - relay_chain_slot_duration, - para_id, - collator_key.expect("Command line arguments do not allow this. qed"), - overseer_handle, - announce_block, - backend.clone(), - node_extra_args, - )?; - } - - start_network.start_network(); - - Ok(task_manager) - }) - } -} - -/// Build the import queue for the shell runtime. -pub(crate) struct BuildShellImportQueue(PhantomData); - -impl BuildImportQueue for BuildShellImportQueue { - fn build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - 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 RuntimeApi = FakeRuntimeApi; - type BuildImportQueue = BuildShellImportQueue; - type BuildRpcExtensions = BuildEmptyRpcExtensions; - type StartConsensus = StartRelayChainConsensus; - - const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Unresistant; -} - -struct Verifier { - client: Arc, - aura_verifier: Box>, - relay_chain_verifier: Box>, - _phantom: PhantomData, -} - -#[async_trait::async_trait] -impl VerifierT for Verifier -where - Client: ProvideRuntimeApi + Send + Sync, - Client::Api: AuraRuntimeApi, - AuraId: AuraIdT + Sync, -{ - async fn verify( - &self, - block_import: BlockImportParams, - ) -> Result, String> { - if self.client.runtime_api().has_aura_api(*block_import.header.parent_hash()) { - self.aura_verifier.verify(block_import).await - } else { - self.relay_chain_verifier.verify(block_import).await - } - } -} - -/// Build the import queue for parachain runtimes that started with relay chain consensus and -/// switched to aura. -pub(crate) struct BuildRelayToAuraImportQueue( - PhantomData<(RuntimeApi, AuraId)>, -); - -impl BuildImportQueue - for BuildRelayToAuraImportQueue -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi, - AuraId: AuraIdT + Sync, -{ - fn build_import_queue( - client: Arc>, - block_import: ParachainBlockImport, - config: &Configuration, - telemetry_handle: Option, - task_manager: &TaskManager, - ) -> sc_service::error::Result> { - let verifier_client = client.clone(); - - let aura_verifier = - cumulus_client_consensus_aura::build_verifier::<::Pair, _, _, _>( - cumulus_client_consensus_aura::BuildVerifierParams { - client: verifier_client.clone(), - create_inherent_data_providers: move |parent_hash, _| { - let cidp_client = verifier_client.clone(); - async move { - let slot_duration = cumulus_client_consensus_aura::slot_duration_at( - &*cidp_client, - parent_hash, - )?; - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - - Ok((slot, timestamp)) - } - }, - telemetry: telemetry_handle, - }, - ); - - let relay_chain_verifier = - Box::new(RelayChainVerifier::new(client.clone(), |_, _| async { Ok(()) })); - - let verifier = Verifier { - client, - relay_chain_verifier, - aura_verifier: Box::new(aura_verifier), - _phantom: PhantomData, - }; - - let registry = config.prometheus_registry(); - let spawner = task_manager.spawn_essential_handle(); - - Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry)) - } -} - -/// Uses the lookahead collator to support async backing. -/// -/// Start an aura powered parachain node. Some system chains use this. -pub(crate) struct AuraNode( - pub PhantomData<(RuntimeApi, AuraId, StartConsensus)>, -); - -impl Default for AuraNode { - fn default() -> Self { - Self(Default::default()) - } -} - -impl NodeSpec for AuraNode -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + substrate_frame_rpc_system::AccountNonceApi, - AuraId: AuraIdT + Sync, - StartConsensus: self::StartConsensus + 'static, -{ - type RuntimeApi = RuntimeApi; - type BuildImportQueue = BuildRelayToAuraImportQueue; - type BuildRpcExtensions = BuildParachainRpcExtensions; - type StartConsensus = StartConsensus; - const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Resistant; -} - -pub fn new_aura_node_spec(extra_args: &NodeExtraArgs) -> Box -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + substrate_frame_rpc_system::AccountNonceApi, - AuraId: AuraIdT + Sync, -{ - if extra_args.use_slot_based_consensus { - Box::new(AuraNode::< - RuntimeApi, - AuraId, - StartSlotBasedAuraConsensus, - >::default()) - } else { - Box::new(AuraNode::< - RuntimeApi, - AuraId, - StartLookaheadAuraConsensus, - >::default()) - } -} - -/// 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 for StartRelayChainConsensus { - fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc>>, - _keystore: KeystorePtr, - _relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, - _backend: Arc, - _node_extra_args: NodeExtraArgs, - ) -> Result<(), 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<(RuntimeApi, AuraId)>, -); - -impl StartSlotBasedAuraConsensus -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi, - AuraId: AuraIdT + Sync, -{ - #[docify::export_content] - fn launch_slot_based_collator( - params: SlotBasedParams< - ParachainBlockImport, - CIDP, - ParachainClient, - ParachainBackend, - Arc, - CHP, - Proposer, - CS, - >, - task_manager: &TaskManager, - ) where - CIDP: CreateInherentDataProviders + 'static, - CIDP::InherentDataProviders: Send, - CHP: cumulus_client_consensus_common::ValidationCodeHashProvider + Send + 'static, - Proposer: ProposerInterface + Send + Sync + 'static, - CS: CollatorServiceInterface + Send + Sync + Clone + 'static, - { - let (collation_future, block_builder_future) = - slot_based::run::::Pair, _, _, _, _, _, _, _, _>(params); - - task_manager.spawn_essential_handle().spawn( - "collation-task", - Some("parachain-block-authoring"), - collation_future, - ); - task_manager.spawn_essential_handle().spawn( - "block-builder-task", - Some("parachain-block-authoring"), - block_builder_future, - ); - } -} - -impl StartConsensus - for StartSlotBasedAuraConsensus -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi, - AuraId: AuraIdT + Sync, -{ - fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc>>, - keystore: KeystorePtr, - relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - _overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, - backend: Arc, - _node_extra_args: NodeExtraArgs, - ) -> Result<(), Error> { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - - let proposer = Proposer::new(proposer_factory); - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let client_for_aura = client.clone(); - let params = SlotBasedParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend.clone(), - relay_client: relay_chain_interface, - code_hash_provider: move |block_hash| { - client_for_aura.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - }, - keystore, - collator_key, - para_id, - relay_chain_slot_duration, - proposer, - collator_service, - authoring_duration: Duration::from_millis(2000), - reinitialize: false, - slot_drift: Duration::from_secs(1), - }; - - // We have a separate function only to be able to use `docify::export` on this piece of - // code. - Self::launch_slot_based_collator(params, task_manager); - - Ok(()) - } -} - -/// Wait for the Aura runtime API to appear on chain. -/// This is useful for chains that started out without Aura. Components that -/// are depending on Aura functionality will wait until Aura appears in the runtime. -async fn wait_for_aura(client: Arc>) -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi, - AuraId: AuraIdT + Sync, -{ - let finalized_hash = client.chain_info().finalized_hash; - if client.runtime_api().has_aura_api(finalized_hash) { - return; - }; - - let mut stream = client.finality_notification_stream(); - while let Some(notification) = stream.next().await { - if client.runtime_api().has_aura_api(notification.hash) { - return; - } - } -} - -/// Start consensus using the lookahead aura collator. -pub(crate) struct StartLookaheadAuraConsensus( - PhantomData<(RuntimeApi, AuraId)>, -); - -impl StartConsensus - for StartLookaheadAuraConsensus -where - RuntimeApi: ConstructNodeRuntimeApi>, - RuntimeApi::RuntimeApi: AuraRuntimeApi, - AuraId: AuraIdT + Sync, -{ - fn start_consensus( - client: Arc>, - block_import: ParachainBlockImport, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc>>, - keystore: KeystorePtr, - relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, - backend: Arc, - node_extra_args: NodeExtraArgs, - ) -> Result<(), Error> { - let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry, - telemetry.clone(), - ); - - let collator_service = CollatorService::new( - client.clone(), - Arc::new(task_manager.spawn_handle()), - announce_block, - client.clone(), - ); - - let params = aura::ParamsWithExport { - export_pov: node_extra_args.export_pov, - params: AuraParams { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend, - relay_client: relay_chain_interface, - code_hash_provider: { - let client = client.clone(); - move |block_hash| { - client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) - } - }, - keystore, - collator_key, - para_id, - overseer_handle, - relay_chain_slot_duration, - proposer: Proposer::new(proposer_factory), - collator_service, - authoring_duration: Duration::from_millis(1500), - reinitialize: false, - }, - }; - - let fut = - async move { - wait_for_aura(client).await; - aura::run_with_export::::Pair, _, _, _, _, _, _, _, _>(params).await; - }; - task_manager.spawn_essential_handle().spawn("aura", None, fut); - - Ok(()) - } -} - -/// Checks that the hardware meets the requirements and print a warning otherwise. -fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { - // Polkadot para-chains should generally use these requirements to ensure that the relay-chain - // will not take longer than expected to import its blocks. - if let Err(err) = frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { - log::warn!( - "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ - https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", - err - ); - } -} - -type SyncCmdResult = sc_cli::Result<()>; - -type AsyncCmdResult<'a> = - sc_cli::Result<(Pin + 'a>>, TaskManager)>; - -pub(crate) trait DynNodeSpec { - fn prepare_check_block_cmd( - self: Box, - config: Configuration, - cmd: &CheckBlockCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_export_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ExportBlocksCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_export_state_cmd( - self: Box, - config: Configuration, - cmd: &ExportStateCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_import_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ImportBlocksCmd, - ) -> AsyncCmdResult<'_>; - - fn prepare_revert_cmd( - self: Box, - config: Configuration, - cmd: &RevertCmd, - ) -> AsyncCmdResult<'_>; - - fn run_export_genesis_head_cmd( - self: Box, - config: Configuration, - cmd: &ExportGenesisHeadCommand, - ) -> SyncCmdResult; - - fn run_benchmark_block_cmd( - self: Box, - config: Configuration, - cmd: &BlockCmd, - ) -> SyncCmdResult; - - #[cfg(any(feature = "runtime-benchmarks"))] - fn run_benchmark_storage_cmd( - self: Box, - config: Configuration, - cmd: &StorageCmd, - ) -> SyncCmdResult; - - fn start_node( - self: Box, - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, - node_extra_args: NodeExtraArgs, - ) -> Pin>>>; -} - -impl DynNodeSpec for T -where - T: NodeSpec, -{ - fn prepare_check_block_cmd( - self: Box, - config: Configuration, - cmd: &CheckBlockCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) - } - - fn prepare_export_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ExportBlocksCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, config.database)), partial.task_manager)) - } - - fn prepare_export_state_cmd( - self: Box, - config: Configuration, - cmd: &ExportStateCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, config.chain_spec)), partial.task_manager)) - } - - fn prepare_import_blocks_cmd( - self: Box, - config: Configuration, - cmd: &ImportBlocksCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager)) - } - - fn prepare_revert_cmd( - self: Box, - config: Configuration, - cmd: &RevertCmd, - ) -> AsyncCmdResult<'_> { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - Ok((Box::pin(cmd.run(partial.client, partial.backend, None)), partial.task_manager)) - } - - fn run_export_genesis_head_cmd( - self: Box, - config: Configuration, - cmd: &ExportGenesisHeadCommand, - ) -> SyncCmdResult { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - cmd.run(partial.client) - } - - fn run_benchmark_block_cmd( - self: Box, - config: Configuration, - cmd: &BlockCmd, - ) -> SyncCmdResult { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - cmd.run(partial.client) - } - - #[cfg(any(feature = "runtime-benchmarks"))] - fn run_benchmark_storage_cmd( - self: Box, - config: Configuration, - cmd: &StorageCmd, - ) -> SyncCmdResult { - let partial = Self::new_partial(&config).map_err(sc_cli::Error::Service)?; - let db = partial.backend.expose_db(); - let storage = partial.backend.expose_storage(); - - cmd.run(config, partial.client, db, storage) - } - - fn start_node( - self: Box, - parachain_config: Configuration, - polkadot_config: Configuration, - collator_options: CollatorOptions, - para_id: ParaId, - hwbench: Option, - node_extra_args: NodeExtraArgs, - ) -> Pin>>> { - match parachain_config.network.network_backend { - sc_network::config::NetworkBackendType::Libp2p => - ::start_node::>( - parachain_config, - polkadot_config, - collator_options, - para_id, - hwbench, - node_extra_args, - ), - sc_network::config::NetworkBackendType::Litep2p => - ::start_node::( - parachain_config, - polkadot_config, - collator_options, - para_id, - hwbench, - node_extra_args, - ), - } - } -} diff --git a/cumulus/primitives/aura/Cargo.toml b/cumulus/primitives/aura/Cargo.toml index 062b9ce736e7f7b9d36f23908bf94437a06f22cb..185b2d40833f0614a0856b79015eb90f0c57c30a 100644 --- a/cumulus/primitives/aura/Cargo.toml +++ b/cumulus/primitives/aura/Cargo.toml @@ -10,24 +10,14 @@ description = "Core primitives for Aura in Cumulus" workspace = true [dependencies] -codec = { features = ["derive"], workspace = true } # Substrate sp-api = { workspace = true } sp-consensus-aura = { workspace = true } -sp-runtime = { workspace = true } - -# Polkadot -polkadot-core-primitives = { workspace = true } -polkadot-primitives = { workspace = true } [features] default = ["std"] std = [ - "codec/std", - "polkadot-core-primitives/std", - "polkadot-primitives/std", "sp-api/std", "sp-consensus-aura/std", - "sp-runtime/std", ] diff --git a/cumulus/primitives/parachain-inherent/Cargo.toml b/cumulus/primitives/parachain-inherent/Cargo.toml index 172af4b9ec63e0843fdf59cdf90f21c842d60b41..a4271d3fd9cc34ac9a32787c3cce16d4cffc38ab 100644 --- a/cumulus/primitives/parachain-inherent/Cargo.toml +++ b/cumulus/primitives/parachain-inherent/Cargo.toml @@ -17,8 +17,6 @@ scale-info = { features = ["derive"], workspace = true } # Substrate sp-core = { workspace = true } sp-inherents = { workspace = true } -sp-runtime = { optional = true, workspace = true } -sp-state-machine = { optional = true, workspace = true } sp-trie = { workspace = true } # Cumulus @@ -33,7 +31,5 @@ std = [ "scale-info/std", "sp-core/std", "sp-inherents/std", - "sp-runtime?/std", - "sp-state-machine?/std", "sp-trie/std", ] diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index f48dd927ee962a23a0d831ec071469bb34e517e3..2529297691e8bcc15e31d070e20975c1eeaa1282 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -165,15 +165,17 @@ where ); return Ok(()) }; - let benchmarked_weight = info.weight.proof_size(); - let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); - // Unspent weight according to the `actual_weight` from `PostDispatchInfo` // This unspent weight will be refunded by the `CheckWeight` extension, so we need to // account for that. let unspent = post_info.calc_unspent(info).proof_size(); - let storage_size_diff = - benchmarked_weight.saturating_sub(unspent).abs_diff(consumed_weight as u64); + let benchmarked_weight = info.weight.proof_size().saturating_sub(unspent); + let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); + + let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64); + + let extrinsic_len = frame_system::AllExtrinsicsLen::::get().unwrap_or(0); + let node_side_pov_size = post_dispatch_proof_size.saturating_add(extrinsic_len.into()); // This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate // that in. @@ -181,16 +183,31 @@ 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) } + + // If we encounter a situation where the node-side proof size is already higher than + // what we have in the runtime bookkeeping, we add the difference to the `BlockWeight`. + // This prevents that the proof size grows faster than the runtime proof size. + let block_weight_proof_size = current.total().proof_size(); + let missing_from_node = node_side_pov_size.saturating_sub(block_weight_proof_size); + if missing_from_node > 0 { + log::warn!( + target: LOG_TARGET, + "Node-side PoV size higher than runtime proof size weight. node-side: {node_side_pov_size} extrinsic_len: {extrinsic_len} runtime: {block_weight_proof_size}, missing: {missing_from_node}. Setting to node-side proof size." + ); + current.accrue(Weight::from_parts(0, missing_from_node), info.class); + } }); Ok(()) } @@ -294,6 +311,121 @@ mod tests { }) } + #[test] + fn underestimating_refund() { + // We fixed a bug where `pre dispatch info weight > consumed weight > post info weight` + // resulted in error. + + // The real cost will be 100 bytes of storage size + let mut test_ext = setup_test_externalities(&[0, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { weight: Weight::from_parts(0, 101), ..Default::default() }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(0, 99)), + pays_fee: Default::default(), + }; + + assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + // We expect an accrue of 1 + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1250); + }) + } + + #[test] + fn sets_to_node_storage_proof_if_higher() { + // The storage proof reported by the proof recorder is higher than what is stored on + // the runtime side. + { + let mut test_ext = setup_test_externalities(&[1000, 1005]); + + test_ext.execute_with(|| { + // Stored in BlockWeight is 5 + set_current_storage_weight(5); + + // Benchmarked storage weight: 10 + let info = DispatchInfo { weight: Weight::from_parts(0, 10), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(1000)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + // We expect that the storage weight was set to the node-side proof size (1005) + + // extrinsics length (150) + assert_eq!(get_storage_weight().total().proof_size(), 1155); + }) + } + + // In this second scenario the proof size on the node side is only lower + // after reclaim happened. + { + let mut test_ext = setup_test_externalities(&[175, 180]); + test_ext.execute_with(|| { + set_current_storage_weight(85); + + // Benchmarked storage weight: 100 + let info = + DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // After this pre_dispatch, the BlockWeight proof size will be + // 85 (initial) + 100 (benched) + 150 (tx length) = 335 + assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); + + let pre = StorageWeightReclaim::(PhantomData) + .pre_dispatch(&ALICE, CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(175)); + + assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); + + // First we will reclaim 95, which leaves us with 240 BlockWeight. This is lower + // than 180 (proof size hf) + 150 (length), so we expect it to be set to 330. + assert_ok!(StorageWeightReclaim::::post_dispatch( + Some(pre), + &info, + &post_info, + LEN, + &Ok(()) + )); + + // We expect that the storage weight was set to the node-side proof weight + assert_eq!(get_storage_weight().total().proof_size(), 330); + }) + } + } + #[test] fn does_nothing_without_extension() { let mut test_ext = new_test_ext(); @@ -507,7 +639,7 @@ mod tests { #[test] fn test_nothing_relcaimed() { - let mut test_ext = setup_test_externalities(&[100, 200]); + let mut test_ext = setup_test_externalities(&[0, 100]); test_ext.execute_with(|| { set_current_storage_weight(0); @@ -530,7 +662,7 @@ mod tests { .pre_dispatch(&ALICE, CALL, &info, LEN) .unwrap(); // Should return `setup_test_externalities` proof recorder value: 100. - assert_eq!(pre, Some(100)); + assert_eq!(pre, Some(0)); // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` // we always need to call `post_dispatch` to verify that they interoperate correctly. diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 82d18c8c0aac60b98fad565857fff5561a1ac21e..2ca8b82001d5ad0146d2b686a9a93f4ee5996385 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -15,13 +15,11 @@ log = { workspace = true } # Substrate frame-support = { workspace = true } -sp-io = { workspace = true } sp-runtime = { workspace = true } pallet-asset-conversion = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true } -polkadot-runtime-parachains = { workspace = true } xcm = { workspace = true } xcm-executor = { workspace = true } xcm-builder = { workspace = true } @@ -38,8 +36,6 @@ std = [ "log/std", "pallet-asset-conversion/std", "polkadot-runtime-common/std", - "polkadot-runtime-parachains/std", - "sp-io/std", "sp-runtime/std", "xcm-builder/std", "xcm-executor/std", @@ -51,7 +47,6 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", - "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 3ebcb44fa439480762f4a667b3aac2ad35ef5b60..e568c79bb6a0185133ce60bde52641a9856622b9 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -99,6 +99,10 @@ where impl InspectMessageQueues for ParentAsUmp { + fn clear_messages() { + T::clear_messages(); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { T::get_messages() } diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 274f16ab630d6c0f9cff092b0ed79624e2a27a67..ba0a3487011abea762b0257fef3d84f763d26153 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -132,7 +132,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; #[cfg(feature = "increment-spec-version")] @@ -146,7 +146,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; diff --git a/cumulus/test/service/benches/validate_block_glutton.rs b/cumulus/test/service/benches/validate_block_glutton.rs index 6ec338c7f14280fc0de6f1067e94e3f5e77de77c..6fe26519a3ebdc96c45d90f2f2f990b4ac154e38 100644 --- a/cumulus/test/service/benches/validate_block_glutton.rs +++ b/cumulus/test/service/benches/validate_block_glutton.rs @@ -43,7 +43,7 @@ use sp_runtime::traits::Header as HeaderT; use cumulus_test_service::bench_utils as utils; async fn import_block( - mut client: &cumulus_test_client::Client, + client: &cumulus_test_client::Client, built: cumulus_test_runtime::Block, import_existing: bool, ) { diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs index 4ace894b392aa2393741572249ed35f0a7101845..67ffbdd1d2129921782626300292d2267f26a8a0 100644 --- a/cumulus/test/service/src/bench_utils.rs +++ b/cumulus/test/service/src/bench_utils.rs @@ -111,7 +111,7 @@ pub fn extrinsic_set_validation_data( } /// Import block into the given client and make sure the import was successful -pub async fn import_block(mut client: &TestClient, block: &NodeBlock, import_existing: bool) { +pub async fn import_block(client: &TestClient, block: &NodeBlock, import_existing: bool) { let mut params = BlockImportParams::new(BlockOrigin::File, block.header.clone()); params.body = Some(block.extrinsics.clone()); params.state_action = StateAction::Execute; diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index 37ca27542cbfe0cd0d93ba7eb6a6dfb7a05459c0..220b0449f3392727228f99b3d10f2ccbc2165693 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use std::{net::SocketAddr, path::PathBuf}; +use std::path::PathBuf; use cumulus_client_cli::{ExportGenesisHeadCommand, ExportGenesisWasmCommand}; use polkadot_service::{ChainSpec, ParaId, PrometheusConfig}; use sc_cli::{ CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, - Result as CliResult, SharedParams, SubstrateCli, + Result as CliResult, RpcEndpoint, SharedParams, SubstrateCli, }; use sc_service::BasePath; @@ -122,7 +122,7 @@ impl CliConfiguration for RelayChainCli { .or_else(|| self.base_path.clone().map(Into::into))) } - fn rpc_addr(&self, default_listen_port: u16) -> CliResult> { + fn rpc_addr(&self, default_listen_port: u16) -> CliResult>> { self.base.base.rpc_addr(default_listen_port) } @@ -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 503c2f24fd54941e1ac65b2fdabd0790ac375ca2..a600dcce3d66e789cdcd8164fa9c51aa3d69d306 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -38,7 +38,7 @@ use sp_consensus_aura::sr25519::AuthorityPair; use std::{ collections::HashSet, future::Future, - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, time::Duration, }; use url::Url; @@ -78,8 +78,9 @@ use sc_network::{ }; use sc_service::{ config::{ - BlocksPruning, DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, NetworkConfiguration, - OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, WasmExecutionMethod, + BlocksPruning, DatabaseSource, ExecutorConfiguration, KeystoreConfig, MultiaddrWithPeerId, + NetworkConfiguration, OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, + RpcConfiguration, RpcEndpoint, WasmExecutionMethod, }, BasePath, ChainSpec as ChainSpecService, Configuration, Error as ServiceError, PartialComponents, Role, RpcHandlers, TFullBackend, TFullClient, TaskManager, @@ -194,15 +195,16 @@ pub fn new_partial( enable_import_proof_record: bool, ) -> Result { let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = WasmExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = @@ -379,7 +381,7 @@ where let keystore = params.keystore_container.keystore(); let rpc_builder = { let client = client.clone(); - Box::new(move |_, _| rpc_ext_builder(client.clone())) + Box::new(move |_| rpc_ext_builder(client.clone())) }; let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { @@ -863,38 +865,41 @@ pub fn node_config( state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: + sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() + }, + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: None, + max_subs_per_conn: Default::default(), + port: 9945, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), }, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: None, - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9945, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(key_seed), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, data_path: root, base_path, wasm_runtime_overrides: None, - runtime_cache_size: 2, }) } @@ -1006,7 +1011,22 @@ pub fn run_relay_chain_validator_node( ); if let Some(port) = port { - config.rpc_addr = Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port)); + config.rpc.addr = Some(vec![RpcEndpoint { + batch_config: config.rpc.batch_config, + cors: config.rpc.cors.clone(), + listen_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)), + max_connections: config.rpc.max_connections, + max_payload_in_mb: config.rpc.max_request_size, + max_payload_out_mb: config.rpc.max_response_size, + max_subscriptions_per_connection: config.rpc.max_subs_per_conn, + max_buffer_capacity_per_connection: config.rpc.message_buffer_capacity, + rpc_methods: config.rpc.methods, + rate_limit: config.rpc.rate_limit, + rate_limit_trust_proxy_headers: config.rpc.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: config.rpc.rate_limit_whitelisted_ips.clone(), + retry_random_port: true, + is_optional: false, + }]); } let mut workers_path = std::env::current_exe().unwrap(); diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index 8de3660c223627155a27f5ecc659a3ea030d7b02..afed14278d17560411fc13bb7c9c325890487a1d 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -296,9 +296,11 @@ impl Bridge for () { fn init() {} } +pub type BridgeLaneId = Vec; + #[derive(Clone, Default, Debug)] pub struct BridgeMessage { - pub id: u32, + pub lane_id: BridgeLaneId, pub nonce: u64, pub payload: Vec, } @@ -310,7 +312,7 @@ pub trait BridgeMessageHandler { message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError>; - fn notify_source_message_delivery(lane_id: u32); + fn notify_source_message_delivery(lane_id: BridgeLaneId); } impl BridgeMessageHandler for () { @@ -324,7 +326,7 @@ impl BridgeMessageHandler for () { Err(BridgeMessageDispatchError(Box::new("Not a bridge"))) } - fn notify_source_message_delivery(_lane_id: u32) {} + fn notify_source_message_delivery(_lane_id: BridgeLaneId) {} } #[derive(Debug)] @@ -1079,12 +1081,12 @@ macro_rules! decl_test_networks { }); match dispatch_result { - Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg.clone()), + Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg), Ok(()) => { <::Source as TestExt>::ext_wrapper(|| { - <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.id); + <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.lane_id.clone()); }); - $crate::log::debug!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg.clone()); + $crate::log::debug!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg); } } } diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 60698de1d6addf53385ba6e3f30b36e9a1d7f784..55b9156e6a0ae811c8ef1d2bf8761452ae8c561a 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -1,7 +1,7 @@ # this image is built on top of existing Zombienet image ARG ZOMBIENET_IMAGE # this image uses substrate-relay image built elsewhere -ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.6 +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.8 # metadata ARG VCS_REF diff --git a/docs/BACKPORT.md b/docs/BACKPORT.md new file mode 100644 index 0000000000000000000000000000000000000000..0b4a97e6f667594ffb38f8b12e3a35f3d0720d16 --- /dev/null +++ b/docs/BACKPORT.md @@ -0,0 +1,21 @@ +# Backporting + +This document explains how to backport a merged PR from `master` to one of the `stable*` branches. +Backports should only be used to fix bugs or security issues - never to introduce new features. + +## Steps + +1. Fix a bug through a PR that targets `master`. +2. Add label `A4-needs-backport` to the PR. +3. Merge the PR into `master`. +4. Wait for the bot to open the backport PR. +5. Ensure the change is audited or does not need audit. +6. Merge the backport PR. + +The label can also be added after the PR is merged. + +## Example + +For example here where the dev triggered the process by adding the label after merging: + +![backport](./images/backport-ex2.png) diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 653e6a2a3e929e052cdd839884ed30c5b5327201..bea36741135969fb216fbe559709d8b43a1c4b35 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -55,9 +55,10 @@ The Westend testnet will be updated to a new runtime every two weeks with the la **From `master` to `stable`** -Backports in this direction can be anything that is audited and either a `minor` or a `patch` bump. [Security -fixes](#bug-and-security-fix) should be prioritized over additions or improvements. Crates that are declared as internal -API can also have `major` version bumps through backports. +Backports in this direction can be anything that is audited and either a `minor` or a `patch` bump. +See [BACKPORT.md](./BACKPORT.md) for more explanation. [Security fixes](#bug-and-security-fix) +should be prioritized over additions or improvements. Crates that are declared as internal API can +also have `major` version bumps through backports. **From `stable` to `master`** @@ -164,5 +165,6 @@ Describes how developers should merge bug and security fixes. 2. The Pull Request is marked as priority fix. 3. Audit happens with priority. 4. It is merged into `master`. -5. It is automatically back-ported to `stable`. -6. The fix will be released in the next *Stable* release. In urgent cases, a release can happen earlier. +5. Dev adds the `A4-needs-backport` label. +6. It is automatically back-ported to `stable`. +7. The fix will be released in the next *Stable* release. In urgent cases, a release can happen earlier. diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index d8f956b82d2d807a75957c36801966ac58036898..53f42b9ae4fb41e995c16ef03f0abcc61a030684 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -42,13 +42,13 @@ The set of labels and their description can be found [here](https://paritytech.g 3. If you’re still working on your PR, please submit as “Draft”. Once a PR is ready for review change the status to “Open”, so that the maintainers get to review your PR. Generally PRs should sit for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. -4. With respect to auditing, please see [AUDIT.md](../AUDIT.md). In general, merging to master can happen independent of +4. With respect to auditing, please see [AUDIT.md](../AUDIT.md). In general, merging to master can happen independently of audit. 5. PRs will be able to be merged once all reviewers' comments are addressed and CI is successful. **Noting breaking changes:** When breaking APIs, the PR description should mention what was changed alongside some examples on how to change the code to make it work/compile. It should also mention potential storage migrations and if -they require some special setup aside adding it to the list of migrations in the runtime. +they require some special setup aside from adding it to the list of migrations in the runtime. ## Reviewing pull requests @@ -161,11 +161,11 @@ test output there is a script * `./scripts/update-ui-tests.sh` to update the tests for a current rust version locally * `./scripts/update-ui-tests.sh 1.70` # to update the tests for a specific rust version locally -Or if you have opened PR and you're member of `paritytech` - you can use command-bot to run the tests for you in CI: -* `bot update-ui` - will run the tests for the current rust version -* `bot update-ui latest --rust_version=1.70.0` - will run the tests for the specified rust version -* `bot update-ui latest -v CMD_IMAGE=paritytech/ci-unified:bullseye-1.70.0-2023-05-23 --rust_version=1.70.0` - will run -the tests for the specified rust version and specified image +Or if you have opened PR and you're member of `paritytech` - you can use [/cmd](./commands-readme.md) +to run the tests for you in CI: +* `/cmd update-ui` - will run the tests for the current rust version +* `/cmd update-ui --image docker.io/paritytech/ci-unified:bullseye-1.70.0-2023-05-23` - +will run the tests for the specified rust version and specified image ## Feature Propagation @@ -175,7 +175,7 @@ We use [zepter](https://github.com/ggwpez/zepter) to enforce features are propag If you're member of **paritytech** org - you can use command-bot to run various of common commands in CI: -Start with comment in PR: `bot help` to see the list of available commands. +Start with comment in PR: `/cmd --help` to see the list of available commands. ## Deprecating code diff --git a/docs/contributor/DOCUMENTATION_GUIDELINES.md b/docs/contributor/DOCUMENTATION_GUIDELINES.md index cc1082347d20c666534ca243685dc24cc62c7114..5ac99fff1cdbfa2b2425947f5af242c8dd03966f 100644 --- a/docs/contributor/DOCUMENTATION_GUIDELINES.md +++ b/docs/contributor/DOCUMENTATION_GUIDELINES.md @@ -136,7 +136,7 @@ the `macro@my_macro_name` syntax in your link. Read more about how to correctly The above five guidelines must always be reasonably respected in the documentation. -The following are a set of notes that may not necessarily hold in all circumstances: +The following is a set of notes that may not necessarily hold in all circumstances: --- @@ -205,7 +205,7 @@ properly do this. ## Pallet Crates -The guidelines so far have been general in nature, and are applicable to crates that are pallets and crates that're not +The guidelines so far have been general in nature, and are applicable to crates that are pallets and crates that are not pallets. The following is relevant to how to document parts of a crate that is a pallet. See diff --git a/docs/contributor/PULL_REQUEST_TEMPLATE.md b/docs/contributor/PULL_REQUEST_TEMPLATE.md index 083b30b4a3567904a62c83dd07a21677b7c0e048..99455c985076df0792bf2fff9fd7f488804595a1 100644 --- a/docs/contributor/PULL_REQUEST_TEMPLATE.md +++ b/docs/contributor/PULL_REQUEST_TEMPLATE.md @@ -20,7 +20,7 @@ reviewed by reviewers, if the PR does NOT have the `R0-Silent` label. In case of ## Review Notes -*In depth notes about the **implenentation** details of your PR. This should be the main guide for reviewers to +*In depth notes about the **implementation** details of your PR. This should be the main guide for reviewers to understand your approach and effectively review it. If too long, use [`
`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)*. @@ -33,8 +33,9 @@ possibly integration.* # Checklist * [ ] My PR includes a detailed description as outlined in the "Description" and its two subsections above. -* [ ] My PR follows the [labeling requirements](CONTRIBUTING.md#Process) of this project (at minimum one label for `T` - required) +* [ ] My PR follows the [labeling requirements]( +https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process +) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) diff --git a/docs/contributor/commands-readme.md b/docs/contributor/commands-readme.md new file mode 100644 index 0000000000000000000000000000000000000000..861c3ac784d5deb3f569f86a61201d1d6e2bdbb7 --- /dev/null +++ b/docs/contributor/commands-readme.md @@ -0,0 +1,47 @@ +# Running Commands in PRs + +You can run commands in PRs by triggering it via comment. It will use the context of your PR and post the results back. +Note: it works only for members of the `paritytech` organization. + +## Usage + +`/cmd --help` to see all available commands and usage format + +`/cmd --help` to see the usage of a specific command + +### Commands + +- `/cmd fmt` to format the code in the PR. It commits back with the formatted code (fmt) and configs (taplo). + +- `/cmd bench` to generate weights for a runtime. Read more about [Weight Generation](weight-generation.md) + +- `/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. +By default, the Start and End/Failure of the command will be commented with the link to a pipeline. +If you want to avoid, use this flag. Go to +[Action Tab](https://github.com/paritytech/polkadot-sdk/actions/workflows/cmd.yml) to see the pipeline status. + +2.`--continue-on-fail` to continue running the command even if something inside a command +(like specific pallet weight generation) are failed. +Basically avoids interruption in the middle with `exit 1` +The pipeline logs will include what is failed (like which runtimes/pallets), then you can re-run them separately or not. + +3.`--clean` to clean up all yours and bot's comments in PR relevant to `/cmd` commands. If you run too many commands, +or they keep failing, and you're rerunning them again, it's handy to add this flag to keep a PR clean. + +### Adding new Commands + +Feel free to add new commands to the workflow, however **_note_** that triggered workflows will use the actions +from `main` (default) branch, meaning they will take effect only after the PR with new changes/command is merged. +If you want to test the new command, it's better to test in your fork and local-to-fork PRs, where you control +the default branch. + +### Examples + +The regex in cmd.yml is: `^(\/cmd )([-\/\s\w.=:]+)$` accepts only alphanumeric, space, "-", "/", "=", ":", "." chars. + +`/cmd bench --runtime bridge-hub-westend --pallet=pallet_name` +`/cmd update-ui --image=docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v202407161507 --clean` diff --git a/docs/contributor/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 new file mode 100644 index 0000000000000000000000000000000000000000..ebfdca59cae5d6f00b681d0b8ff94080e17202a4 --- /dev/null +++ b/docs/contributor/weight-generation.md @@ -0,0 +1,69 @@ +# Weight Generation + +To generate weights for a runtime. +Weights generation is using self-hosted runner which is provided by Parity CI, the rest commands are using standard +GitHub runners on `ubuntu-latest` or `ubuntu-20.04`. +Self-hosted runner for benchmarks (arc-runners-Polkadot-sdk-benchmark) is configured to meet requirements of reference +hardware for running validators +https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware + +In a PR run the actions through comment: + +```sh +/cmd bench --help # outputs the actual usage documentation with examples and supported runtimes + +# or + +/cmd --help # to see all available commands +``` + +To regenerate all weights (however it will take long, +so don't do it unless you really need it), run the following command: +```sh +/cmd bench +``` + +To generate weights for all pallets in a particular runtime(s), run the following command: +```sh +/cmd bench --runtime kusama polkadot +``` + +For Substrate pallets (supports sub-modules too): +```sh +/cmd bench --runtime dev --pallet pallet_asset_conversion_ops +``` + +> **📝 Note**: The action is not being run right-away, it will be queued and run in the next available runner. +So might be quick, but might also take up to 10 mins (That's in control of Github). +Once the action is run, you'll see reaction 👀 on original comment, and if you didn't pass `--quiet` - +it will also send a link to a pipeline when started, and link to whole workflow when finished. + +--- + +> **💡Hint #1** : if you run all runtimes or all pallets, it might be that some pallet in the middle is failed +to generate weights, thus it stops (fails) the whole pipeline. +> If you want, you can make it to continue running, even if some pallets are failed, add `--continue-on-fail` +flag to the command. The report will include which runtimes/pallets have failed, then you can re-run +them separately after all is done. + +This way it runs all possible runtimes for the specified pallets, if it finds them in the runtime +```sh +/cmd bench --pallet pallet_balances pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible +``` + +If you want to run all specific pallet(s) for specific runtime(s), you can do it like this: +```sh +/cmd bench --runtime bridge-hub-polkadot --pallet pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible +``` + + +> **💡Hint #2** : Sometimes when you run too many commands, or they keep failing and you're rerunning them again, +it's handy to add `--clean` flag to the command. This will clean up all yours and bot's comments in PR relevant to +/cmd commands. + +```sh +/cmd bench --runtime kusama polkadot --pallet=pallet_balances --clean --continue-on-fail +``` + +> **💡Hint #3** : If you have questions or need help, feel free to tag @paritytech/opstooling (in github comments) +or ping in [matrix](https://matrix.to/#/#command-bot:parity.io) channel. diff --git a/docs/images/Polkadot_Logo_Horizontal_Pink_BlackOnWhite.png b/docs/images/Polkadot_Logo_Horizontal_Pink_BlackOnWhite.png new file mode 100644 index 0000000000000000000000000000000000000000..ef2b997100ea26310f6588b5b932e3c9cc7d2604 Binary files /dev/null and b/docs/images/Polkadot_Logo_Horizontal_Pink_BlackOnWhite.png differ diff --git a/docs/images/Polkadot_Logo_Horizontal_Pink_WhiteOnBlack.png b/docs/images/Polkadot_Logo_Horizontal_Pink_WhiteOnBlack.png new file mode 100644 index 0000000000000000000000000000000000000000..421a38e1bdfad4c81be564dcebccf24d55a545a9 Binary files /dev/null and b/docs/images/Polkadot_Logo_Horizontal_Pink_WhiteOnBlack.png differ diff --git a/docs/images/backport-ex2.png b/docs/images/backport-ex2.png new file mode 100644 index 0000000000000000000000000000000000000000..97ccf6b00fb91c1691f1dd4eaf69c626ed6be741 Binary files /dev/null and b/docs/images/backport-ex2.png differ diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index 2f85171bb93d2c1e65b51a02e82559b2c5f96a55..adc1c1a8efbca343e6693ca0027654cba3ea532b 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -22,6 +22,7 @@ frame = { features = [ "runtime", ], workspace = true, default-features = true } pallet-examples = { workspace = true } +pallet-contracts = { workspace = true } pallet-default-config-example = { workspace = true, default-features = true } pallet-example-offchain-worker = { workspace = true, default-features = true } @@ -30,7 +31,7 @@ simple-mermaid = "0.1.1" docify = { workspace = true } # Polkadot SDK deps, typically all should only be in scope such that we can link to their doc item. -polkadot-sdk = { features = ["runtime"], workspace = true, default-features = true } +polkadot-sdk = { features = ["runtime-full"], workspace = true, default-features = true } node-cli = { workspace = true } kitchensink-runtime = { workspace = true } chain-spec-builder = { workspace = true, default-features = true } diff --git a/docs/sdk/assets/header.html b/docs/sdk/assets/header.html index f55c31b53216cddc5da94099efc3566c27bdfe63..c24c10940759771dbcb672024e290b3422e57f96 100644 --- a/docs/sdk/assets/header.html +++ b/docs/sdk/assets/header.html @@ -14,12 +14,13 @@ headers.forEach(header => { let link = document.createElement("a"); link.href = "#" + header.id; - link.textContent = header.textContent; + const headerTextContent = header.textContent.replace("§", "") + link.textContent = headerTextContent; link.className = header.tagName.toLowerCase(); toc.appendChild(link); - if (header.id == "modules" && header.textContent == "Modules") { + if (header.id == "modules" && headerTextContent == "Modules") { modules.forEach(module => { let link = document.createElement("a"); link.href = module.href; 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/async_backing_guide.rs b/docs/sdk/src/guides/async_backing_guide.rs index a9fda2c3aa8ac046f0fa0606d6163ae97b2b66dd..25ef3a12cbf033d37ebaca2bb6464dd67c92735e 100644 --- a/docs/sdk/src/guides/async_backing_guide.rs +++ b/docs/sdk/src/guides/async_backing_guide.rs @@ -174,7 +174,7 @@ //! - In the `para_client` field, pass in a cloned para client rather than the original //! - Add a `para_backend` parameter after `para_client`, passing in our para backend //! - Provide a `code_hash_provider` closure like that shown below -//! - Increase `authoring_duration` from 500 milliseconds to 1500 +//! - Increase `authoring_duration` from 500 milliseconds to 2000 //! ```ignore //! let params = AuraParams { //! .. @@ -185,7 +185,7 @@ //! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash()) //! }, //! .. -//! authoring_duration: Duration::from_millis(1500), +//! authoring_duration: Duration::from_millis(2000), //! .. //! }; //! ``` diff --git a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs index 939043b53529c101ea9dc438605865534b7659d8..38ef18b88e0d0af6ee6b25cadf577367a55c5e22 100644 --- a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs +++ b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs @@ -85,7 +85,7 @@ //! This phase consists of plugging in the new slot-based collator. //! //! 1. In `node/src/service.rs` import the slot based collator instead of the lookahead collator. -#![doc = docify::embed!("../../cumulus/polkadot-parachain/src/service.rs", slot_based_colator_import)] +#![doc = docify::embed!("../../cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs", slot_based_colator_import)] //! //! 2. In `start_consensus()` //! - Remove the `overseer_handle` param (also remove the @@ -94,7 +94,7 @@ //! `slot_drift` field with a value of `Duration::from_secs(1)`. //! - Replace the single future returned by `aura::run` with the two futures returned by it and //! spawn them as separate tasks: -#![doc = docify::embed!("../../cumulus/polkadot-parachain/src/service.rs", launch_slot_based_collator)] +#![doc = docify::embed!("../../cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs", launch_slot_based_collator)] //! //! 3. In `start_parachain_node()` remove the `overseer_handle` param passed to `start_consensus`. //! diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index 3c74469e1768d0b7749353ba337e9f4bdd533589..fcfaab00e5522e054326aff5d273fa86b9d9b3da 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -16,12 +16,12 @@ //! //! ## 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. //! //! > Be aware that you can read the entire source code backing this tutorial by clicking on the -//! > [`source`](./mod.rs.html) button at the top right of the page. +//! > `source` button at the top right of the page. //! //! You should have studied the following modules as a prelude to this guide: //! @@ -45,7 +45,7 @@ //! Consider the following as a "shell pallet". We continue building the rest of this pallet based //! on this template. //! -//! [`pallet::config`] and [`pallet::pallet`](frame_support::pallet) are both mandatory parts of any +//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any //! pallet. Refer to the documentation of each to get an overview of what they do. #![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", shell_pallet)] //! @@ -319,13 +319,13 @@ //! - Learn more about the individual pallet items/macros, such as event and errors and call, in //! [`frame::pallet_macros`]. //! -//! [`pallet::storage`]: ../../../frame_support/pallet_macros/attr.config.html -//! [`pallet::call`]: ../../../frame_support/pallet_macros/attr.call.html -//! [`pallet::event`]: ../../../frame_support/pallet_macros/attr.event.html -//! [`pallet::error`]: ../../../frame_support/pallet_macros/attr.error.html -//! [`pallet::pallet`]: ../../../frame_support/pallet_macros/attr.pallet.html -//! [`pallet::config`]: ../../../frame_support/pallet_macros/attr.config.html -//! [`pallet::generate_deposit`]: ../../../frame_support/pallet_macros/attr.generate_deposit.html +//! [`pallet::storage`]: frame_support::pallet_macros::storage +//! [`pallet::call`]: frame_support::pallet_macros::call +//! [`pallet::event`]: frame_support::pallet_macros::event +//! [`pallet::error`]: frame_support::pallet_macros::error +//! [`pallet::pallet`]: frame_support::pallet +//! [`pallet::config`]: frame_support::pallet_macros::config +//! [`pallet::generate_deposit`]: frame_support::pallet_macros::generate_deposit #[docify::export] #[frame::pallet(dev_mode)] @@ -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 bc0970c01f1ff8700e9dcb95091c58559076023f..35f73c290bf2c676bb010caee3a362fb5e14ee2b 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -25,9 +25,9 @@ #![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")] #![warn(rustdoc::broken_intra_doc_links)] #![warn(rustdoc::private_intra_doc_links)] -#![doc(html_favicon_url = "https://polkadot.network/favicon-32x32.png")] +#![doc(html_favicon_url = "https://polkadot.com/favicon.ico")] #![doc( - html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" + 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/templates.rs b/docs/sdk/src/polkadot_sdk/templates.rs index e87eb9c2bc8abe72538f4bc88a8f6348e1e90343..ab742ad5c3a2bd10fc621405ce8b4b407e93cc76 100644 --- a/docs/sdk/src/polkadot_sdk/templates.rs +++ b/docs/sdk/src/polkadot_sdk/templates.rs @@ -7,42 +7,35 @@ //! //! ## 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 //! //! In June 2023, OpenZeppelin was awarded a grant from the [Polkadot //! treasury](https://polkadot.polkassembly.io/treasury/406) for building a number of Polkadot-sdk -//! based templates. These templates are expected to be a great starting point for developers. -//! -//! - +//! based templates. These templates are a great starting point for developers and newcomers. +//! So far OpenZeppelin has released two templates, which have been fully [audited](https://github.com/OpenZeppelin/polkadot-runtime-templates/tree/main/audits): +//! - [`generic-runtime-template`](https://github.com/OpenZeppelin/polkadot-runtime-templates?tab=readme-ov-file#generic-runtime-template): +//! A minimal template that has all the common pallets that parachains use with secure defaults. +//! - [`evm-runtime-template`](https://github.com/OpenZeppelin/polkadot-runtime-templates/tree/main?tab=readme-ov-file#evm-template): +//! This template has EVM compatibility out of the box and allows migrating your solidity contracts +//! or EVM compatible dapps easily. It also uses 20 byte addresses like Ethereum and has some +//! Account Abstraction support. //! //! ## POP-CLI //! diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 557795cb410c01e3d2faea3941b8efd4d0372fa4..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 @@ -178,7 +180,6 @@ //! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build //! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config //! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig -//! [`chain_spec_builder`]: ../../../staging_chain_spec_builder/index.html //! [`serde`]: https://serde.rs/field-attrs.html //! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch //! [`GenesisBuilder::get_preset`]: sp_genesis_builder::GenesisBuilder::get_preset diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index c45f0126337e5a42b83fee6e80c5be04c03c1d73..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 @@ -32,10 +32,7 @@ use frame::{ runtime, }, prelude::*, - runtime::{ - apis::{self, impl_runtime_apis, ExtrinsicInclusionMode}, - prelude::*, - }, + runtime::{apis, prelude::*}, }; use sp_genesis_builder::PresetId; @@ -49,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/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs b/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs index 4a0ba3ca48f5a374f92424aa039330721efcc4d2..c91b66b944c603eaa75625d9d1e84b698f1c3027 100644 --- a/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs +++ b/docs/sdk/src/reference_docs/runtime_vs_smart_contract.rs @@ -20,8 +20,8 @@ //! #### Smart Contracts in Substrate //! Smart Contracts are autonomous, programmable constructs deployed on the blockchain. //! In [FRAME](frame), Smart Contracts infrastructure is implemented by the -//! [`pallet_contracts`](../../../pallet_contracts/index.html) for WASM-based contracts or the -//! [`pallet_evm`](../../../pallet_evm/index.html) for EVM-compatible contracts. These pallets +//! [`pallet_contracts`] for WASM-based contracts or the +//! [`pallet_evm`](https://github.com/polkadot-evm/frontier/tree/master/frame/evm) for EVM-compatible contracts. These pallets //! enable Smart Contract developers to build applications and systems on top of a Substrate-based //! blockchain. //! @@ -108,7 +108,7 @@ //! - **Deployment and Iteration**: Smart Contracts, by nature, are designed for more //! straightforward deployment and iteration. Developers can quickly deploy contracts. //! - **Contract Code Updates**: Once deployed, although typically immutable, Smart Contracts can be -//! upgraded, but lack of migration logic. The [pallet_contracts](../../../pallet_contracts/index.html) +//! upgraded, but lack of migration logic. The [`pallet_contracts`] //! allows for contracts to be upgraded by exposing the `set_code` dispatchable. More details on this //! can be found in [Ink! documentation on upgradeable contracts](https://use.ink/basics/upgradeable-contracts). //! - **Isolated Impact**: Upgrades or changes to a smart contract generally impact only that diff --git a/polkadot/README.md b/polkadot/README.md index 47af79a3aa92470f6de863e1ab9f5e4b96308889..fa14995e9af376b6bd3a2040af512a7d1ba52127 100644 --- a/polkadot/README.md +++ b/polkadot/README.md @@ -103,9 +103,8 @@ Connect to the global Polkadot Mainnet network by running: ../target/release/polkadot --chain=polkadot ``` -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry](https://telemetry.polkadot.io/#list/Polkadot): https://telemetry.polkadot.io/#list/Polkadot +You can see your node on [Polkadot telemetry](https://telemetry.polkadot.io/#list/0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3) +(set a custom name with `--name "my custom name"`). ### Connect to the "Kusama" Canary Network @@ -115,9 +114,8 @@ Connect to the global Kusama canary network by running: ../target/release/polkadot --chain=kusama ``` -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry](https://telemetry.polkadot.io/#list/Kusama): https://telemetry.polkadot.io/#list/Kusama +You can see your node on [Kusama telemetry](https://telemetry.polkadot.io/#list/0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe) +(set a custom name with `--name "my custom name"`). ### Connect to the Westend Testnet @@ -127,9 +125,8 @@ Connect to the global Westend testnet by running: ../target/release/polkadot --chain=westend ``` -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry](https://telemetry.polkadot.io/#list/Westend): https://telemetry.polkadot.io/#list/Westend +You can see your node on [Westend telemetry](https://telemetry.polkadot.io/#list/0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e) +(set a custom name with `--name "my custom name"`). ### Obtaining DOTs @@ -147,7 +144,7 @@ Then, grab the Polkadot source code: ```bash git clone https://github.com/paritytech/polkadot-sdk.git -cd polkadot +cd polkadot-sdk ``` Then build the code. You will need to build in release mode (`--release`) to start a network. Only @@ -185,7 +182,7 @@ You can run a simple single-node development "network" on your machine by runnin cargo run --bin polkadot --release -- --dev ``` -You can muck around by heading to and choose "Local Node" from the +You can muck around by heading to and choosing "Local Node" from the Settings menu. ### Local Two-node Testnet @@ -214,11 +211,11 @@ that we currently maintain. ### Using Docker -[Using Docker](../docs/contributor/docker.md) +[Using Docker](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/docker.md) ### Shell Completion -[Shell Completion](doc/shell-completion.md) +[Shell Completion](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/doc/shell-completion.md) ## Contributing @@ -232,4 +229,4 @@ that we currently maintain. ## License -Polkadot is [GPL 3.0 licensed](LICENSE). +Polkadot is [GPL 3.0 licensed](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/LICENSE). diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index 62d99122c3012701aa46039b13c3c05d3331d8c3..2947867c516e3353a2d3ad3f63a046dc90ef26d8 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -230,7 +230,7 @@ where let hwbench = (!cli.run.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) })) .flatten(); @@ -373,16 +373,16 @@ pub fn run() -> Result<()> { Ok(runner.async_run(|mut config| { let (client, backend, _, task_manager) = polkadot_service::new_chain_ops(&mut config, None)?; + let task_handle = task_manager.spawn_handle(); let aux_revert = Box::new(|client, backend, blocks| { - polkadot_service::revert_backend(client, backend, blocks, config).map_err( - |err| { + polkadot_service::revert_backend(client, backend, blocks, config, task_handle) + .map_err(|err| { match err { polkadot_service::Error::Blockchain(err) => err.into(), // Generic application-specific error. err => sc_cli::Error::Application(err.into()), } - }, - ) + }) }); Ok(( cmd.run(client, backend, Some(aux_revert)).map_err(Error::SubstrateCli), diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index d38516a4ff713f32c98c4e616155c4cdffb8bfb8..50adbddea4136e08e64893c419fb6bb28e5134f3 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -45,7 +45,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ request_async_backing_params, request_availability_cores, request_para_backing_state, request_persisted_validation_data, request_validation_code, request_validation_code_hash, - request_validators, vstaging::fetch_claim_queue, + request_validators, runtime::fetch_claim_queue, }; use polkadot_primitives::{ collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index bc0187bf49228a2bf5e691c4fddce89ede9dc225..e678118440f59080286f05b85193bf5e8a615c2e 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -22,6 +22,7 @@ kvdb = { workspace = true } derive_more = { workspace = true, default-features = true } thiserror = { workspace = true } itertools = { workspace = true } +async-trait = { workspace = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } diff --git a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs index a18e667253d08298eb32fd5a65762f80482715a6..0b03f1127ee812ec303230562a166f4d8b185fd5 100644 --- a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs +++ b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs @@ -74,15 +74,16 @@ fn main() -> Result<(), String> { .map_err(|e| e.to_string())?; println!("{}", average_usage); - // We expect no variance for received and sent - // but use 0.001 because we operate with floats + // We expect some small variance for received and sent because the + // test messages are generated at every benchmark run and they contain + // random data so use 0.01 as the accepted variance. messages.extend(average_usage.check_network_usage(&[ - ("Received from peers", 52941.6071, 0.001), - ("Sent to peers", 63810.1859, 0.001), + ("Received from peers", 52941.6071, 0.01), + ("Sent to peers", 63995.2200, 0.01), ])); messages.extend(average_usage.check_cpu_usage(&[ - ("approval-distribution", 6.3912, 0.1), - ("approval-voting", 10.0578, 0.1), + ("approval-distribution", 12.2736, 0.1), + ("approval-voting", 2.7174, 0.1), ])); if messages.is_empty() { diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs index 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..bf6ea0c98149247e5172d284462f3f375e03f2ef 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -44,6 +44,7 @@ use polkadot_node_subsystem::{ overseer, RuntimeApiError, SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{determine_new_blocks, runtime::RuntimeInfo}; +use polkadot_overseer::SubsystemSender; use polkadot_primitives::{ node_features, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ConsensusLog, CoreIndex, GroupIndex, Hash, Header, SessionIndex, @@ -62,9 +63,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 +112,8 @@ enum ImportedBlockInfoError { /// Computes information about the imported block. Returns an error if the info couldn't be /// extracted. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn imported_block_info( - ctx: &mut Context, +async fn imported_block_info>( + sender: &mut Sender, env: ImportedBlockInfoEnv<'_>, block_hash: Hash, block_header: &Header, @@ -123,11 +125,12 @@ async fn imported_block_info( // fetch candidates let included_candidates: Vec<_> = { let (c_tx, c_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::CandidateEvents(c_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::CandidateEvents(c_tx), + )) + .await; let events: Vec = match c_rx.await { Ok(Ok(events)) => events, @@ -150,11 +153,12 @@ async fn imported_block_info( // short, that shouldn't happen. let session_index = { let (s_tx, s_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_header.parent_hash, - RuntimeApiRequest::SessionIndexForChild(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_header.parent_hash, + RuntimeApiRequest::SessionIndexForChild(s_tx), + )) + .await; let session_index = match s_rx.await { Ok(Ok(s)) => s, @@ -200,11 +204,12 @@ async fn imported_block_info( // by one block. This gives us the opposite invariant for sessions - the parent block's // post-state gives us the canonical information about the session index for any of its // children, regardless of which slot number they might be produced at. - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::CurrentBabeEpoch(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::CurrentBabeEpoch(s_tx), + )) + .await; match s_rx.await { Ok(Ok(s)) => s, @@ -215,7 +220,7 @@ async fn imported_block_info( }; let extended_session_info = - get_extended_session_info(env.runtime_info, ctx.sender(), block_hash, session_index).await; + get_extended_session_info(env.runtime_info, sender, block_hash, session_index).await; let enable_v2_assignments = extended_session_info.map_or(false, |extended_session_info| { *extended_session_info .node_features @@ -224,7 +229,7 @@ async fn imported_block_info( .unwrap_or(&false) }); - let session_info = get_session_info(env.runtime_info, ctx.sender(), block_hash, session_index) + let session_info = get_session_info(env.runtime_info, sender, block_hash, session_index) .await .ok_or(ImportedBlockInfoError::SessionInfoUnavailable)?; @@ -328,9 +333,15 @@ pub struct BlockImportedCandidates { /// * and return information about all candidates imported under each block. /// /// It is the responsibility of the caller to schedule wakeups for each block. -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -pub(crate) async fn handle_new_head( - ctx: &mut Context, +pub(crate) async fn handle_new_head< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender, + AVSender: SubsystemSender, + B: Backend, +>( + sender: &mut Sender, + approval_voting_sender: &mut AVSender, state: &State, db: &mut OverlayedBackend<'_, B>, session_info_provider: &mut RuntimeInfo, @@ -348,7 +359,7 @@ pub(crate) async fn handle_new_head( let header = { let (h_tx, h_rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockHeader(head, h_tx)).await; + sender.send_message(ChainApiMessage::BlockHeader(head, h_tx)).await; match h_rx.await? { Err(e) => { gum::debug!( @@ -374,7 +385,7 @@ pub(crate) async fn handle_new_head( let lower_bound_number = finalized_number.unwrap_or(lower_bound_number).max(lower_bound_number); let new_blocks = determine_new_blocks( - ctx.sender(), + sender, |h| db.load_block_entry(h).map(|e| e.is_some()), head, &header, @@ -400,12 +411,15 @@ pub(crate) async fn handle_new_head( keystore: &state.keystore, }; - match imported_block_info(ctx, env, block_hash, &block_header, finalized_number).await { + match imported_block_info(sender, env, block_hash, &block_header, finalized_number) + .await + { Ok(i) => imported_blocks_and_info.push((block_hash, block_header, i)), Err(error) => { // It's possible that we've lost a race with finality. let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockHash(block_header.number, tx)) + sender + .send_message(ChainApiMessage::FinalizedBlockHash(block_header.number, tx)) .await; let lost_to_finality = match rx.await { @@ -449,17 +463,11 @@ pub(crate) async fn handle_new_head( force_approve, } = imported_block_info; - let session_info = match get_session_info( - session_info_provider, - ctx.sender(), - head, - session_index, - ) - .await - { - Some(session_info) => session_info, - None => return Ok(Vec::new()), - }; + let session_info = + match get_session_info(session_info_provider, sender, head, session_index).await { + Some(session_info) => session_info, + None => return Ok(Vec::new()), + }; let (block_tick, no_show_duration) = { let block_tick = slot_number_to_tick(state.slot_duration_millis, slot); @@ -509,7 +517,7 @@ pub(crate) async fn handle_new_head( }; // If all bits are already set, then send an approve message. if approved_bitfield.count_ones() == approved_bitfield.len() { - ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; + sender.send_message(ChainSelectionMessage::Approved(block_hash)).await; } let block_entry = v3::BlockEntry { @@ -566,7 +574,7 @@ pub(crate) async fn handle_new_head( // Notify chain-selection of all approved hashes. for hash in approved_hashes { - ctx.send_message(ChainSelectionMessage::Approved(hash)).await; + sender.send_message(ChainSelectionMessage::Approved(hash)).await; } } @@ -574,9 +582,13 @@ pub(crate) async fn handle_new_head( hash: block_hash, number: block_header.number, parent_hash: block_header.parent_hash, - candidates: included_candidates.iter().map(|(hash, _, _, _)| *hash).collect(), + candidates: included_candidates + .iter() + .map(|(hash, _, core_index, group_index)| (*hash, *core_index, *group_index)) + .collect(), slot, session: session_index, + vrf_story: relay_vrf_story, }); imported_candidates.push(BlockImportedCandidates { @@ -598,7 +610,8 @@ pub(crate) async fn handle_new_head( "Informing distribution of newly imported chain", ); - ctx.send_unbounded_message(ApprovalDistributionMessage::NewBlocks(approval_meta)); + approval_voting_sender + .send_unbounded_message(ApprovalDistributionMessage::NewBlocks(approval_meta)); Ok(imported_candidates) } @@ -609,12 +622,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 +659,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,7 +673,7 @@ pub(crate) mod tests { State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::default()), spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( @@ -800,8 +817,9 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = - imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await.unwrap(); + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(4)) + .await + .unwrap(); assert_eq!(info.included_candidates, included_candidates); assert_eq!(info.session_index, session); @@ -947,7 +965,7 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await; + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(4)).await; assert_matches!(info, Err(ImportedBlockInfoError::VrfInfoUnavailable)); }) @@ -1086,7 +1104,7 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = imported_block_info(&mut ctx, env, hash, &header, &Some(6)).await; + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(6)).await; assert_matches!(info, Err(ImportedBlockInfoError::BlockAlreadyFinalized)); }) @@ -1122,7 +1140,8 @@ pub(crate) mod tests { #[test] fn imported_block_info_extracts_force_approve() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; let session_info = dummy_session_info(session); @@ -1185,7 +1204,7 @@ pub(crate) mod tests { }; let info = - imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await.unwrap(); + imported_block_info(ctx.sender(), env, hash, &header, &Some(4)).await.unwrap(); assert_eq!(info.included_candidates, included_candidates); assert_eq!(info.session_index, session); @@ -1378,8 +1397,11 @@ pub(crate) mod tests { let test_fut = { Box::pin(async move { let mut overlay_db = OverlayedBackend::new(&db); + + let mut approval_voting_sender = ctx.sender().clone(); let result = handle_new_head( - &mut ctx, + ctx.sender(), + &mut approval_voting_sender, &state, &mut overlay_db, &mut session_info_provider, diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index d4b6855a44d0e410bbec757cae25eb7bd4803acb..2149ce81fa80db498ef5fbeb94f8f83229e5da53 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -40,8 +40,9 @@ use polkadot_node_subsystem::{ ApprovalCheckError, ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, AssignmentCheckError, AssignmentCheckResult, AvailabilityRecoveryMessage, BlockDescription, CandidateValidationMessage, ChainApiMessage, - ChainSelectionMessage, DisputeCoordinatorMessage, HighestApprovedAncestorBlock, - RuntimeApiMessage, RuntimeApiRequest, + ChainSelectionMessage, CheckedIndirectAssignment, CheckedIndirectSignedApprovalVote, + DisputeCoordinatorMessage, HighestApprovedAncestorBlock, RuntimeApiMessage, + RuntimeApiRequest, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, SubsystemSender, @@ -55,9 +56,8 @@ use polkadot_node_subsystem_util::{ }; use polkadot_primitives::{ ApprovalVoteMultipleCandidates, ApprovalVotingParams, BlockNumber, CandidateHash, - CandidateIndex, CandidateReceipt, CoreIndex, DisputeStatement, ExecutorParams, GroupIndex, - Hash, PvfExecKind, SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, - ValidatorIndex, ValidatorPair, ValidatorSignature, + CandidateIndex, CandidateReceipt, CoreIndex, ExecutorParams, GroupIndex, Hash, PvfExecKind, + SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -91,9 +91,11 @@ use schnellru::{ByLength, LruMap}; use approval_checking::RequiredTranches; use bitvec::{order::Lsb0, vec::BitVec}; -use criteria::{AssignmentCriteria, RealAssignmentCriteria}; +pub use criteria::{AssignmentCriteria, Config as AssignmentConfig, RealAssignmentCriteria}; use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry}; -use time::{slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick}; +use polkadot_node_primitives::approval::time::{ + slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick, +}; mod approval_checking; pub mod approval_db; @@ -102,7 +104,6 @@ pub mod criteria; mod import; mod ops; mod persisted_entries; -pub mod time; use crate::{ approval_checking::{Check, TranchesToApproveResult}, @@ -123,7 +124,6 @@ const APPROVAL_CHECKING_TIMEOUT: Duration = Duration::from_secs(120); const WAIT_FOR_SIGS_TIMEOUT: Duration = Duration::from_millis(500); const APPROVAL_CACHE_SIZE: u32 = 1024; -const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. const APPROVAL_DELAY: Tick = 2; pub(crate) const LOG_TARGET: &str = "parachain::approval-voting"; @@ -165,7 +165,8 @@ pub struct ApprovalVotingSubsystem { db: Arc, mode: Mode, metrics: Metrics, - clock: Box, + clock: Arc, + spawner: Arc, } #[derive(Clone)] @@ -484,6 +485,7 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, + spawner: Arc, ) -> Self { ApprovalVotingSubsystem::with_config_and_clock( config, @@ -491,7 +493,8 @@ impl ApprovalVotingSubsystem { keystore, sync_oracle, metrics, - Box::new(SystemClock {}), + Arc::new(SystemClock {}), + spawner, ) } @@ -502,7 +505,8 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, - clock: Box, + clock: Arc, + spawner: Arc, ) -> Self { ApprovalVotingSubsystem { keystore, @@ -512,6 +516,7 @@ impl ApprovalVotingSubsystem { mode: Mode::Syncing(sync_oracle), metrics, clock, + spawner, } } @@ -551,12 +556,21 @@ fn db_sanity_check(db: Arc, config: DatabaseConfig) -> SubsystemRe #[overseer::subsystem(ApprovalVoting, error = SubsystemError, prefix = self::overseer)] impl ApprovalVotingSubsystem { - fn start(self, ctx: Context) -> SpawnedSubsystem { + fn start(self, mut ctx: Context) -> SpawnedSubsystem { let backend = DbBackend::new(self.db.clone(), self.db_config); - let future = - run::(ctx, self, Box::new(RealAssignmentCriteria), backend) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) - .boxed(); + let to_other_subsystems = ctx.sender().clone(); + let to_approval_distr = ctx.sender().clone(); + + let future = run::( + ctx, + to_other_subsystems, + to_approval_distr, + self, + Box::new(RealAssignmentCriteria), + backend, + ) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) + .boxed(); SpawnedSubsystem { name: "approval-voting-subsystem", future } } @@ -825,7 +839,7 @@ where struct State { keystore: Arc, slot_duration_millis: u64, - clock: Box, + clock: Arc, assignment_criteria: Box, spans: HashMap, // Per block, candidate records about how long we take until we gather enough @@ -961,20 +975,20 @@ impl State { } // Returns the approval voting params from the RuntimeApi. - #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] - async fn get_approval_voting_params_or_default( + async fn get_approval_voting_params_or_default>( &self, - ctx: &mut Context, + sender: &mut Sender, session_index: SessionIndex, block_hash: Hash, ) -> Option { let (s_tx, s_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(session_index, s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(session_index, s_tx), + )) + .await; match s_rx.await { Ok(Ok(params)) => { @@ -1143,9 +1157,36 @@ enum Action { Conclude, } +/// Trait for providing approval voting subsystem with work. +#[async_trait::async_trait] +pub trait ApprovalVotingWorkProvider { + async fn recv(&mut self) -> SubsystemResult>; +} + +#[async_trait::async_trait] #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -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, @@ -1169,19 +1210,11 @@ where no_show_stats: NoShowStats::default(), }; - // `None` on start-up. Gets initialized/updated on leaf update - let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { - keystore: None, - session_cache_lru_size: DISPUTE_WINDOW.get(), - }); - let mut wakeups = Wakeups::default(); - let mut currently_checking_set = CurrentlyCheckingSet::default(); - let mut delayed_approvals_timers = DelayedApprovalTimer::default(); - let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE)); - let mut last_finalized_height: Option = { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockNumber(tx)).await; + to_other_subsystems + .send_message(ChainApiMessage::FinalizedBlockNumber(tx)) + .await; match rx.await? { Ok(number) => Some(number), Err(err) => { @@ -1191,13 +1224,24 @@ where } }; + // `None` on start-up. Gets initialized/updated on leaf update + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + let mut wakeups = Wakeups::default(); + let mut currently_checking_set = CurrentlyCheckingSet::default(); + let mut delayed_approvals_timers = DelayedApprovalTimer::default(); + let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE)); + loop { let mut overlayed_db = OverlayedBackend::new(&backend); let actions = futures::select! { (_tick, woken_block, woken_candidate) = wakeups.next(&*state.clock).fuse() => { subsystem.metrics.on_wakeup(); process_wakeup( - &mut ctx, + &mut to_other_subsystems, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1207,9 +1251,11 @@ where &wakeups, ).await? } - next_msg = ctx.recv().fuse() => { + next_msg = work_provider.recv().fuse() => { let mut actions = handle_from_overseer( - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, + &subsystem.spawner, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1269,7 +1315,8 @@ where &mut overlayed_db, &mut session_info_provider, &state, - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, block_hash, validator_index, &subsystem.metrics, @@ -1291,7 +1338,9 @@ where }; if handle_actions( - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, + &subsystem.spawner, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1318,6 +1367,63 @@ where Ok(()) } +// Starts a worker thread that runs the approval voting subsystem. +pub async fn start_approval_worker< + WorkProvider: ApprovalVotingWorkProvider + Send + 'static, + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + work_provider: WorkProvider, + to_other_subsystems: Sender, + to_approval_distr: ADSender, + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + spawner: Arc, + task_name: &'static str, + group_name: &'static str, + clock: Arc, +) -> SubsystemResult<()> { + let approval_voting = ApprovalVotingSubsystem::with_config_and_clock( + config, + db.clone(), + keystore, + sync_oracle, + metrics, + clock, + spawner, + ); + let backend = DbBackend::new(db.clone(), approval_voting.db_config); + let spawner = approval_voting.spawner.clone(); + spawner.spawn_blocking( + task_name, + Some(group_name), + Box::pin(async move { + if let Err(err) = run( + work_provider, + to_other_subsystems, + to_approval_distr, + approval_voting, + Box::new(RealAssignmentCriteria), + backend, + ) + .await + { + gum::error!(target: LOG_TARGET, ?err, "Approval voting worker stopped processing messages"); + }; + }), + ); + Ok(()) +} + // Handle actions is a function that accepts a set of instructions // and subsequently updates the underlying approvals_db in accordance // with the linear set of instructions passed in. Therefore, actions @@ -1338,8 +1444,19 @@ where // // returns `true` if any of the actions was a `Conclude` command. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_actions( - ctx: &mut Context, +async fn handle_actions< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, + spawn_handle: &Arc, state: &mut State, overlayed_db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -1371,7 +1488,8 @@ async fn handle_actions( // Note that chaining these iterators is O(n) as we must consume // the prior iterator. let next_actions: Vec = issue_approval( - ctx, + sender, + approval_voting_sender, state, overlayed_db, session_info_provider, @@ -1422,10 +1540,12 @@ async fn handle_actions( let validator_index = indirect_cert.validator; if distribute_assignment { - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeAssignment( - indirect_cert, - claimed_candidate_indices, - )); + approval_voting_sender.send_unbounded_message( + ApprovalDistributionMessage::DistributeAssignment( + indirect_cert, + claimed_candidate_indices, + ), + ); } match approvals_cache.get(&candidate_hash) { @@ -1440,7 +1560,8 @@ async fn handle_actions( actions_iter = new_actions.into_iter(); }, None => { - let ctx = &mut *ctx; + let sender = sender.clone(); + let spawn_handle = spawn_handle.clone(); currently_checking_set .insert_relay_block_hash( @@ -1449,7 +1570,8 @@ async fn handle_actions( relay_block_hash, async move { launch_approval( - ctx, + sender, + spawn_handle, metrics.clone(), session, candidate, @@ -1478,13 +1600,13 @@ async fn handle_actions( }) .with_string_tag("block-hash", format!("{:?}", block_hash)) .with_stage(jaeger::Stage::ApprovalChecking); - ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; + sender.send_message(ChainSelectionMessage::Approved(block_hash)).await; }, Action::BecomeActive => { *mode = Mode::Active; let (messages, next_actions) = distribution_messages_for_activation( - ctx, + sender, overlayed_db, state, delayed_approvals_timers, @@ -1492,7 +1614,7 @@ async fn handle_actions( ) .await?; - ctx.send_messages(messages.into_iter()).await; + approval_voting_sender.send_messages(messages.into_iter()).await; let next_actions: Vec = next_actions.into_iter().map(|v| v.clone()).chain(actions_iter).collect(); @@ -1566,8 +1688,8 @@ fn get_assignment_core_indices( } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn distribution_messages_for_activation( - ctx: &mut Context, +async fn distribution_messages_for_activation>( + sender: &mut Sender, db: &OverlayedBackend<'_, impl Backend>, state: &State, delayed_approvals_timers: &mut DelayedApprovalTimer, @@ -1607,9 +1729,30 @@ async fn distribution_messages_for_activation( hash: block_hash, number: block_entry.block_number(), parent_hash: block_entry.parent_hash(), - candidates: block_entry.candidates().iter().map(|(_, c_hash)| *c_hash).collect(), + candidates: block_entry + .candidates() + .iter() + .map(|(core_index, c_hash)| { + let candidate = db.load_candidate_entry(c_hash).ok().flatten(); + let group_index = candidate + .and_then(|entry| { + entry.approval_entry(&block_hash).map(|entry| entry.backing_group()) + }) + .unwrap_or_else(|| { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?c_hash, + "Missing candidate entry or approval entry", + ); + GroupIndex::default() + }); + (*c_hash, *core_index, group_index) + }) + .collect(), slot: block_entry.slot(), session: block_entry.session(), + vrf_story: block_entry.relay_vrf_story(), }); let mut signatures_queued = HashSet::new(); for (core_index, candidate_hash) in block_entry.candidates() { @@ -1672,7 +1815,7 @@ async fn distribution_messages_for_activation( let ExtendedSessionInfo { ref executor_params, .. } = match get_extended_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.block_hash(), block_entry.session(), ) @@ -1770,9 +1913,16 @@ async fn distribution_messages_for_activation( } // Handle an incoming signal from the overseer. Returns true if execution should conclude. -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_from_overseer( - ctx: &mut Context, +async fn handle_from_overseer< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, + spawn_handle: &Arc, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -1790,7 +1940,8 @@ async fn handle_from_overseer( jaeger::PerLeafSpan::new(activated.span, "approval-voting"); state.spans.insert(head, approval_voting_span); match import::handle_new_head( - ctx, + sender, + approval_voting_sender, state, db, session_info_provider, @@ -1872,35 +2023,33 @@ 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 @@ -1910,7 +2059,7 @@ async fn handle_from_overseer( .with_stage(jaeger::Stage::ApprovalChecking) .with_string_tag("leaf", format!("{:?}", target)); match handle_approved_ancestor( - ctx, + sender, db, target, lower_bound, @@ -1933,7 +2082,14 @@ async fn handle_from_overseer( }, ApprovalVotingMessage::GetApprovalSignaturesForCandidate(candidate_hash, tx) => { metrics.on_candidate_signatures_request(); - get_approval_signatures_for_candidate(ctx, db, candidate_hash, tx).await?; + get_approval_signatures_for_candidate( + approval_voting_sender.clone(), + spawn_handle, + db, + candidate_hash, + tx, + ) + .await?; Vec::new() }, }, @@ -1947,8 +2103,11 @@ async fn handle_from_overseer( /// This involves an unbounded message send to approval-distribution, the caller has to ensure that /// calls to this function are infrequent and bounded. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn get_approval_signatures_for_candidate( - ctx: &mut Context, +async fn get_approval_signatures_for_candidate< + Sender: SubsystemSender, +>( + mut sender: Sender, + spawn_handle: &Arc, db: &OverlayedBackend<'_, impl Backend>, candidate_hash: CandidateHash, tx: oneshot::Sender, ValidatorSignature)>>, @@ -2007,7 +2166,6 @@ async fn get_approval_signatures_for_candidate( } } - let mut sender = ctx.sender().clone(); let get_approvals = async move { let (tx_distribution, rx_distribution) = oneshot::channel(); sender.send_unbounded_message(ApprovalDistributionMessage::GetApprovalSignatures( @@ -2087,12 +2245,17 @@ async fn get_approval_signatures_for_candidate( ?candidate_hash, "Spawning task for fetching signatures from approval-distribution" ); - ctx.spawn("get-approval-signatures", Box::pin(get_approvals)) + spawn_handle.spawn( + "get-approval-signatures", + Some("approval-voting-subsystem"), + Box::pin(get_approvals), + ); + Ok(()) } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_approved_ancestor( - ctx: &mut Context, +async fn handle_approved_ancestor>( + sender: &mut Sender, db: &OverlayedBackend<'_, impl Backend>, target: Hash, lower_bound: BlockNumber, @@ -2112,7 +2275,7 @@ async fn handle_approved_ancestor( let target_number = { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockNumber(target, tx)).await; + sender.send_message(ChainApiMessage::BlockNumber(target, tx)).await; match rx.await { Ok(Ok(Some(n))) => n, @@ -2133,12 +2296,13 @@ async fn handle_approved_ancestor( let ancestry = if target_number > lower_bound + 1 { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::Ancestors { - hash: target, - k: (target_number - (lower_bound + 1)) as usize, - response_channel: tx, - }) - .await; + sender + .send_message(ChainApiMessage::Ancestors { + hash: target, + k: (target_number - (lower_bound + 1)) as usize, + response_channel: tx, + }) + .await; match rx.await { Ok(Ok(a)) => a, @@ -2439,29 +2603,30 @@ fn schedule_wakeup_action( maybe_action } -async fn check_and_import_assignment( +async fn import_assignment( sender: &mut Sender, state: &State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, - assignment: IndirectAssignmentCertV2, - candidate_indices: CandidateBitfield, + checked_assignment: CheckedIndirectAssignment, ) -> SubsystemResult<(AssignmentCheckResult, Vec)> where Sender: SubsystemSender, { let tick_now = state.clock.tick_now(); - - let mut check_and_import_assignment_span = state + let assignment = checked_assignment.assignment(); + let candidate_indices = checked_assignment.candidate_indices(); + let tranche = checked_assignment.tranche(); + let mut import_assignment_span = state .spans .get(&assignment.block_hash) - .map(|span| span.child("check-and-import-assignment")) - .unwrap_or_else(|| jaeger::Span::new(assignment.block_hash, "check-and-import-assignment")) + .map(|span| span.child("import-assignment")) + .unwrap_or_else(|| jaeger::Span::new(assignment.block_hash, "import-assignment")) .with_relay_parent(assignment.block_hash) .with_stage(jaeger::Stage::ApprovalChecking); for candidate_index in candidate_indices.iter_ones() { - check_and_import_assignment_span.add_uint_tag("candidate-index", candidate_index as u64); + import_assignment_span.add_uint_tag("candidate-index", candidate_index as u64); } let block_entry = match db.load_block_entry(&assignment.block_hash)? { @@ -2514,8 +2679,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 +2707,23 @@ where )), // no candidate at core. }; - check_and_import_assignment_span + import_assignment_span .add_string_tag("candidate-hash", format!("{:?}", assigned_candidate_hash)); - check_and_import_assignment_span.add_string_tag( + import_assignment_span.add_string_tag( "traceID", format!("{:?}", jaeger::hash_to_trace_identifier(assigned_candidate_hash.0)), ); - let approval_entry = match candidate_entry.approval_entry_mut(&assignment.block_hash) { - Some(a) => a, - None => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::Internal( - assignment.block_hash, - assigned_candidate_hash, - )), - Vec::new(), + if candidate_entry.approval_entry_mut(&assignment.block_hash).is_none() { + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::Internal( + assignment.block_hash, + assigned_candidate_hash, )), + Vec::new(), + )); }; - backing_groups.push(approval_entry.backing_group()); claimed_core_indices.push(claimed_core_index); assigned_candidate_hashes.push(assigned_candidate_hash); } @@ -2579,42 +2739,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 +2771,7 @@ where }; is_duplicate &= approval_entry.is_assigned(assignment.validator); approval_entry.import_assignment(tranche, assignment.validator, tick_now); - check_and_import_assignment_span.add_uint_tag("tranche", tranche as u64); + import_assignment_span.add_uint_tag("tranche", tranche as u64); // We've imported a new assignment, so we need to schedule a wake-up for when that might // no-show. @@ -2704,30 +2828,28 @@ where Ok((res, actions)) } -async fn check_and_import_approval( +async fn import_approval( sender: &mut Sender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, metrics: &Metrics, - approval: IndirectSignedApprovalVoteV2, - with_response: impl FnOnce(ApprovalCheckResult) -> T, + approval: CheckedIndirectSignedApprovalVote, wakeups: &Wakeups, -) -> SubsystemResult<(Vec, T)> +) -> SubsystemResult<(Vec, ApprovalCheckResult)> where Sender: SubsystemSender, { macro_rules! respond_early { ($e: expr) => {{ - let t = with_response($e); - return Ok((Vec::new(), t)) + return Ok((Vec::new(), $e)) }}; } let mut span = state .spans .get(&approval.block_hash) - .map(|span| span.child("check-and-import-approval")) - .unwrap_or_else(|| jaeger::Span::new(approval.block_hash, "check-and-import-approval")) + .map(|span| span.child("import-approval")) + .unwrap_or_else(|| jaeger::Span::new(approval.block_hash, "import-approval")) .with_string_fmt_debug_tag("candidate-index", approval.candidate_indices.clone()) .with_relay_parent(approval.block_hash) .with_stage(jaeger::Stage::ApprovalChecking); @@ -2774,67 +2896,11 @@ where ), ); - { - let session_info = match get_session_info( - session_info_provider, - sender, - approval.block_hash, - block_entry.session(), - ) - .await - { - Some(s) => s, - None => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownSessionIndex( - block_entry.session() - ),)) - }, - }; - - let pubkey = match session_info.validators.get(approval.validator) { - Some(k) => k, - None => respond_early!(ApprovalCheckResult::Bad( - ApprovalCheckError::InvalidValidatorIndex(approval.validator), - )), - }; - - gum::trace!( - target: LOG_TARGET, - "Received approval for num_candidates {:}", - approval.candidate_indices.count_ones() - ); - - let candidate_hashes: Vec = - approved_candidates_info.iter().map(|candidate| candidate.1).collect(); - // Signature check: - match DisputeStatement::Valid( - ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes.clone()), - ) - .check_signature( - &pubkey, - if let Some(candidate_hash) = candidate_hashes.first() { - *candidate_hash - } else { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidValidatorIndex( - approval.validator - ),)) - }, - block_entry.session(), - &approval.signature, - ) { - Err(_) => { - gum::error!( - target: LOG_TARGET, - "Error while checking signature {:}", - approval.candidate_indices.count_ones() - ); - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidSignature( - approval.validator - ),)) - }, - Ok(()) => {}, - }; - } + gum::trace!( + target: LOG_TARGET, + "Received approval for num_candidates {:}", + approval.candidate_indices.count_ones() + ); let mut actions = Vec::new(); for (approval_candidate_index, approved_candidate_hash) in approved_candidates_info { @@ -2898,9 +2964,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 +3240,8 @@ fn should_trigger_assignment( } } -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn process_wakeup( - ctx: &mut Context, +async fn process_wakeup>( + sender: &mut Sender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -3209,7 +3272,7 @@ async fn process_wakeup( let ExtendedSessionInfo { ref session_info, ref executor_params, .. } = match get_extended_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.block_hash(), block_entry.session(), ) @@ -3351,7 +3414,7 @@ async fn process_wakeup( // Note that this function also schedules a wakeup as necessary. actions.extend( advance_approval_state( - ctx.sender(), + sender, state, db, session_info_provider, @@ -3372,8 +3435,14 @@ async fn process_wakeup( // spawned. When the background work is no longer needed, the `AbortHandle` should be dropped // to cancel the background work and any requests it has spawned. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn launch_approval( - ctx: &mut Context, +async fn launch_approval< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender, +>( + mut sender: Sender, + spawn_handle: Arc, metrics: Metrics, session_index: SessionIndex, candidate: CandidateReceipt, @@ -3424,14 +3493,15 @@ async fn launch_approval( .with_stage(jaeger::Stage::ApprovalChecking); let timer = metrics.time_recover_and_approve(); - ctx.send_message(AvailabilityRecoveryMessage::RecoverAvailableData( - candidate.clone(), - session_index, - Some(backing_group), - core_index, - a_tx, - )) - .await; + sender + .send_message(AvailabilityRecoveryMessage::RecoverAvailableData( + candidate.clone(), + session_index, + Some(backing_group), + core_index, + a_tx, + )) + .await; let request_validation_result_span = span .child("request-validation-result") @@ -3440,15 +3510,18 @@ async fn launch_approval( .with_string_tag("block-hash", format!("{:?}", block_hash)) .with_stage(jaeger::Stage::ApprovalChecking); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ValidationCodeByHash(candidate.descriptor.validation_code_hash, code_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ValidationCodeByHash( + candidate.descriptor.validation_code_hash, + code_tx, + ), + )) + .await; let candidate = candidate.clone(); let metrics_guard = StaleGuard(Some(metrics)); - let mut sender = ctx.sender().clone(); let background = async move { // Force the move of the timer into the background task. let _timer = timer; @@ -3578,14 +3651,19 @@ async fn launch_approval( } }; let (background, remote_handle) = background.remote_handle(); - ctx.spawn("approval-checks", Box::pin(background)).map(move |()| remote_handle) + spawn_handle.spawn("approval-checks", Some("approval-voting-subsystem"), Box::pin(background)); + Ok(remote_handle) } // Issue and import a local approval vote. Should only be invoked after approval checks // have been done. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn issue_approval( - ctx: &mut Context, +async fn issue_approval< + Sender: SubsystemSender, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -3664,7 +3742,7 @@ async fn issue_approval( let session_info = match get_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.parent_hash(), block_entry.session(), ) @@ -3706,7 +3784,7 @@ async fn issue_approval( ); let actions = advance_approval_state( - ctx.sender(), + sender, state, db, session_info_provider, @@ -3723,7 +3801,8 @@ async fn issue_approval( db, session_info_provider, state, - ctx, + sender, + approval_voting_sender, block_hash, validator_index, metrics, @@ -3742,11 +3821,15 @@ async fn issue_approval( // Create signature for the approved candidates pending signatures #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn maybe_create_signature( +async fn maybe_create_signature< + Sender: SubsystemSender, + ADSender: SubsystemSender, +>( db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, state: &State, - ctx: &mut Context, + sender: &mut Sender, + approval_voting_sender: &mut ADSender, block_hash: Hash, validator_index: ValidatorIndex, metrics: &Metrics, @@ -3765,7 +3848,7 @@ async fn maybe_create_signature( }; let approval_params = state - .get_approval_voting_params_or_default(ctx, block_entry.session(), block_hash) + .get_approval_voting_params_or_default(sender, block_entry.session(), block_hash) .await .unwrap_or_default(); @@ -3785,7 +3868,7 @@ async fn maybe_create_signature( let session_info = match get_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.parent_hash(), block_entry.session(), ) @@ -3866,7 +3949,7 @@ async fn maybe_create_signature( metrics.on_approval_produced(); - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( + approval_voting_sender.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( IndirectSignedApprovalVoteV2 { block_hash: block_entry.block_hash(), candidate_indices: candidates_indices, @@ -3907,7 +3990,7 @@ fn issue_local_invalid_statement( candidate_hash: CandidateHash, candidate: CandidateReceipt, ) where - Sender: overseer::ApprovalVotingSenderTrait, + Sender: SubsystemSender, { // We need to send an unbounded message here to break a cycle: // DisputeCoordinatorMessage::IssueLocalStatement -> diff --git a/polkadot/node/core/approval-voting/src/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..65aa4f894c23eba75b58edafdd520bb2342594a8 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -35,14 +35,15 @@ use polkadot_node_subsystem::{ messages::{ AllMessages, ApprovalVotingMessage, AssignmentCheckResult, AvailabilityRecoveryMessage, }, - ActiveLeavesUpdate, + ActiveLeavesUpdate, SubsystemContext, }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; -use polkadot_overseer::HeadSupportsParachains; +use polkadot_overseer::{HeadSupportsParachains, SpawnGlue}; use polkadot_primitives::{ - ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, - Id as ParaId, IndexedVec, NodeFeatures, ValidationCode, ValidatorSignature, + ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, DisputeStatement, GroupIndex, + Header, Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, + ValidatorSignature, }; use std::{cmp::max, time::Duration}; @@ -139,8 +140,8 @@ impl HeadSupportsParachains for MockSupportsParachains { } } -fn slot_to_tick(t: impl Into) -> crate::time::Tick { - crate::time::slot_number_to_tick(SLOT_DURATION_MILLIS, t.into()) +fn slot_to_tick(t: impl Into) -> Tick { + slot_number_to_tick(SLOT_DURATION_MILLIS, t.into()) } #[derive(Default, Clone)] @@ -535,7 +536,7 @@ impl Default for HarnessConfig { struct TestHarness { virtual_overseer: VirtualOverseer, - clock: Box, + clock: Arc, sync_oracle_handle: TestSyncOracleHandle, } @@ -549,8 +550,8 @@ fn test_harness>( config; let pool = sp_core::testing::TaskExecutor::new(); - let (context, virtual_overseer) = - polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + let (mut context, virtual_overseer) = + polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); let keystore = LocalKeystore::in_memory(); let _ = keystore.sr25519_generate_new( @@ -558,12 +559,14 @@ fn test_harness>( Some(&Sr25519Keyring::Alice.to_seed()), ); - let clock = Box::new(clock); + let clock = Arc::new(clock); let db = kvdb_memorydb::create(test_constants::NUM_COLUMNS); let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[]); - + let sender = context.sender().clone(); let subsystem = run( context, + sender.clone(), + sender.clone(), ApprovalVotingSubsystem::with_config_and_clock( Config { col_approval_data: test_constants::TEST_CONFIG.col_approval_data, @@ -574,6 +577,7 @@ fn test_harness>( sync_oracle, Metrics::default(), clock.clone(), + Arc::new(SpawnGlue(pool)), ), assignment_criteria, backend, @@ -647,7 +651,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 +670,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 +693,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 +725,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 +735,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 +1136,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 +1158,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 +1176,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 +1252,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 +1293,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 +1358,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 +1381,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 +1430,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 +1441,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 +1495,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 +1541,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 +1552,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 +1606,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 +1694,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 +1719,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 +1778,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 +1896,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 +1936,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 +2006,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 +2061,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 +2135,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 +2305,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 +2330,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 +2357,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 +2470,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 +2493,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 +2751,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 +2765,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 +3220,8 @@ where F1: 'static + Fn(ValidatorIndex) -> Result + Send - + Sync, + + Sync + + Clone, F2: Fn(Tick) -> bool, { let TriggersAssignmentConfig { @@ -3384,7 +3250,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 +3311,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 +3325,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 +3630,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 +3666,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 +3681,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 +3811,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 +3838,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 +3854,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 +4117,7 @@ async fn handle_approval_on_max_coalesce_count( async fn handle_approval_on_max_wait_time( virtual_overseer: &mut VirtualOverseer, candidate_indices: Vec, - clock: Box, + clock: Arc, ) { const TICK_NOW_BEGIN: u64 = 1; const MAX_COALESCE_COUNT: u32 = 3; @@ -4543,7 +4415,7 @@ async fn build_chain_with_two_blocks_with_one_candidate_each( async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( virtual_overseer: &mut VirtualOverseer, store: TestStore, - clock: &Box, + clock: &Arc, sync_oracle_handle: TestSyncOracleHandle, ) { assert_matches!( @@ -4992,7 +4864,6 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { })); } ); - assert_matches!( overseer_recv(&mut virtual_overseer).await, AllMessages::RuntimeApi( @@ -5058,7 +4929,7 @@ fn test_gathering_assignments_statements() { let mut state = State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|_| Ok(0))), spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( @@ -5153,7 +5024,7 @@ fn test_observe_assignment_gathering_status() { let mut state = State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|_| Ok(0))), spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 5bcd47a2434c71f3a6dd88e4c0456f467817deb7..f276321c87ed08f2f0d9f5e67901b7ce8492d4c5 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -100,9 +100,9 @@ use polkadot_node_subsystem_util::{ executor_params_at_relay_parent, request_from_runtime, request_session_index_for_child, request_validator_groups, request_validators, runtime::{ - self, prospective_parachains_mode, request_min_backing_votes, ProspectiveParachainsMode, + self, fetch_claim_queue, prospective_parachains_mode, request_min_backing_votes, + ClaimQueueSnapshot, ProspectiveParachainsMode, }, - vstaging::{fetch_claim_queue, ClaimQueueSnapshot}, Validator, }; use polkadot_primitives::{ @@ -121,7 +121,7 @@ use polkadot_statement_table::{ Config as TableConfig, Context as TableContextTrait, Table, }; use sp_keystore::KeystorePtr; -use util::{runtime::request_node_features, vstaging::get_disabled_validators_with_fallback}; +use util::runtime::{get_disabled_validators_with_fallback, request_node_features}; mod error; diff --git a/polkadot/node/core/candidate-validation/Cargo.toml b/polkadot/node/core/candidate-validation/Cargo.toml index 13ab3e3fba50af4db1dc13497729f6c932ab7348..fcacc38cae65cb6b6bd8fe56a247c38a06fb05a1 100644 --- a/polkadot/node/core/candidate-validation/Cargo.toml +++ b/polkadot/node/core/candidate-validation/Cargo.toml @@ -17,7 +17,6 @@ gum = { workspace = true, default-features = true } sp-keystore = { workspace = true } sp-application-crypto = { workspace = true } -sp-maybe-compressed-blob = { workspace = true, default-features = true } codec = { features = ["bit-vec", "derive"], workspace = true } polkadot-primitives = { workspace = true, default-features = true } @@ -36,5 +35,6 @@ sp-keyring = { workspace = true, default-features = true } futures = { features = ["thread-pool"], workspace = true } assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } polkadot-primitives-test-helpers = { workspace = true } diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 1985964ebc512ea8bdb53631896643f9dcb85735..a9732e93441452a68dfe80cedc1502b23e3b96ba 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -27,9 +27,7 @@ use polkadot_node_core_pvf::{ InternalValidationError, InvalidCandidate as WasmInvalidCandidate, PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, }; -use polkadot_node_primitives::{ - BlockData, InvalidCandidate, PoV, ValidationResult, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT, -}; +use polkadot_node_primitives::{InvalidCandidate, PoV, ValidationResult}; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ @@ -41,9 +39,7 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_util as util; use polkadot_overseer::ActiveLeavesUpdate; -use polkadot_parachain_primitives::primitives::{ - ValidationParams, ValidationResult as WasmValidationResult, -}; +use polkadot_parachain_primitives::primitives::ValidationResult as WasmValidationResult; use polkadot_primitives::{ executor_params::{ DEFAULT_APPROVAL_EXECUTION_TIMEOUT, DEFAULT_BACKING_EXECUTION_TIMEOUT, @@ -51,7 +47,7 @@ use polkadot_primitives::{ }, AuthorityDiscoveryId, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateReceipt, ExecutorParams, Hash, OccupiedCoreAssumption, PersistedValidationData, - PvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, + PvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; use sp_application_crypto::{AppCrypto, ByteArray}; use sp_keystore::KeystorePtr; @@ -431,14 +427,15 @@ where .iter() .any(|v| keystore.has_keys(&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)])); - let is_present_authority = session_info - .discovery_keys + // We could've checked discovery_keys but on Kusama validators.len() < discovery_keys.len(). + let is_present_validator = session_info + .validators .iter() - .any(|v| keystore.has_keys(&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)])); + .any(|v| keystore.has_keys(&[(v.to_raw_vec(), ValidatorId::ID)])); // There is still a chance to be a previous session authority, but this extra work does not // affect the finalization. - is_past_present_or_future_authority && !is_present_authority + is_past_present_or_future_authority && !is_present_validator } // Sends PVF with unknown code hashes to the validation host returning the list of code hashes sent. @@ -504,21 +501,12 @@ where continue; }; - let pvf = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => PvfPrepData::from_code( - code.into_owned(), - executor_params.clone(), - timeout, - PrepareJobKind::Prechecking, - ), - Err(e) => { - gum::debug!(target: LOG_TARGET, err=?e, "cannot decompress validation code"); - continue - }, - }; + let pvf = PvfPrepData::from_code( + validation_code.0, + executor_params.clone(), + timeout, + PrepareJobKind::Prechecking, + ); active_pvfs.push(pvf); processed_code_hashes.push(code_hash); @@ -651,21 +639,12 @@ where let timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Precheck); - let pvf = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => PvfPrepData::from_code( - code.into_owned(), - executor_params, - timeout, - PrepareJobKind::Prechecking, - ), - Err(e) => { - gum::debug!(target: LOG_TARGET, err=?e, "precheck: cannot decompress validation code"); - return PreCheckOutcome::Invalid - }, - }; + let pvf = PvfPrepData::from_code( + validation_code.0, + executor_params, + timeout, + PrepareJobKind::Prechecking, + ); match validation_backend.precheck_pvf(pvf).await { Ok(_) => PreCheckOutcome::Valid, @@ -873,41 +852,7 @@ async fn validate_candidate_exhaustive( return Ok(ValidationResult::Invalid(e)) } - let raw_validation_code = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => code, - Err(e) => { - gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (validation code)"); - - // Code already passed pre-checking, if decompression fails now this most likely means - // some local corruption happened. - return Err(ValidationFailed("Code decompression failed".to_string())) - }, - }; - metrics.observe_code_size(raw_validation_code.len()); - - metrics.observe_pov_size(pov.block_data.0.len(), true); - let raw_block_data = - match sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT) { - Ok(block_data) => BlockData(block_data.to_vec()), - Err(e) => { - gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (PoV code)"); - - // If the PoV is invalid, the candidate certainly is. - return Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure)) - }, - }; - metrics.observe_pov_size(raw_block_data.0.len(), false); - - let params = ValidationParams { - parent_head: persisted_validation_data.parent_head.clone(), - block_data: raw_block_data, - relay_parent_number: persisted_validation_data.relay_parent_number, - relay_parent_storage_root: persisted_validation_data.relay_parent_storage_root, - }; - + let persisted_validation_data = Arc::new(persisted_validation_data); let result = match exec_kind { // Retry is disabled to reduce the chance of nondeterministic blocks getting backed and // honest backers getting slashed. @@ -915,7 +860,7 @@ async fn validate_candidate_exhaustive( let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind); let pvf = PvfPrepData::from_code( - raw_validation_code.to_vec(), + validation_code.0, executor_params, prep_timeout, PrepareJobKind::Compilation, @@ -925,7 +870,8 @@ async fn validate_candidate_exhaustive( .validate_candidate( pvf, exec_timeout, - params.encode(), + persisted_validation_data.clone(), + pov, polkadot_node_core_pvf::Priority::Normal, ) .await @@ -933,9 +879,10 @@ async fn validate_candidate_exhaustive( PvfExecKind::Approval => validation_backend .validate_candidate_with_retry( - raw_validation_code.to_vec(), + validation_code.0, pvf_exec_timeout(&executor_params, exec_kind), - params, + persisted_validation_data.clone(), + pov, executor_params, PVF_APPROVAL_EXECUTION_RETRY_DELAY, polkadot_node_core_pvf::Priority::Critical, @@ -961,6 +908,8 @@ async fn validate_candidate_exhaustive( Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)), Err(ValidationError::Invalid(WasmInvalidCandidate::WorkerReportedInvalid(e))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e))), + Err(ValidationError::Invalid(WasmInvalidCandidate::PoVDecompressionFailure)) => + Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure)), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError( "ambiguous worker death".to_string(), @@ -1007,7 +956,7 @@ async fn validate_candidate_exhaustive( // invalid. Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch)) } else { - Ok(ValidationResult::Valid(outputs, persisted_validation_data)) + Ok(ValidationResult::Valid(outputs, (*persisted_validation_data).clone())) } }, } @@ -1020,7 +969,8 @@ trait ValidationBackend { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - encoded_params: Vec, + pvd: Arc, + pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result; @@ -1035,9 +985,10 @@ trait ValidationBackend { /// preparation. async fn validate_candidate_with_retry( &mut self, - raw_validation_code: Vec, + code: Vec, exec_timeout: Duration, - params: ValidationParams, + pvd: Arc, + pov: Arc, executor_params: ExecutorParams, retry_delay: Duration, // The priority for the preparation job. @@ -1046,7 +997,7 @@ trait ValidationBackend { let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); // Construct the PVF a single time, since it is an expensive operation. Cloning it is cheap. let pvf = PvfPrepData::from_code( - raw_validation_code, + code, executor_params, prep_timeout, PrepareJobKind::Compilation, @@ -1057,7 +1008,13 @@ trait ValidationBackend { // Use `Priority::Critical` as finality trumps parachain liveliness. let mut validation_result = self - .validate_candidate(pvf.clone(), exec_timeout, params.encode(), prepare_priority) + .validate_candidate( + pvf.clone(), + exec_timeout, + pvd.clone(), + pov.clone(), + prepare_priority, + ) .await; if validation_result.is_ok() { return validation_result @@ -1130,10 +1087,14 @@ trait ValidationBackend { validation_result ); - // Encode the params again when re-trying. We expect the retry case to be relatively - // rare, and we want to avoid unconditionally cloning data. validation_result = self - .validate_candidate(pvf.clone(), new_timeout, params.encode(), prepare_priority) + .validate_candidate( + pvf.clone(), + new_timeout, + pvd.clone(), + pov.clone(), + prepare_priority, + ) .await; } } @@ -1153,13 +1114,13 @@ impl ValidationBackend for ValidationHost { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - encoded_params: Vec, + pvd: Arc, + pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { let (tx, rx) = oneshot::channel(); - if let Err(err) = - self.execute_pvf(pvf, exec_timeout, encoded_params, prepare_priority, tx).await + if let Err(err) = self.execute_pvf(pvf, exec_timeout, pvd, pov, prepare_priority, tx).await { return Err(InternalValidationError::HostCommunication(format!( "cannot send pvf to the validation host, it might have shut down: {:?}", diff --git a/polkadot/node/core/candidate-validation/src/metrics.rs b/polkadot/node/core/candidate-validation/src/metrics.rs index 28fc957ddb1a7acd0b5c67f4a52e70bf5e56f734..1459907aa5999980dd82ec0e08a5839959167cef 100644 --- a/polkadot/node/core/candidate-validation/src/metrics.rs +++ b/polkadot/node/core/candidate-validation/src/metrics.rs @@ -23,8 +23,6 @@ pub(crate) struct MetricsInner { pub(crate) validate_from_chain_state: prometheus::Histogram, pub(crate) validate_from_exhaustive: prometheus::Histogram, pub(crate) validate_candidate_exhaustive: prometheus::Histogram, - pub(crate) pov_size: prometheus::HistogramVec, - pub(crate) code_size: prometheus::Histogram, } /// Candidate validation metrics. @@ -70,21 +68,6 @@ impl Metrics { .as_ref() .map(|metrics| metrics.validate_candidate_exhaustive.start_timer()) } - - pub fn observe_code_size(&self, code_size: usize) { - if let Some(metrics) = &self.0 { - metrics.code_size.observe(code_size as f64); - } - } - - pub fn observe_pov_size(&self, pov_size: usize, compressed: bool) { - if let Some(metrics) = &self.0 { - metrics - .pov_size - .with_label_values(&[if compressed { "true" } else { "false" }]) - .observe(pov_size as f64); - } - } } impl metrics::Metrics for Metrics { @@ -121,33 +104,6 @@ impl metrics::Metrics for Metrics { ))?, registry, )?, - pov_size: prometheus::register( - prometheus::HistogramVec::new( - prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_pov_size", - "The compressed and decompressed size of the proof of validity of a candidate", - ) - .buckets( - prometheus::exponential_buckets(16384.0, 2.0, 10) - .expect("arguments are always valid; qed"), - ), - &["compressed"], - )?, - registry, - )?, - code_size: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_code_size", - "The size of the decompressed WASM validation blob used for checking a candidate", - ) - .buckets( - prometheus::exponential_buckets(16384.0, 2.0, 10) - .expect("arguments are always valid; qed"), - ), - )?, - registry, - )?, }; Ok(Metrics(Some(metrics))) } diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 86d855f78b454dcea2e8ac50fb77ea6ed2afe727..0dcd84bab6cfe965613bdd6571e6fe8ce53ceaef 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -20,17 +20,17 @@ use super::*; use assert_matches::assert_matches; use futures::executor; use polkadot_node_core_pvf::PrepareError; +use polkadot_node_primitives::{BlockData, VALIDATION_CODE_BOMB_LIMIT}; use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; use polkadot_primitives::{ - CoreIndex, GroupIndex, HeadData, Id as ParaId, IndexedVec, SessionInfo, UpwardMessage, - ValidatorId, ValidatorIndex, + CoreIndex, GroupIndex, HeadData, Id as ParaId, SessionInfo, UpwardMessage, ValidatorId, }; use polkadot_primitives_test_helpers::{ dummy_collator, dummy_collator_signature, dummy_hash, make_valid_candidate_descriptor, }; -use sp_core::testing::TaskExecutor; +use sp_core::{sr25519::Public, testing::TaskExecutor}; use sp_keyring::Sr25519Keyring; use sp_keystore::{testing::MemoryKeystore, Keystore}; @@ -385,7 +385,8 @@ impl ValidationBackend for MockValidateCandidateBackend { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { // This is expected to panic if called more times than expected, indicating an error in the @@ -950,115 +951,6 @@ fn compressed_code_works() { assert_matches!(v, Ok(ValidationResult::Valid(_, _))); } -#[test] -fn code_decompression_failure_is_error() { - let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() }; - let pov = PoV { block_data: BlockData(vec![1; 32]) }; - let head_data = HeadData(vec![1, 1, 1]); - - let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; - let validation_code = - sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1) - .map(ValidationCode) - .unwrap(); - - let descriptor = make_valid_candidate_descriptor( - ParaId::from(1_u32), - dummy_hash(), - validation_data.hash(), - pov.hash(), - validation_code.hash(), - head_data.hash(), - dummy_hash(), - Sr25519Keyring::Alice, - ); - - let validation_result = WasmValidationResult { - head_data, - new_validation_code: None, - upward_messages: Default::default(), - horizontal_messages: Default::default(), - processed_downward_messages: 0, - hrmp_watermark: 0, - }; - - let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() }; - - let pool = TaskExecutor::new(); - let (_ctx, _ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let v = executor::block_on(validate_candidate_exhaustive( - MockValidateCandidateBackend::with_hardcoded_result(Ok(validation_result)), - validation_data, - validation_code, - candidate_receipt, - Arc::new(pov), - ExecutorParams::default(), - PvfExecKind::Backing, - &Default::default(), - )); - - assert_matches!(v, Err(_)); -} - -#[test] -fn pov_decompression_failure_is_invalid() { - let validation_data = - PersistedValidationData { max_pov_size: POV_BOMB_LIMIT as u32, ..Default::default() }; - let head_data = HeadData(vec![1, 1, 1]); - - let raw_block_data = vec![2u8; POV_BOMB_LIMIT + 1]; - let pov = sp_maybe_compressed_blob::compress(&raw_block_data, POV_BOMB_LIMIT + 1) - .map(|raw| PoV { block_data: BlockData(raw) }) - .unwrap(); - - let validation_code = ValidationCode(vec![2; 16]); - - let descriptor = make_valid_candidate_descriptor( - ParaId::from(1_u32), - dummy_hash(), - validation_data.hash(), - pov.hash(), - validation_code.hash(), - head_data.hash(), - dummy_hash(), - Sr25519Keyring::Alice, - ); - - let validation_result = WasmValidationResult { - head_data, - new_validation_code: None, - upward_messages: Default::default(), - horizontal_messages: Default::default(), - processed_downward_messages: 0, - hrmp_watermark: 0, - }; - - let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() }; - - let pool = TaskExecutor::new(); - let (_ctx, _ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let v = executor::block_on(validate_candidate_exhaustive( - MockValidateCandidateBackend::with_hardcoded_result(Ok(validation_result)), - validation_data, - validation_code, - candidate_receipt, - Arc::new(pov), - ExecutorParams::default(), - PvfExecKind::Backing, - &Default::default(), - )); - - assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure))); -} - struct MockPreCheckBackend { result: Result<(), PrepareError>, } @@ -1075,7 +967,8 @@ impl ValidationBackend for MockPreCheckBackend { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { unreachable!() @@ -1149,70 +1042,6 @@ fn precheck_works() { executor::block_on(test_fut); } -#[test] -fn precheck_invalid_pvf_blob_compression() { - let relay_parent = [3; 32].into(); - - let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; - let validation_code = - sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1) - .map(ValidationCode) - .unwrap(); - let validation_code_hash = validation_code.hash(); - - let pool = TaskExecutor::new(); - let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let (check_fut, check_result) = precheck_pvf( - ctx.sender(), - MockPreCheckBackend::with_hardcoded_result(Ok(())), - relay_parent, - validation_code_hash, - ) - .remote_handle(); - - let test_fut = async move { - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - rp, - RuntimeApiRequest::ValidationCodeByHash( - vch, - tx - ), - )) => { - assert_eq!(vch, validation_code_hash); - assert_eq!(rp, relay_parent); - - let _ = tx.send(Ok(Some(validation_code.clone()))); - } - ); - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionIndexForChild(tx)) - ) => { - tx.send(Ok(1u32.into())).unwrap(); - } - ); - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionExecutorParams(_, tx)) - ) => { - tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); - } - ); - assert_matches!(check_result.await, PreCheckOutcome::Invalid); - }; - - let test_fut = future::join(test_fut, check_fut); - executor::block_on(test_fut); -} - #[test] fn precheck_properly_classifies_outcomes() { let inner = |prepare_result, precheck_outcome| { @@ -1292,7 +1121,8 @@ impl ValidationBackend for MockHeadsUp { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { unreachable!() @@ -1363,10 +1193,10 @@ fn dummy_candidate_backed( ) } -fn dummy_session_info(discovery_keys: Vec) -> SessionInfo { +fn dummy_session_info(keys: Vec) -> SessionInfo { SessionInfo { - validators: IndexedVec::::from(vec![]), - discovery_keys, + validators: keys.iter().cloned().map(Into::into).collect(), + discovery_keys: keys.iter().cloned().map(Into::into).collect(), assignment_keys: vec![], validator_groups: Default::default(), n_cores: 4u32, @@ -1415,7 +1245,7 @@ fn maybe_prepare_validation_golden_path() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); @@ -1533,7 +1363,7 @@ fn maybe_prepare_validation_resets_state_on_a_new_session() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 2); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); }; @@ -1679,7 +1509,7 @@ fn maybe_prepare_validation_does_not_prepare_pvfs_if_not_a_validator_in_the_next ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); }; @@ -1726,7 +1556,7 @@ fn maybe_prepare_validation_does_not_prepare_pvfs_if_a_validator_in_the_current_ ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Alice.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Alice.public()])))); } ); }; @@ -1773,7 +1603,7 @@ fn maybe_prepare_validation_prepares_a_limited_number_of_pvfs() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index 5f86da87f21ca060cfe14de536b39e652d12b7b1..5096fe5e6891cd6de7c93036837928fce6cb748a 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -1351,6 +1351,12 @@ impl Initialized { } } for validator_index in new_state.votes().invalid.keys() { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?validator_index, + "Disabled offchain for voting invalid against a valid candidate", + ); self.offchain_disabled_validators .insert_against_valid(session, *validator_index); } @@ -1375,6 +1381,13 @@ impl Initialized { } for (validator_index, (kind, _sig)) in new_state.votes().valid.raw() { let is_backer = kind.is_backing(); + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?validator_index, + ?is_backer, + "Disabled offchain for voting valid for an invalid candidate", + ); self.offchain_disabled_validators.insert_for_invalid( session, *validator_index, diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index daa384b36ffbaf2c8d3c004e35eb537a3c33e4c0..34d9ddf3a97c0be0c7f6e3b43cbc3a1b126b2423 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -478,6 +478,18 @@ pub fn is_potential_spam( let all_invalid_votes_disabled = vote_state.invalid_votes_all_disabled(is_disabled); let ignore_disabled = !is_confirmed && all_invalid_votes_disabled; + gum::trace!( + target: LOG_TARGET, + ?candidate_hash, + ?is_disputed, + ?is_included, + ?is_backed, + ?is_confirmed, + ?all_invalid_votes_disabled, + ?ignore_disabled, + "Checking for potential spam" + ); + (is_disputed && !is_included && !is_backed && !is_confirmed) || ignore_disabled } diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index 97da5a1e94a07947765ddc8311b23c8d03cb4f79..705014e67a05eff6a8c5954bdb0834faef10cc0e 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -12,24 +12,18 @@ workspace = true [dependencies] futures = { workspace = true } gum = { workspace = true, default-features = true } -codec = { workspace = true, default-features = true } thiserror = { workspace = true } fatality = { workspace = true } -bitvec = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } -polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } [dev-dependencies] assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } -polkadot-node-subsystem-types = { workspace = true, default-features = true } polkadot-primitives-test-helpers = { workspace = true } +sp-tracing = { workspace = true } sp-core = { workspace = true, default-features = true } -sc-keystore = { workspace = true, default-features = true } -sp-application-crypto = { workspace = true, default-features = true } -sp-keyring = { workspace = true, default-features = true } -sp-keystore = { workspace = true, default-features = true } +rand = { workspace = true } rstest = { workspace = true } diff --git a/polkadot/node/core/prospective-parachains/src/error.rs b/polkadot/node/core/prospective-parachains/src/error.rs index 2b0933ab1c7e03e1a020d3cdc2c4953dfd9d294a..4b332b9c5de59aa49081bfeb3e8b2b7335c98717 100644 --- a/polkadot/node/core/prospective-parachains/src/error.rs +++ b/polkadot/node/core/prospective-parachains/src/error.rs @@ -30,18 +30,6 @@ use fatality::Nested; #[allow(missing_docs)] #[fatality::fatality(splitable)] pub enum Error { - #[fatal] - #[error("SubsystemError::Context error: {0}")] - SubsystemContext(String), - - #[fatal] - #[error("Spawning a task failed: {0}")] - SpawnFailed(SubsystemError), - - #[fatal] - #[error("Participation worker receiver exhausted.")] - ParticipationWorkerReceiverExhausted, - #[fatal] #[error("Receiving message from overseer failed: {0}")] SubsystemReceive(#[source] SubsystemError), @@ -55,9 +43,6 @@ pub enum Error { #[error(transparent)] ChainApi(#[from] ChainApiError), - #[error(transparent)] - Subsystem(SubsystemError), - #[error("Request to chain API subsystem dropped")] ChainApiRequestCanceled(oneshot::Canceled), diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs index b5fe70e7692345c62fd98644f6ecf9570d7a8b67..b060897d439168e4dd04acd1e3e896429d7c3ed4 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs @@ -18,41 +18,58 @@ //! //! # Overview //! -//! This module exposes two main types: [`FragmentChain`] and [`CandidateStorage`] which are meant -//! to be used in close conjunction. Each fragment chain is associated with a particular -//! relay-parent and each node in the chain represents a candidate. Each parachain has a single -//! candidate storage, but can have one chain for each relay chain block in the view. -//! Therefore, the same candidate can be present in multiple fragment chains of a parachain. One of -//! the purposes of the candidate storage is to deduplicate the large candidate data that is being -//! referenced from multiple fragment chains. +//! The main type exposed by this module is the [`FragmentChain`]. //! -//! A chain has an associated [`Scope`] which defines limits on candidates within the chain. -//! Candidates themselves have their own [`Constraints`] which are either the constraints from the -//! scope, or, if there are previous nodes in the chain, a modified version of the previous -//! candidate's constraints. +//! Each fragment chain is associated with a particular relay-parent (an active leaf) and has a +//! [`Scope`], which contains the allowed relay parents (up to `allowed_ancestry_len`), the pending +//! availability candidates and base constraints derived from the latest included candidate. Each +//! parachain has a single `FragmentChain` for each active leaf where it's scheduled. //! -//! Another use of the `CandidateStorage` is to keep a record of candidates which may not be yet -//! included in any chain, but which may become part of a chain in the future. This is needed for -//! elastic scaling, so that we may parallelise the backing process across different groups. As long -//! as some basic constraints are not violated by an unconnected candidate (like the relay parent -//! being in scope), we proceed with the backing process, hoping that its predecessors will be -//! backed soon enough. This is commonly called a potential candidate. Note that not all potential -//! candidates will be maintained in the CandidateStorage. The total number of connected + potential -//! candidates will be at most max_candidate_depth + 1. +//! A fragment chain consists mainly of the current best backable chain (we'll call this the best +//! chain) and a storage of unconnected potential candidates (we'll call this the unconnected +//! storage). +//! +//! The best chain contains all the candidates pending availability and a subsequent chain +//! of candidates that have reached the backing quorum and are better than any other backable forks +//! according to the fork selection rule (more on this rule later). It has a length of size at most +//! `max_candidate_depth + 1`. +//! +//! The unconnected storage keeps a record of seconded/backable candidates that may be +//! added to the best chain in the future. +//! Once a candidate is seconded, it becomes part of this unconnected storage. +//! Only after it is backed it may be added to the best chain (but not necessarily). It's only +//! added if it builds on the latest candidate in the chain and if there isn't a better backable +//! candidate according to the fork selection rule. +//! +//! An important thing to note is that the candidates present in the unconnected storage may have +//! any/no relationship between them. In other words, they may form N trees and may even form +//! cycles. This is needed so that we may begin validating candidates for which we don't yet know +//! their parent (so we may parallelize the backing process across different groups for elastic +//! scaling) and so that we accept parachain forks. +//! +//! We accept parachain forks only if the fork selection rule allows for it. In other words, if we +//! have a backed candidate, we begin seconding/validating a fork only if it has a lower candidate +//! hash. Once both forks are backed, we discard the one with the higher candidate hash. +//! We assume all validators pick the same fork according to the fork selection rule. If we decided +//! to not accept parachain forks, candidates could end up getting only half of the backing votes or +//! even less (for forks of larger arity). This would affect the validator rewards. Still, we don't +//! guarantee that a fork-producing parachains will be able to fully use elastic scaling. +//! +//! Once a candidate is backed and becomes part of the best chain, we can trim from the +//! unconnected storage candidates which constitute forks on the best chain and no longer have +//! potential. //! //! This module also makes use of types provided by the Inclusion Emulator module, such as //! [`Fragment`] and [`Constraints`]. These perform the actual job of checking for validity of //! prospective fragments. //! -//! # Parachain forks +//! # Fork choice rule //! -//! Parachains are expected to not create forks, hence the use of fragment chains as opposed to -//! fragment trees. If parachains do create forks, their performance in regards to async backing and -//! elastic scaling will suffer, because different validators will have different views of the -//! future. +//! The motivation for the fork choice rule is described in the previous chapter. //! -//! This is a compromise we can make - collators which want to use async backing and elastic scaling -//! need to cooperate for the highest throughput. +//! The current rule is: choose the candidate with the lower candidate hash. +//! The candidate hash is quite random and finding a candidate with a lower hash in order to favour +//! it would essentially mean solving a proof of work problem. //! //! # Parachain cycles //! @@ -65,70 +82,117 @@ //! resolved by having candidates reference their parent by candidate hash. //! //! However, dealing with cycles increases complexity during the backing/inclusion process for no -//! practical reason. Therefore, fragment chains will not accept such candidates. +//! practical reason. +//! These cycles may be accepted by fragment chains while candidates are part of the unconnected +//! storage, but they will definitely not make it to the best chain. //! //! On the other hand, enforcing that a parachain will NEVER be acyclic would be very complicated //! (looping through the entire parachain's history on every new candidate or changing the candidate //! receipt to reference the parent's candidate hash). //! +//! Therefore, we don't provide a guarantee that a cycle-producing parachain will work (although in +//! practice they probably will if the cycle length is larger than the number of assigned cores +//! multiplied by two). +//! //! # Spam protection //! -//! As long as the [`CandidateStorage`] has bounded input on the number of candidates supplied, -//! [`FragmentChain`] complexity is bounded. This means that higher-level code needs to be selective -//! about limiting the amount of candidates that are considered. +//! As long as the supplied number of candidates is bounded, [`FragmentChain`] complexity is +//! bounded. This means that higher-level code needs to be selective about limiting the amount of +//! candidates that are considered. +//! +//! Practically speaking, the collator-protocol will not allow more than `max_candidate_depth + 1` +//! collations to be fetched at a relay parent and statement-distribution will not allow more than +//! `max_candidate_depth + 1` seconded candidates at a relay parent per each validator in the +//! backing group. Considering the `allowed_ancestry_len` configuration value, the number of +//! candidates in a `FragmentChain` (including its unconnected storage) should not exceed: +//! +//! `allowed_ancestry_len * (max_candidate_depth + 1) * backing_group_size`. //! //! The code in this module is not designed for speed or efficiency, but conceptual simplicity. //! Our assumption is that the amount of candidates and parachains we consider will be reasonably //! bounded and in practice will not exceed a few thousand at any time. This naive implementation //! will still perform fairly well under these conditions, despite being somewhat wasteful of //! memory. +//! +//! Still, the expensive candidate data (CandidateCommitments) are wrapped in an `Arc` and shared +//! across fragment chains of the same para on different active leaves. #[cfg(test)] mod tests; use std::{ + cmp::{min, Ordering}, collections::{ hash_map::{Entry, HashMap}, - BTreeMap, HashSet, + BTreeMap, HashSet, VecDeque, }, sync::Arc, }; use super::LOG_TARGET; -use polkadot_node_subsystem::messages::{Ancestors, HypotheticalCandidate}; +use polkadot_node_subsystem::messages::Ancestors; use polkadot_node_subsystem_util::inclusion_emulator::{ - ConstraintModifications, Constraints, Fragment, ProspectiveCandidate, RelayChainBlockInfo, + self, ConstraintModifications, Constraints, Fragment, HypotheticalOrConcreteCandidate, + ProspectiveCandidate, RelayChainBlockInfo, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, - PersistedValidationData, + BlockNumber, CandidateCommitments, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, + PersistedValidationData, ValidationCodeHash, }; +use thiserror::Error; + +/// Fragment chain related errors. +#[derive(Debug, Clone, PartialEq, Error)] +pub(crate) enum Error { + #[error("Candidate already known")] + CandidateAlreadyKnown, + #[error("Candidate's parent head is equal to its output head. Would introduce a cycle.")] + ZeroLengthCycle, + #[error("Candidate would introduce a cycle")] + Cycle, + #[error("Candidate would introduce two paths to the same output state")] + MultiplePaths, + #[error("Attempting to directly introduce a Backed candidate. It should first be introduced as Seconded")] + IntroduceBackedCandidate, + #[error("Relay parent {0:?} of the candidate precedes the relay parent {1:?} of a pending availability candidate")] + RelayParentPrecedesCandidatePendingAvailability(Hash, Hash), + #[error("Candidate would introduce a fork with a pending availability candidate: {0:?}")] + ForkWithCandidatePendingAvailability(CandidateHash), + #[error("Fork selection rule favours another candidate: {0:?}")] + ForkChoiceRule(CandidateHash), + #[error("Could not find parent of the candidate")] + ParentCandidateNotFound, + #[error("Could not compute candidate constraints: {0:?}")] + ComputeConstraints(inclusion_emulator::ModificationError), + #[error("Candidate violates constraints: {0:?}")] + CheckAgainstConstraints(inclusion_emulator::FragmentValidityError), + #[error("Relay parent would move backwards from the latest candidate in the chain")] + RelayParentMovedBackwards, + #[error(transparent)] + CandidateEntry(#[from] CandidateEntryError), + #[error("Relay parent {0:?} not in scope. Earliest relay parent allowed {1:?}")] + RelayParentNotInScope(Hash, Hash), +} -/// Kinds of failures to import a candidate into storage. -#[derive(Debug, Clone, PartialEq)] -pub enum CandidateStorageInsertionError { - /// An error indicating that a supplied candidate didn't match the persisted - /// validation data provided alongside it. - PersistedValidationDataMismatch, - /// The candidate was already known. - CandidateAlreadyKnown(CandidateHash), +/// The rule for selecting between two backed candidate forks, when adding to the chain. +/// All validators should adhere to this rule, in order to not lose out on rewards in case of +/// forking parachains. +fn fork_selection_rule(hash1: &CandidateHash, hash2: &CandidateHash) -> Ordering { + hash1.cmp(hash2) } -/// Stores candidates and information about them such as their relay-parents and their backing -/// states. +/// Utility for storing candidates and information about them such as their relay-parents and their +/// backing states. This does not assume any restriction on whether or not the candidates form a +/// chain. Useful for storing all kinds of candidates. #[derive(Clone, Default)] pub(crate) struct CandidateStorage { - // Index from head data hash to candidate hashes with that head data as a parent. Purely for + // Index from head data hash to candidate hashes with that head data as a parent. Useful for // efficiency when responding to `ProspectiveValidationDataRequest`s or when trying to find a // new candidate to push to a chain. - // Even though having multiple candidates with same parent would be invalid for a parachain, it - // could happen across different relay chain forks, hence the HashSet. by_parent_head: HashMap>, - // Index from head data hash to candidate hashes outputting that head data. Purely for + // Index from head data hash to candidate hashes outputting that head data. For // efficiency when responding to `ProspectiveValidationDataRequest`s. - // Even though having multiple candidates with same output would be invalid for a parachain, - // it could happen across different relay chain forks. by_output_head: HashMap>, // Index from candidate hash to fragment node. @@ -136,63 +200,59 @@ pub(crate) struct CandidateStorage { } impl CandidateStorage { - /// Introduce a new candidate. - pub fn add_candidate( + /// Introduce a new pending availability candidate. + pub fn add_pending_availability_candidate( &mut self, + candidate_hash: CandidateHash, candidate: CommittedCandidateReceipt, persisted_validation_data: PersistedValidationData, - state: CandidateState, - ) -> Result { - let candidate_hash = candidate.hash(); - if self.by_candidate_hash.contains_key(&candidate_hash) { - return Err(CandidateStorageInsertionError::CandidateAlreadyKnown(candidate_hash)) - } + ) -> Result<(), Error> { + let entry = CandidateEntry::new( + candidate_hash, + candidate, + persisted_validation_data, + CandidateState::Backed, + )?; - if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { - return Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) - } + self.add_candidate_entry(entry) + } - let entry = CandidateEntry { - candidate_hash, - parent_head_data_hash: persisted_validation_data.parent_head.hash(), - output_head_data_hash: candidate.commitments.head_data.hash(), - relay_parent: candidate.descriptor.relay_parent, - state, - candidate: Arc::new(ProspectiveCandidate { - commitments: candidate.commitments, - persisted_validation_data, - pov_hash: candidate.descriptor.pov_hash, - validation_code_hash: candidate.descriptor.validation_code_hash, - }), - }; + /// Return the number of stored candidates. + pub fn len(&self) -> usize { + self.by_candidate_hash.len() + } + + /// Introduce a new candidate entry. + fn add_candidate_entry(&mut self, candidate: CandidateEntry) -> Result<(), Error> { + let candidate_hash = candidate.candidate_hash; + if self.by_candidate_hash.contains_key(&candidate_hash) { + return Err(Error::CandidateAlreadyKnown) + } self.by_parent_head - .entry(entry.parent_head_data_hash()) + .entry(candidate.parent_head_data_hash) .or_default() .insert(candidate_hash); self.by_output_head - .entry(entry.output_head_data_hash()) + .entry(candidate.output_head_data_hash) .or_default() .insert(candidate_hash); - // sanity-checked already. - self.by_candidate_hash.insert(candidate_hash, entry); + self.by_candidate_hash.insert(candidate_hash, candidate); - Ok(candidate_hash) + Ok(()) } /// Remove a candidate from the store. - pub fn remove_candidate(&mut self, candidate_hash: &CandidateHash) { + fn remove_candidate(&mut self, candidate_hash: &CandidateHash) { if let Some(entry) = self.by_candidate_hash.remove(candidate_hash) { - if let Entry::Occupied(mut e) = self.by_parent_head.entry(entry.parent_head_data_hash()) - { + if let Entry::Occupied(mut e) = self.by_parent_head.entry(entry.parent_head_data_hash) { e.get_mut().remove(&candidate_hash); if e.get().is_empty() { e.remove(); } } - if let Entry::Occupied(mut e) = self.by_output_head.entry(entry.output_head_data_hash()) - { + if let Entry::Occupied(mut e) = self.by_output_head.entry(entry.output_head_data_hash) { e.get_mut().remove(&candidate_hash); if e.get().is_empty() { e.remove(); @@ -202,7 +262,7 @@ impl CandidateStorage { } /// Note that an existing candidate has been backed. - pub fn mark_backed(&mut self, candidate_hash: &CandidateHash) { + fn mark_backed(&mut self, candidate_hash: &CandidateHash) { if let Some(entry) = self.by_candidate_hash.get_mut(candidate_hash) { gum::trace!(target: LOG_TARGET, ?candidate_hash, "Candidate marked as backed"); entry.state = CandidateState::Backed; @@ -211,38 +271,18 @@ impl CandidateStorage { } } - /// Whether a candidate is recorded as being backed. - pub fn is_backed(&self, candidate_hash: &CandidateHash) -> bool { - self.by_candidate_hash - .get(candidate_hash) - .map_or(false, |e| e.state == CandidateState::Backed) - } - /// Whether a candidate is contained within the storage already. - pub fn contains(&self, candidate_hash: &CandidateHash) -> bool { + fn contains(&self, candidate_hash: &CandidateHash) -> bool { self.by_candidate_hash.contains_key(candidate_hash) } - /// Return an iterator over the stored candidates. - pub fn candidates(&self) -> impl Iterator { + /// Return an iterator over references to the stored candidates, in arbitrary order. + fn candidates(&self) -> impl Iterator { self.by_candidate_hash.values() } - /// Retain only candidates which pass the predicate. - pub(crate) fn retain(&mut self, pred: impl Fn(&CandidateHash) -> bool) { - self.by_candidate_hash.retain(|h, _v| pred(h)); - self.by_parent_head.retain(|_parent, children| { - children.retain(|h| pred(h)); - !children.is_empty() - }); - self.by_output_head.retain(|_output, candidates| { - candidates.retain(|h| pred(h)); - !candidates.is_empty() - }); - } - - /// Get head-data by hash. - pub(crate) fn head_data_by_hash(&self, hash: &Hash) -> Option<&HeadData> { + /// Try getting head-data by hash. + fn head_data_by_hash(&self, hash: &Hash) -> Option<&HeadData> { // First, search for candidates outputting this head data and extract the head data // from their commitments if they exist. // @@ -262,16 +302,8 @@ impl CandidateStorage { }) } - /// Returns candidate's relay parent, if present. - pub(crate) fn relay_parent_of_candidate(&self, candidate_hash: &CandidateHash) -> Option { - self.by_candidate_hash.get(candidate_hash).map(|entry| entry.relay_parent) - } - - /// Returns the candidates which have the given head data hash as parent. - /// We don't allow forks in a parachain, but we may have multiple candidates with same parent - /// across different relay chain forks. That's why it returns an iterator (but only one will be - /// valid and used in the end). - fn possible_para_children<'a>( + /// Returns the backed candidates which have the given head data hash as parent. + fn possible_backed_para_children<'a>( &'a self, parent_head_hash: &'a Hash, ) -> impl Iterator + 'a { @@ -280,12 +312,11 @@ impl CandidateStorage { .get(parent_head_hash) .into_iter() .flat_map(|hashes| hashes.iter()) - .filter_map(move |h| by_candidate_hash.get(h)) - } - - #[cfg(test)] - pub fn len(&self) -> (usize, usize) { - (self.by_parent_head.len(), self.by_candidate_hash.len()) + .filter_map(move |h| { + by_candidate_hash.get(h).and_then(|candidate| { + (candidate.state == CandidateState::Backed).then_some(candidate) + }) + }) } } @@ -293,14 +324,24 @@ impl CandidateStorage { /// /// Candidates aren't even considered until they've at least been seconded. #[derive(Debug, PartialEq, Clone)] -pub(crate) enum CandidateState { +enum CandidateState { /// The candidate has been seconded. Seconded, /// The candidate has been completely backed by the group. Backed, } +#[derive(Debug, Clone, PartialEq, Error)] +/// Possible errors when construcing a candidate entry. +pub enum CandidateEntryError { + #[error("Candidate does not match the persisted validation data provided alongside it")] + PersistedValidationDataMismatch, + #[error("Candidate's parent head is equal to its output head. Would introduce a cycle")] + ZeroLengthCycle, +} + #[derive(Debug, Clone)] +/// Representation of a candidate into the [`CandidateStorage`]. pub(crate) struct CandidateEntry { candidate_hash: CandidateHash, parent_head_data_hash: Hash, @@ -311,16 +352,79 @@ pub(crate) struct CandidateEntry { } impl CandidateEntry { + /// Create a new seconded candidate entry. + pub fn new_seconded( + candidate_hash: CandidateHash, + candidate: CommittedCandidateReceipt, + persisted_validation_data: PersistedValidationData, + ) -> Result { + Self::new(candidate_hash, candidate, persisted_validation_data, CandidateState::Seconded) + } + pub fn hash(&self) -> CandidateHash { self.candidate_hash } - pub fn parent_head_data_hash(&self) -> Hash { + fn new( + candidate_hash: CandidateHash, + candidate: CommittedCandidateReceipt, + persisted_validation_data: PersistedValidationData, + state: CandidateState, + ) -> Result { + if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { + return Err(CandidateEntryError::PersistedValidationDataMismatch) + } + + let parent_head_data_hash = persisted_validation_data.parent_head.hash(); + let output_head_data_hash = candidate.commitments.head_data.hash(); + + if parent_head_data_hash == output_head_data_hash { + return Err(CandidateEntryError::ZeroLengthCycle) + } + + Ok(Self { + candidate_hash, + parent_head_data_hash, + output_head_data_hash, + relay_parent: candidate.descriptor.relay_parent, + state, + candidate: Arc::new(ProspectiveCandidate { + commitments: candidate.commitments, + persisted_validation_data, + pov_hash: candidate.descriptor.pov_hash, + validation_code_hash: candidate.descriptor.validation_code_hash, + }), + }) + } +} + +impl HypotheticalOrConcreteCandidate for CandidateEntry { + fn commitments(&self) -> Option<&CandidateCommitments> { + Some(&self.candidate.commitments) + } + + fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + Some(&self.candidate.persisted_validation_data) + } + + fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + Some(&self.candidate.validation_code_hash) + } + + fn parent_head_data_hash(&self) -> Hash { self.parent_head_data_hash } - pub fn output_head_data_hash(&self) -> Hash { - self.output_head_data_hash + fn output_head_data_hash(&self) -> Option { + Some(self.output_head_data_hash) + } + + fn relay_parent(&self) -> Hash { + self.relay_parent + } + + fn candidate_hash(&self) -> CandidateHash { + self.candidate_hash } } @@ -337,8 +441,6 @@ pub(crate) struct PendingAvailability { /// The scope of a [`FragmentChain`]. #[derive(Debug, Clone)] pub(crate) struct Scope { - /// The assigned para id of this `FragmentChain`. - para: ParaId, /// The relay parent we're currently building on top of. relay_parent: RelayChainBlockInfo, /// The other relay parents candidates are allowed to build upon, mapped by the block number. @@ -356,10 +458,14 @@ pub(crate) struct Scope { /// An error variant indicating that ancestors provided to a scope /// had unexpected order. #[derive(Debug)] -pub struct UnexpectedAncestor { +pub(crate) struct UnexpectedAncestor { /// The block number that this error occurred at. + /// Allow as dead code, but it's being read in logs. + #[allow(dead_code)] pub number: BlockNumber, /// The previous seen block number, which did not match `number`. + /// Allow as dead code, but it's being read in logs. + #[allow(dead_code)] pub prev: BlockNumber, } @@ -381,7 +487,6 @@ impl Scope { /// /// It is allowed to provide zero ancestors. pub fn with_ancestors( - para: ParaId, relay_parent: RelayChainBlockInfo, base_constraints: Constraints, pending_availability: Vec, @@ -408,7 +513,6 @@ impl Scope { } Ok(Scope { - para, relay_parent, base_constraints, pending_availability, @@ -436,24 +540,29 @@ impl Scope { self.ancestors_by_hash.get(hash).map(|info| info.clone()) } + /// Get the base constraints of the scope + pub fn base_constraints(&self) -> &Constraints { + &self.base_constraints + } + /// Whether the candidate in question is one pending availability in this scope. - pub fn get_pending_availability( + fn get_pending_availability( &self, candidate_hash: &CandidateHash, ) -> Option<&PendingAvailability> { self.pending_availability.iter().find(|c| &c.candidate_hash == candidate_hash) } - - /// Get the base constraints of the scope - pub fn base_constraints(&self) -> &Constraints { - &self.base_constraints - } } -pub struct FragmentNode { +#[cfg_attr(test, derive(Clone))] +/// A node that is part of a `BackedChain`. It holds constraints based on the ancestors in the +/// chain. +struct FragmentNode { fragment: Fragment, candidate_hash: CandidateHash, cumulative_modifications: ConstraintModifications, + parent_head_data_hash: Hash, + output_head_data_hash: Hash, } impl FragmentNode { @@ -462,211 +571,336 @@ impl FragmentNode { } } -/// Response given by `can_add_candidate_as_potential` -#[derive(PartialEq, Debug)] -pub enum PotentialAddition { - /// Can be added as either connected or unconnected candidate. - Anyhow, - /// Can only be added as a connected candidate to the chain. - IfConnected, - /// Cannot be added. - None, +impl From<&FragmentNode> for CandidateEntry { + fn from(node: &FragmentNode) -> Self { + // We don't need to perform the checks done in `CandidateEntry::new()`, since a + // `FragmentNode` always comes from a `CandidateEntry` + Self { + candidate_hash: node.candidate_hash, + parent_head_data_hash: node.parent_head_data_hash, + output_head_data_hash: node.output_head_data_hash, + candidate: node.fragment.candidate_clone(), + relay_parent: node.relay_parent(), + // A fragment node is always backed. + state: CandidateState::Backed, + } + } } -/// This is a chain of candidates based on some underlying storage of candidates and a scope. +/// A candidate chain of backed/backable candidates. +/// Includes the candidates pending availability and candidates which may be backed on-chain. +#[derive(Default)] +#[cfg_attr(test, derive(Clone))] +struct BackedChain { + // Holds the candidate chain. + chain: Vec, + // Index from head data hash to the candidate hash with that head data as a parent. + // Only contains the candidates present in the `chain`. + by_parent_head: HashMap, + // Index from head data hash to the candidate hash outputting that head data. + // Only contains the candidates present in the `chain`. + by_output_head: HashMap, + // A set of the candidate hashes in the `chain`. + candidates: HashSet, +} + +impl BackedChain { + fn push(&mut self, candidate: FragmentNode) { + self.candidates.insert(candidate.candidate_hash); + self.by_parent_head + .insert(candidate.parent_head_data_hash, candidate.candidate_hash); + self.by_output_head + .insert(candidate.output_head_data_hash, candidate.candidate_hash); + self.chain.push(candidate); + } + + fn clear(&mut self) -> Vec { + self.by_parent_head.clear(); + self.by_output_head.clear(); + self.candidates.clear(); + + std::mem::take(&mut self.chain) + } + + fn revert_to_parent_hash<'a>( + &'a mut self, + parent_head_data_hash: &Hash, + ) -> impl Iterator + 'a { + let mut found_index = None; + for index in 0..self.chain.len() { + let node = &self.chain[0]; + + if found_index.is_some() { + self.by_parent_head.remove(&node.parent_head_data_hash); + self.by_output_head.remove(&node.output_head_data_hash); + self.candidates.remove(&node.candidate_hash); + } else if &node.output_head_data_hash == parent_head_data_hash { + found_index = Some(index); + } + } + + if let Some(index) = found_index { + self.chain.drain(min(index + 1, self.chain.len())..) + } else { + // Don't remove anything, but use drain to satisfy the compiler. + self.chain.drain(0..0) + } + } + + fn contains(&self, hash: &CandidateHash) -> bool { + self.candidates.contains(hash) + } +} + +/// This is the fragment chain specific to an active leaf. /// -/// All nodes in the chain must be either pending availability or within the scope. Within the scope -/// means it's built off of the relay-parent or an ancestor. +/// It holds the current best backable candidate chain, as well as potential candidates +/// which could become connected to the chain in the future or which could even overwrite the +/// existing chain. +#[cfg_attr(test, derive(Clone))] pub(crate) struct FragmentChain { + // The current scope, which dictates the on-chain operating constraints that all future + // candidates must adhere to. scope: Scope, - chain: Vec, - - candidates: HashSet, + // The current best chain of backable candidates. It only contains candidates which build on + // top of each other and which have reached the backing quorum. In the presence of potential + // forks, this chain will pick a fork according to the `fork_selection_rule`. + best_chain: BackedChain, - // Index from head data hash to candidate hashes with that head data as a parent. - by_parent_head: HashMap, - // Index from head data hash to candidate hashes outputting that head data. - by_output_head: HashMap, + // The potential candidate storage. Contains candidates which are not yet part of the `chain` + // but may become in the future. These can form any tree shape as well as contain any + // unconnected candidates for which we don't know the parent. + unconnected: CandidateStorage, } impl FragmentChain { - /// Create a new [`FragmentChain`] with given scope and populated from the storage. - pub fn populate(scope: Scope, storage: &CandidateStorage) -> Self { - gum::trace!( - target: LOG_TARGET, - relay_parent = ?scope.relay_parent.hash, - relay_parent_num = scope.relay_parent.number, - para_id = ?scope.para, - ancestors = scope.ancestors.len(), - "Instantiating Fragment Chain", - ); - + /// Create a new [`FragmentChain`] with the given scope and populate it with the candidates + /// pending availability. + pub fn init(scope: Scope, mut candidates_pending_availability: CandidateStorage) -> Self { let mut fragment_chain = Self { scope, - chain: Vec::new(), - candidates: HashSet::new(), - by_parent_head: HashMap::new(), - by_output_head: HashMap::new(), + best_chain: BackedChain::default(), + unconnected: CandidateStorage::default(), }; - fragment_chain.populate_chain(storage); + // We only need to populate the best backable chain. Candidates pending availability must + // form a chain with the latest included head. + fragment_chain.populate_chain(&mut candidates_pending_availability); fragment_chain } - /// Get the scope of the Fragment Chain. + /// Populate the [`FragmentChain`] given the new candidates pending availability and the + /// optional previous fragment chain (of the previous relay parent). + pub fn populate_from_previous(&mut self, prev_fragment_chain: &FragmentChain) { + let mut prev_storage = prev_fragment_chain.unconnected.clone(); + + for candidate in prev_fragment_chain.best_chain.chain.iter() { + // If they used to be pending availability, don't add them. This is fine + // because: + // - if they still are pending availability, they have already been added to the new + // storage. + // - if they were included, no point in keeping them. + // + // This cannot happen for the candidates in the unconnected storage. The pending + // availability candidates will always be part of the best chain. + if prev_fragment_chain + .scope + .get_pending_availability(&candidate.candidate_hash) + .is_none() + { + let _ = prev_storage.add_candidate_entry(candidate.into()); + } + } + + // First populate the best backable chain. + self.populate_chain(&mut prev_storage); + + // Now that we picked the best backable chain, trim the forks generated by candidates which + // are not present in the best chain. + self.trim_uneligible_forks(&mut prev_storage, None); + + // Finally, keep any candidates which haven't been trimmed but still have potential. + self.populate_unconnected_potential_candidates(prev_storage); + } + + /// Get the scope of the [`FragmentChain`]. pub fn scope(&self) -> &Scope { &self.scope } - /// Returns the number of candidates in the chain - pub(crate) fn len(&self) -> usize { - self.candidates.len() + /// Returns the number of candidates in the best backable chain. + pub fn best_chain_len(&self) -> usize { + self.best_chain.chain.len() } - /// Whether the candidate exists. - pub(crate) fn contains_candidate(&self, candidate: &CandidateHash) -> bool { - self.candidates.contains(candidate) + /// Returns the number of candidates in unconnected potential storage. + pub fn unconnected_len(&self) -> usize { + self.unconnected.len() + } + + /// Whether the candidate exists as part of the unconnected potential candidates. + pub fn contains_unconnected_candidate(&self, candidate: &CandidateHash) -> bool { + self.unconnected.contains(candidate) } /// Return a vector of the chain's candidate hashes, in-order. - pub(crate) fn to_vec(&self) -> Vec { - self.chain.iter().map(|candidate| candidate.candidate_hash).collect() + pub fn best_chain_vec(&self) -> Vec { + self.best_chain.chain.iter().map(|candidate| candidate.candidate_hash).collect() } - /// Try accumulating more candidates onto the chain. - /// - /// Candidates can only be added if they build on the already existing chain. - pub(crate) fn extend_from_storage(&mut self, storage: &CandidateStorage) { - self.populate_chain(storage); + /// Return a vector of the unconnected potential candidate hashes, in arbitrary order. + pub fn unconnected(&self) -> impl Iterator { + self.unconnected.candidates() } - /// Returns the hypothetical state of a candidate with the given hash and parent head data - /// in regards to the existing chain. - /// - /// Returns true if either: - /// - the candidate is already present - /// - the candidate can be added to the chain - /// - the candidate could potentially be added to the chain in the future (its ancestors are - /// still unknown but it doesn't violate other rules). - /// - /// If this returns false, the candidate could never be added to the current chain (not now, not - /// ever) - pub(crate) fn hypothetical_membership( + /// Return whether this candidate is backed in this chain or the unconnected storage. + pub fn is_candidate_backed(&self, hash: &CandidateHash) -> bool { + self.best_chain.candidates.contains(hash) || + matches!( + self.unconnected.by_candidate_hash.get(hash), + Some(candidate) if candidate.state == CandidateState::Backed + ) + } + + /// Mark a candidate as backed. This can trigger a recreation of the best backable chain. + pub fn candidate_backed(&mut self, newly_backed_candidate: &CandidateHash) { + // Already backed. + if self.best_chain.candidates.contains(newly_backed_candidate) { + return + } + let Some(parent_head_hash) = self + .unconnected + .by_candidate_hash + .get(newly_backed_candidate) + .map(|entry| entry.parent_head_data_hash) + else { + // Candidate is not in unconnected storage. + return + }; + + // Mark the candidate hash. + self.unconnected.mark_backed(newly_backed_candidate); + + // Revert to parent_head_hash + if !self.revert_to(&parent_head_hash) { + // If nothing was reverted, there is nothing we can do for now. + return + } + + let mut prev_storage = std::mem::take(&mut self.unconnected); + + // Populate the chain. + self.populate_chain(&mut prev_storage); + + // Now that we picked the best backable chain, trim the forks generated by candidates + // which are not present in the best chain. We can start trimming from this candidate + // onwards. + self.trim_uneligible_forks(&mut prev_storage, Some(parent_head_hash)); + + // Finally, keep any candidates which haven't been trimmed but still have potential. + self.populate_unconnected_potential_candidates(prev_storage); + } + + /// Checks if this candidate could be added in the future to this chain. + /// This will return `Error::CandidateAlreadyKnown` if the candidate is already in the chain or + /// the unconnected candidate storage. + pub fn can_add_candidate_as_potential( &self, - candidate: HypotheticalCandidate, - candidate_storage: &CandidateStorage, - ) -> bool { + candidate: &impl HypotheticalOrConcreteCandidate, + ) -> Result<(), Error> { let candidate_hash = candidate.candidate_hash(); - // If we've already used this candidate in the chain - if self.candidates.contains(&candidate_hash) { - return true + if self.best_chain.contains(&candidate_hash) || self.unconnected.contains(&candidate_hash) { + return Err(Error::CandidateAlreadyKnown) } - let can_add_as_potential = self.can_add_candidate_as_potential( - candidate_storage, - &candidate.candidate_hash(), - &candidate.relay_parent(), - candidate.parent_head_data_hash(), - candidate.output_head_data_hash(), - ); + self.check_potential(candidate) + } - if can_add_as_potential == PotentialAddition::None { - return false + /// Try adding a seconded candidate, if the candidate has potential. It will never be added to + /// the chain directly in the seconded state, it will only be part of the unconnected storage. + pub fn try_adding_seconded_candidate( + &mut self, + candidate: &CandidateEntry, + ) -> Result<(), Error> { + if candidate.state == CandidateState::Backed { + return Err(Error::IntroduceBackedCandidate); } - let Some(candidate_relay_parent) = self.scope.ancestor(&candidate.relay_parent()) else { - // can_add_candidate_as_potential already checked for this, but just to be safe. - return false - }; + self.can_add_candidate_as_potential(candidate)?; - let identity_modifications = ConstraintModifications::identity(); - let cumulative_modifications = if let Some(last_candidate) = self.chain.last() { - &last_candidate.cumulative_modifications - } else { - &identity_modifications - }; + // This clone is cheap, as it uses an Arc for the expensive stuff. + // We can't consume the candidate because other fragment chains may use it also. + self.unconnected.add_candidate_entry(candidate.clone())?; - let child_constraints = - match self.scope.base_constraints.apply_modifications(&cumulative_modifications) { - Err(e) => { - gum::debug!( - target: LOG_TARGET, - new_parent_head = ?cumulative_modifications.required_parent, - ?candidate_hash, - err = ?e, - "Failed to apply modifications", - ); - - return false - }, - Ok(c) => c, - }; + Ok(()) + } - let parent_head_hash = candidate.parent_head_data_hash(); - if parent_head_hash == child_constraints.required_parent.hash() { - // We do additional checks for complete candidates. - if let HypotheticalCandidate::Complete { - ref receipt, - ref persisted_validation_data, - .. - } = candidate - { - if Fragment::check_against_constraints( - &candidate_relay_parent, - &child_constraints, - &receipt.commitments, - &receipt.descriptor().validation_code_hash, - persisted_validation_data, - ) - .is_err() - { - gum::debug!( - target: LOG_TARGET, - "Fragment::check_against_constraints() returned error", - ); - return false - } - } + /// Try getting the full head data associated with this hash. + pub fn get_head_data_by_hash(&self, head_data_hash: &Hash) -> Option { + // First, see if this is the head data of the latest included candidate. + let required_parent = &self.scope.base_constraints().required_parent; + if &required_parent.hash() == head_data_hash { + return Some(required_parent.clone()) + } - // If we got this far, it can be added to the chain right now. - true - } else if can_add_as_potential == PotentialAddition::Anyhow { - // Otherwise it is or can be an unconnected candidate, but only if PotentialAddition - // does not force us to only add a connected candidate. - true - } else { - false + // Cheaply check if the head data is in the best backable chain. + let has_head_data_in_chain = self + .best_chain + .by_parent_head + .get(head_data_hash) + .or_else(|| self.best_chain.by_output_head.get(head_data_hash)) + .is_some(); + + if has_head_data_in_chain { + return self.best_chain.chain.iter().find_map(|candidate| { + if &candidate.parent_head_data_hash == head_data_hash { + Some( + candidate + .fragment + .candidate() + .persisted_validation_data + .parent_head + .clone(), + ) + } else if &candidate.output_head_data_hash == head_data_hash { + Some(candidate.fragment.candidate().commitments.head_data.clone()) + } else { + None + } + }); } + + // Lastly, try getting the head data from the unconnected candidates. + self.unconnected.head_data_by_hash(head_data_hash).cloned() } - /// Select `count` candidates after the given `ancestors` which pass - /// the predicate and have not already been backed on chain. + /// Select `count` candidates after the given `ancestors` which can be backed on chain next. /// /// The intention of the `ancestors` is to allow queries on the basis of /// one or more candidates which were previously pending availability becoming /// available or candidates timing out. - pub(crate) fn find_backable_chain( + pub fn find_backable_chain( &self, ancestors: Ancestors, count: u32, - pred: impl Fn(&CandidateHash) -> bool, - ) -> Vec { + ) -> Vec<(CandidateHash, Hash)> { if count == 0 { return vec![] } let base_pos = self.find_ancestor_path(ancestors); - let actual_end_index = std::cmp::min(base_pos + (count as usize), self.chain.len()); + let actual_end_index = + std::cmp::min(base_pos + (count as usize), self.best_chain.chain.len()); let mut res = Vec::with_capacity(actual_end_index - base_pos); - for elem in &self.chain[base_pos..actual_end_index] { - if self.scope.get_pending_availability(&elem.candidate_hash).is_none() && - pred(&elem.candidate_hash) - { - res.push(elem.candidate_hash); + for elem in &self.best_chain.chain[base_pos..actual_end_index] { + // Only supply candidates which are not yet pending availability. `ancestors` should + // have already contained them, but check just in case. + if self.scope.get_pending_availability(&elem.candidate_hash).is_none() { + res.push((elem.candidate_hash, elem.relay_parent())); } else { break } @@ -679,11 +913,11 @@ impl FragmentChain { // Stops when the ancestors are all used or when a node in the chain is not present in the // ancestor set. Returns the index in the chain were the search stopped. fn find_ancestor_path(&self, mut ancestors: Ancestors) -> usize { - if self.chain.is_empty() { + if self.best_chain.chain.is_empty() { return 0; } - for (index, candidate) in self.chain.iter().enumerate() { + for (index, candidate) in self.best_chain.chain.iter().enumerate() { if !ancestors.remove(&candidate.candidate_hash) { return index } @@ -691,16 +925,16 @@ impl FragmentChain { // This means that we found the entire chain in the ancestor set. There won't be anything // left to back. - self.chain.len() + self.best_chain.chain.len() } - // Return the earliest relay parent a new candidate can have in order to be added to the chain. - // This is the relay parent of the last candidate in the chain. + // Return the earliest relay parent a new candidate can have in order to be added to the chain + // right now. This is the relay parent of the last candidate in the chain. // The value returned may not be valid if we want to add a candidate pending availability, which // may have a relay parent which is out of scope. Special handling is needed in that case. // `None` is returned if the candidate's relay parent info cannot be found. fn earliest_relay_parent(&self) -> Option { - if let Some(last_candidate) = self.chain.last() { + if let Some(last_candidate) = self.best_chain.chain.last() { self.scope.ancestor(&last_candidate.relay_parent()).or_else(|| { // if the relay-parent is out of scope _and_ it is in the chain, // it must be a candidate pending availability. @@ -713,152 +947,239 @@ impl FragmentChain { } } - // Checks if this candidate could be added in the future to this chain. - // This assumes that the chain does not already contain this candidate. It may or may not be - // present in the `CandidateStorage`. - // Even if the candidate is a potential candidate, this function will indicate that it can be - // kept only if there's enough room for it. - pub(crate) fn can_add_candidate_as_potential( - &self, - storage: &CandidateStorage, - candidate_hash: &CandidateHash, - relay_parent: &Hash, - parent_head_hash: Hash, - output_head_hash: Option, - ) -> PotentialAddition { - // If we've got enough candidates for the configured depth, no point in adding more. - if self.chain.len() > self.scope.max_depth { - return PotentialAddition::None - } + // Return the earliest relay parent a potential candidate may have for it to ever be added to + // the chain. This is the relay parent of the last candidate pending availability or the + // earliest relay parent in scope. + fn earliest_relay_parent_pending_availability(&self) -> RelayChainBlockInfo { + self.best_chain + .chain + .iter() + .rev() + .find_map(|candidate| { + self.scope + .get_pending_availability(&candidate.candidate_hash) + .map(|c| c.relay_parent.clone()) + }) + .unwrap_or_else(|| self.scope.earliest_relay_parent()) + } - if !self.check_potential(relay_parent, parent_head_hash, output_head_hash) { - return PotentialAddition::None - } + // Populate the unconnected potential candidate storage starting from a previous storage. + fn populate_unconnected_potential_candidates(&mut self, old_storage: CandidateStorage) { + for candidate in old_storage.by_candidate_hash.into_values() { + // Sanity check, all pending availability candidates should be already present in the + // chain. + if self.scope.get_pending_availability(&candidate.candidate_hash).is_some() { + continue + } - let present_in_storage = storage.contains(candidate_hash); + match self.can_add_candidate_as_potential(&candidate) { + Ok(()) => { + let _ = self.unconnected.add_candidate_entry(candidate); + }, + // Swallow these errors as they can legitimately happen when pruning stale + // candidates. + Err(_) => {}, + }; + } + } - let unconnected = self - .find_unconnected_potential_candidates( - storage, - present_in_storage.then_some(candidate_hash), - ) - .len(); + // Check whether a candidate outputting this head data would introduce a cycle or multiple paths + // to the same state. Trivial 0-length cycles are checked in `CandidateEntry::new`. + fn check_cycles_or_invalid_tree(&self, output_head_hash: &Hash) -> Result<(), Error> { + // this should catch a cycle where this candidate would point back to the parent of some + // candidate in the chain. + if self.best_chain.by_parent_head.contains_key(output_head_hash) { + return Err(Error::Cycle) + } - if (self.chain.len() + unconnected) < self.scope.max_depth { - PotentialAddition::Anyhow - } else if (self.chain.len() + unconnected) == self.scope.max_depth { - // If we've only one slot left to fill, it must be filled with a connected candidate. - PotentialAddition::IfConnected - } else { - PotentialAddition::None + // multiple paths to the same state, which can't happen for a chain. + if self.best_chain.by_output_head.contains_key(output_head_hash) { + return Err(Error::MultiplePaths) } + + Ok(()) } - // The candidates which are present in `CandidateStorage`, are not part of this chain but could - // become part of this chain in the future. Capped at the max depth minus the existing chain - // length. - // If `ignore_candidate` is supplied and found in storage, it won't be counted. - pub(crate) fn find_unconnected_potential_candidates( + // Checks the potential of a candidate to be added to the chain now or in the future. + // It works both with concrete candidates for which we have the full PVD and committed receipt, + // but also does some more basic checks for incomplete candidates (before even fetching them). + fn check_potential( &self, - storage: &CandidateStorage, - ignore_candidate: Option<&CandidateHash>, - ) -> Vec { - let mut candidates = vec![]; - for candidate in storage.candidates() { - if let Some(ignore_candidate) = ignore_candidate { - if ignore_candidate == &candidate.candidate_hash { - continue - } - } - // We stop at max_depth + 1 with the search. There's no point in looping further. - if (self.chain.len() + candidates.len()) > self.scope.max_depth { - break - } - if !self.candidates.contains(&candidate.candidate_hash) && - self.check_potential( - &candidate.relay_parent, - candidate.candidate.persisted_validation_data.parent_head.hash(), - Some(candidate.candidate.commitments.head_data.hash()), - ) { - candidates.push(candidate.candidate_hash); + candidate: &impl HypotheticalOrConcreteCandidate, + ) -> Result<(), Error> { + let relay_parent = candidate.relay_parent(); + let parent_head_hash = candidate.parent_head_data_hash(); + + // trivial 0-length cycle. + if let Some(output_head_hash) = candidate.output_head_data_hash() { + if parent_head_hash == output_head_hash { + return Err(Error::ZeroLengthCycle) } } - candidates - } + // Check if the relay parent is in scope. + let Some(relay_parent) = self.scope.ancestor(&relay_parent) else { + return Err(Error::RelayParentNotInScope( + relay_parent, + self.scope.earliest_relay_parent().hash, + )) + }; - // Check if adding a candidate which transitions `parent_head_hash` to `output_head_hash` would - // introduce a fork or a cycle in the parachain. - // `output_head_hash` is optional because we sometimes make this check before retrieving the - // collation. - fn is_fork_or_cycle(&self, parent_head_hash: Hash, output_head_hash: Option) -> bool { - if self.by_parent_head.contains_key(&parent_head_hash) { - // fork. our parent has another child already - return true + // Check if the relay parent moved backwards from the latest candidate pending availability. + let earliest_rp_of_pending_availability = self.earliest_relay_parent_pending_availability(); + if relay_parent.number < earliest_rp_of_pending_availability.number { + return Err(Error::RelayParentPrecedesCandidatePendingAvailability( + relay_parent.hash, + earliest_rp_of_pending_availability.hash, + )) } - if let Some(output_head_hash) = output_head_hash { - if self.by_output_head.contains_key(&output_head_hash) { - // this is not a chain, there are multiple paths to the same state. - return true + // If it's a fork with a backed candidate in the current chain. + if let Some(other_candidate) = self.best_chain.by_parent_head.get(&parent_head_hash) { + if self.scope().get_pending_availability(other_candidate).is_some() { + // Cannot accept a fork with a candidate pending availability. + return Err(Error::ForkWithCandidatePendingAvailability(*other_candidate)) } - // trivial 0-length cycle. - if parent_head_hash == output_head_hash { - return true - } - - // this should catch any other cycles. our output state cannot already be the parent - // state of another candidate, unless this is a cycle, since the already added - // candidates form a chain. - if self.by_parent_head.contains_key(&output_head_hash) { - return true + // If the candidate is backed and in the current chain, accept only a candidate + // according to the fork selection rule. + if fork_selection_rule(other_candidate, &candidate.candidate_hash()) == Ordering::Less { + return Err(Error::ForkChoiceRule(*other_candidate)) } } - false - } + // Try seeing if the parent candidate is in the current chain or if it is the latest + // included candidate. If so, get the constraints the candidate must satisfy. + let (constraints, maybe_min_relay_parent_number) = + if let Some(parent_candidate) = self.best_chain.by_output_head.get(&parent_head_hash) { + let Some(parent_candidate) = + self.best_chain.chain.iter().find(|c| &c.candidate_hash == parent_candidate) + else { + // Should never really happen. + return Err(Error::ParentCandidateNotFound) + }; - // Checks the potential of a candidate to be added to the chain in the future. - // Verifies that the relay parent is in scope and not moving backwards and that we're not - // introducing forks or cycles with other candidates in the chain. - // `output_head_hash` is optional because we sometimes make this check before retrieving the - // collation. - fn check_potential( - &self, - relay_parent: &Hash, - parent_head_hash: Hash, - output_head_hash: Option, - ) -> bool { - if self.is_fork_or_cycle(parent_head_hash, output_head_hash) { - return false + ( + self.scope + .base_constraints + .apply_modifications(&parent_candidate.cumulative_modifications) + .map_err(Error::ComputeConstraints)?, + self.scope.ancestor(&parent_candidate.relay_parent()).map(|rp| rp.number), + ) + } else if self.scope.base_constraints.required_parent.hash() == parent_head_hash { + // It builds on the latest included candidate. + (self.scope.base_constraints.clone(), None) + } else { + // If the parent is not yet part of the chain, there's nothing else we can check for + // now. + return Ok(()) + }; + + // Check for cycles or invalid tree transitions. + if let Some(ref output_head_hash) = candidate.output_head_data_hash() { + self.check_cycles_or_invalid_tree(output_head_hash)?; } - let Some(earliest_rp) = self.earliest_relay_parent() else { return false }; + // Check against constraints if we have a full concrete candidate. + if let (Some(commitments), Some(pvd), Some(validation_code_hash)) = ( + candidate.commitments(), + candidate.persisted_validation_data(), + candidate.validation_code_hash(), + ) { + Fragment::check_against_constraints( + &relay_parent, + &constraints, + commitments, + validation_code_hash, + pvd, + ) + .map_err(Error::CheckAgainstConstraints)?; + } - let Some(relay_parent) = self.scope.ancestor(relay_parent) else { return false }; + if relay_parent.number < constraints.min_relay_parent_number { + return Err(Error::RelayParentMovedBackwards) + } - if relay_parent.number < earliest_rp.number { - return false // relay parent moved backwards. + if let Some(earliest_rp) = maybe_min_relay_parent_number { + if relay_parent.number < earliest_rp { + return Err(Error::RelayParentMovedBackwards) + } } - true + Ok(()) } - // Populate the fragment chain with candidates from CandidateStorage. - // Can be called by the constructor or when introducing a new candidate. - // If we're introducing a new candidate onto an existing chain, we may introduce more than one, - // since we may connect already existing candidates to the chain. - fn populate_chain(&mut self, storage: &CandidateStorage) { - let mut cumulative_modifications = if let Some(last_candidate) = self.chain.last() { - last_candidate.cumulative_modifications.clone() + // Once the backable chain was populated, trim the forks generated by candidates which + // are not present in the best chain. Fan this out into a full breadth-first search. + // If `starting_point` is `Some()`, start the search from the candidates having this parent head + // hash. + fn trim_uneligible_forks(&self, storage: &mut CandidateStorage, starting_point: Option) { + // Start out with the candidates in the chain. They are all valid candidates. + let mut queue: VecDeque<_> = if let Some(starting_point) = starting_point { + [(starting_point, true)].into_iter().collect() } else { - ConstraintModifications::identity() + if self.best_chain.chain.is_empty() { + [(self.scope.base_constraints.required_parent.hash(), true)] + .into_iter() + .collect() + } else { + self.best_chain.chain.iter().map(|c| (c.parent_head_data_hash, true)).collect() + } }; + // To make sure that cycles don't make us loop forever, keep track of the visited parent + // heads. + let mut visited = HashSet::new(); + + while let Some((parent, parent_has_potential)) = queue.pop_front() { + visited.insert(parent); + + let Some(children) = storage.by_parent_head.get(&parent) else { continue }; + // Cannot remove while iterating so store them here temporarily. + let mut to_remove = vec![]; + + for child_hash in children.iter() { + let Some(child) = storage.by_candidate_hash.get(child_hash) else { continue }; + + // Already visited this parent. Either is a cycle or multiple paths that lead to the + // same candidate. Either way, stop this branch to avoid looping forever. + if visited.contains(&child.output_head_data_hash) { + continue + } + + // Only keep a candidate if its full ancestry was already kept as potential and this + // candidate itself has potential. + if parent_has_potential && self.check_potential(child).is_ok() { + queue.push_back((child.output_head_data_hash, true)); + } else { + // Otherwise, remove this candidate and continue looping for its children, but + // mark the parent's potential as `false`. We only want to remove its + // children. + to_remove.push(*child_hash); + queue.push_back((child.output_head_data_hash, false)); + } + } + + for hash in to_remove { + storage.remove_candidate(&hash); + } + } + } + + // Populate the fragment chain with candidates from the supplied `CandidateStorage`. + // Can be called by the constructor or when backing a new candidate. + // When this is called, it may cause the previous chain to be completely erased or it may add + // more than one candidate. + fn populate_chain(&mut self, storage: &mut CandidateStorage) { + let mut cumulative_modifications = + if let Some(last_candidate) = self.best_chain.chain.last() { + last_candidate.cumulative_modifications.clone() + } else { + ConstraintModifications::identity() + }; let Some(mut earliest_rp) = self.earliest_relay_parent() else { return }; loop { - if self.chain.len() > self.scope.max_depth { + if self.best_chain.chain.len() > self.scope.max_depth { break; } @@ -878,113 +1199,157 @@ impl FragmentChain { }; let required_head_hash = child_constraints.required_parent.hash(); - // Even though we don't allow parachain forks under the same active leaf, they may still - // appear under different relay chain forks, hence the iterator below. - let possible_children = storage.possible_para_children(&required_head_hash); - let mut added_child = false; - for candidate in possible_children { - // Add one node to chain if - // 1. it does not introduce a fork or a cycle. - // 2. parent hash is correct. - // 3. relay-parent does not move backwards. - // 4. all non-pending-availability candidates have relay-parent in scope. - // 5. candidate outputs fulfill constraints - - if self.is_fork_or_cycle( - candidate.parent_head_data_hash(), - Some(candidate.output_head_data_hash()), - ) { - continue - } - let pending = self.scope.get_pending_availability(&candidate.candidate_hash); - let Some(relay_parent) = pending - .map(|p| p.relay_parent.clone()) - .or_else(|| self.scope.ancestor(&candidate.relay_parent)) - else { - continue - }; - - // require: candidates don't move backwards - // and only pending availability candidates can be out-of-scope. - // - // earliest_rp can be before the earliest relay parent in the scope - // when the parent is a pending availability candidate as well, but - // only other pending candidates can have a relay parent out of scope. - let min_relay_parent_number = pending - .map(|p| match self.chain.len() { - 0 => p.relay_parent.number, - _ => earliest_rp.number, - }) - .unwrap_or_else(|| earliest_rp.number); - - if relay_parent.number < min_relay_parent_number { - continue // relay parent moved backwards. - } + // Select the few possible backed/backable children which can be added to the chain + // right now. + let possible_children = storage + .possible_backed_para_children(&required_head_hash) + .filter_map(|candidate| { + // Only select a candidate if: + // 1. it does not introduce a fork or a cycle. + // 2. parent hash is correct. + // 3. relay-parent does not move backwards. + // 4. all non-pending-availability candidates have relay-parent in scope. + // 5. candidate outputs fulfill constraints + + let pending = self.scope.get_pending_availability(&candidate.candidate_hash); + let Some(relay_parent) = pending + .map(|p| p.relay_parent.clone()) + .or_else(|| self.scope.ancestor(&candidate.relay_parent)) + else { + return None + }; + + if self.check_cycles_or_invalid_tree(&candidate.output_head_data_hash).is_err() + { + return None + } - // don't add candidates if they're already present in the chain. - // this can never happen, as candidates can only be duplicated if there's a cycle - // and we shouldn't have allowed for a cycle to be chained. - if self.contains_candidate(&candidate.candidate_hash) { - continue - } + // require: candidates don't move backwards + // and only pending availability candidates can be out-of-scope. + // + // earliest_rp can be before the earliest relay parent in the scope + // when the parent is a pending availability candidate as well, but + // only other pending candidates can have a relay parent out of scope. + let min_relay_parent_number = pending + .map(|p| match self.best_chain.chain.len() { + 0 => p.relay_parent.number, + _ => earliest_rp.number, + }) + .unwrap_or_else(|| earliest_rp.number); + + if relay_parent.number < min_relay_parent_number { + return None // relay parent moved backwards. + } - let fragment = { - let mut constraints = child_constraints.clone(); - if let Some(ref p) = pending { - // overwrite for candidates pending availability as a special-case. - constraints.min_relay_parent_number = p.relay_parent.number; + // don't add candidates if they're already present in the chain. + // this can never happen, as candidates can only be duplicated if there's a + // cycle and we shouldn't have allowed for a cycle to be chained. + if self.best_chain.contains(&candidate.candidate_hash) { + return None } - let f = Fragment::new( - relay_parent.clone(), - constraints, - // It's cheap to clone because it's wrapped in an Arc - candidate.candidate.clone(), - ); - - match f { - Ok(f) => f, - Err(e) => { - gum::debug!( - target: LOG_TARGET, - err = ?e, - ?relay_parent, - candidate_hash = ?candidate.candidate_hash, - "Failed to instantiate fragment", - ); - - break - }, + let fragment = { + let mut constraints = child_constraints.clone(); + if let Some(ref p) = pending { + // overwrite for candidates pending availability as a special-case. + constraints.min_relay_parent_number = p.relay_parent.number; + } + + let f = Fragment::new( + relay_parent.clone(), + constraints, + // It's cheap to clone because it's wrapped in an Arc + candidate.candidate.clone(), + ); + + match f { + Ok(f) => f, + Err(e) => { + gum::debug!( + target: LOG_TARGET, + err = ?e, + ?relay_parent, + candidate_hash = ?candidate.candidate_hash, + "Failed to instantiate fragment", + ); + + return None + }, + } + }; + + Some(( + fragment, + candidate.candidate_hash, + candidate.output_head_data_hash, + candidate.parent_head_data_hash, + )) + }); + + // Choose the best candidate. + let best_candidate = + possible_children.min_by(|(_, ref child1, _, _), (_, ref child2, _, _)| { + // Always pick a candidate pending availability as best. + if self.scope.get_pending_availability(child1).is_some() { + Ordering::Less + } else if self.scope.get_pending_availability(child2).is_some() { + Ordering::Greater + } else { + // Otherwise, use the fork selection rule. + fork_selection_rule(child1, child2) } - }; + }); + + if let Some((fragment, candidate_hash, output_head_data_hash, parent_head_data_hash)) = + best_candidate + { + // Remove the candidate from storage. + storage.remove_candidate(&candidate_hash); // Update the cumulative constraint modifications. cumulative_modifications.stack(fragment.constraint_modifications()); // Update the earliest rp - earliest_rp = relay_parent; + earliest_rp = fragment.relay_parent().clone(); let node = FragmentNode { fragment, - candidate_hash: candidate.candidate_hash, + candidate_hash, + parent_head_data_hash, + output_head_data_hash, cumulative_modifications: cumulative_modifications.clone(), }; - self.chain.push(node); - self.candidates.insert(candidate.candidate_hash); - // We've already checked for forks and cycles. - self.by_parent_head - .insert(candidate.parent_head_data_hash(), candidate.candidate_hash); - self.by_output_head - .insert(candidate.output_head_data_hash(), candidate.candidate_hash); - added_child = true; - // We can only add one child for a candidate. (it's a chain, not a tree) - break; - } - - if !added_child { + // Add the candidate to the chain now. + self.best_chain.push(node); + } else { break } } } + + // Revert the best backable chain so that the last candidate will be one outputting the given + // `parent_head_hash`. If the `parent_head_hash` is exactly the required parent of the base + // constraints (builds on the latest included candidate), revert the entire chain. + // Return false if we couldn't find the parent head hash. + fn revert_to(&mut self, parent_head_hash: &Hash) -> bool { + let mut removed_items = None; + if &self.scope.base_constraints.required_parent.hash() == parent_head_hash { + removed_items = Some(self.best_chain.clear()); + } + + if removed_items.is_none() && self.best_chain.by_output_head.contains_key(parent_head_hash) + { + removed_items = Some(self.best_chain.revert_to_parent_hash(parent_head_hash).collect()); + } + + let Some(removed_items) = removed_items else { return false }; + + // Even if it's empty, we need to return true, because we'll be able to add a new candidate + // to the chain. + for node in &removed_items { + let _ = self.unconnected.add_candidate_entry(node.into()); + } + true + } } diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs index 26ee94d59d8ebd50bda29218c85b38476f077111..9886d19e5224efddf1ec13b630c9a4af2bd079e4 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -17,8 +17,12 @@ use super::*; use assert_matches::assert_matches; use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; -use polkadot_primitives::{BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData}; +use polkadot_primitives::{ + BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, Id as ParaId, +}; use polkadot_primitives_test_helpers as test_helpers; +use rand::{seq::SliceRandom, thread_rng}; +use std::ops::Range; fn make_constraints( min_relay_parent_number: BlockNumber, @@ -54,7 +58,7 @@ fn make_committed_candidate( let persisted_validation_data = PersistedValidationData { parent_head, relay_parent_number, - relay_parent_storage_root: Hash::repeat_byte(69), + relay_parent_storage_root: Hash::zero(), max_pov_size: 1_000_000, }; @@ -83,9 +87,20 @@ fn make_committed_candidate( (persisted_validation_data, candidate) } +fn populate_chain_from_previous_storage( + scope: &Scope, + storage: &CandidateStorage, +) -> FragmentChain { + let mut chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + let mut prev_chain = chain.clone(); + prev_chain.unconnected = storage.clone(); + + chain.populate_from_previous(&prev_chain); + chain +} + #[test] fn scope_rejects_ancestors_that_skip_blocks() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 10, hash: Hash::repeat_byte(10), @@ -104,7 +119,6 @@ fn scope_rejects_ancestors_that_skip_blocks() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -117,7 +131,6 @@ fn scope_rejects_ancestors_that_skip_blocks() { #[test] fn scope_rejects_ancestor_for_0_block() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 0, hash: Hash::repeat_byte(0), @@ -136,7 +149,6 @@ fn scope_rejects_ancestor_for_0_block() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -149,7 +161,6 @@ fn scope_rejects_ancestor_for_0_block() { #[test] fn scope_only_takes_ancestors_up_to_min() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 5, hash: Hash::repeat_byte(0), @@ -179,7 +190,6 @@ fn scope_only_takes_ancestors_up_to_min() { let pending_availability = Vec::new(); let scope = Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -194,7 +204,6 @@ fn scope_only_takes_ancestors_up_to_min() { #[test] fn scope_rejects_unordered_ancestors() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 5, hash: Hash::repeat_byte(0), @@ -225,7 +234,6 @@ fn scope_rejects_unordered_ancestors() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -257,718 +265,695 @@ fn candidate_storage_methods() { let mut wrong_pvd = pvd.clone(); wrong_pvd.max_pov_size = 0; assert_matches!( - storage.add_candidate(candidate.clone(), wrong_pvd, CandidateState::Seconded), - Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) + CandidateEntry::new( + candidate_hash, + candidate.clone(), + wrong_pvd.clone(), + CandidateState::Seconded + ), + Err(CandidateEntryError::PersistedValidationDataMismatch) + ); + assert_matches!( + CandidateEntry::new_seconded(candidate_hash, candidate.clone(), wrong_pvd), + Err(CandidateEntryError::PersistedValidationDataMismatch) ); + // Zero-length cycle. + { + let mut candidate = candidate.clone(); + candidate.commitments.head_data = HeadData(vec![1; 10]); + let mut pvd = pvd.clone(); + pvd.parent_head = HeadData(vec![1; 10]); + candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + assert_matches!( + CandidateEntry::new_seconded(candidate_hash, candidate, pvd), + Err(CandidateEntryError::ZeroLengthCycle) + ); + } assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); + assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); - // Add a valid candidate - storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded) - .unwrap(); + // Add a valid candidate. + let candidate_entry = CandidateEntry::new( + candidate_hash, + candidate.clone(), + pvd.clone(), + CandidateState::Seconded, + ) + .unwrap(); + storage.add_candidate_entry(candidate_entry.clone()).unwrap(); assert!(storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 1); - assert_eq!(storage.possible_para_children(&candidate.descriptor.para_head).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), Some(relay_parent)); + assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); assert_eq!( storage.head_data_by_hash(&candidate.descriptor.para_head).unwrap(), &candidate.commitments.head_data ); assert_eq!(storage.head_data_by_hash(&parent_head_hash).unwrap(), &pvd.parent_head); - assert_eq!(storage.is_backed(&candidate_hash), false); + // Now mark it as backed + storage.mark_backed(&candidate_hash); + // Marking it twice is fine. storage.mark_backed(&candidate_hash); - assert_eq!(storage.is_backed(&candidate_hash), true); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); // Re-adding a candidate fails. assert_matches!( - storage.add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded), - Err(CandidateStorageInsertionError::CandidateAlreadyKnown(hash)) if candidate_hash == hash + storage.add_candidate_entry(candidate_entry), + Err(Error::CandidateAlreadyKnown) ); // Remove candidate and re-add it later in backed state. storage.remove_candidate(&candidate_hash); assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); + + // Removing it twice is fine. + storage.remove_candidate(&candidate_hash); + assert!(!storage.contains(&candidate_hash)); + assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Backed) + .add_pending_availability_candidate(candidate_hash, candidate.clone(), pvd) .unwrap(); - assert_eq!(storage.is_backed(&candidate_hash), true); - - // Test retain - storage.retain(|_| true); assert!(storage.contains(&candidate_hash)); - storage.retain(|_| false); - assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); - assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); + + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + + // Now add a second candidate in Seconded state. This will be a fork. + let (pvd_2, candidate_2) = make_committed_candidate( + ParaId::from(5u32), + relay_parent, + 8, + vec![4, 5, 6].into(), + vec![2, 3, 4].into(), + 7, + ); + let candidate_hash_2 = candidate_2.hash(); + let candidate_entry_2 = + CandidateEntry::new_seconded(candidate_hash_2, candidate_2, pvd_2).unwrap(); + + storage.add_candidate_entry(candidate_entry_2).unwrap(); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + + // Now mark it as backed. + storage.mark_backed(&candidate_hash_2); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + [candidate_hash, candidate_hash_2].into_iter().collect() + ); } #[test] -fn populate_and_extend_from_storage_empty() { +fn init_and_populate_from_empty() { // Empty chain and empty storage. - let storage = CandidateStorage::default(); let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); let scope = Scope::with_ancestors( - ParaId::from(2), RelayChainBlockInfo { number: 1, hash: Hash::repeat_byte(1), storage_root: Hash::repeat_byte(2), }, base_constraints, - pending_availability, + Vec::new(), 4, vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 0); + + let mut new_chain = FragmentChain::init(scope, CandidateStorage::default()); + new_chain.populate_from_previous(&chain); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 0); } #[test] -fn populate_and_extend_from_storage_with_existing_empty_to_vec() { +fn test_populate_and_check_potential() { let mut storage = CandidateStorage::default(); let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_c = Hash::repeat_byte(3); + let relay_parent_x = Hash::repeat_byte(1); + let relay_parent_y = Hash::repeat_byte(2); + let relay_parent_z = Hash::repeat_byte(3); + let relay_parent_x_info = + RelayChainBlockInfo { number: 0, hash: relay_parent_x, storage_root: Hash::zero() }; + let relay_parent_y_info = + RelayChainBlockInfo { number: 1, hash: relay_parent_y, storage_root: Hash::zero() }; + let relay_parent_z_info = + RelayChainBlockInfo { number: 2, hash: relay_parent_z, storage_root: Hash::zero() }; + + let ancestors = vec![ + // These need to be ordered in reverse. + relay_parent_y_info.clone(), + relay_parent_x_info.clone(), + ]; + + let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); + // Candidates A -> B -> C. They are all backed let (pvd_a, candidate_a) = make_committed_candidate( para_id, - relay_parent_a, - 0, + relay_parent_x_info.hash, + relay_parent_x_info.number, vec![0x0a].into(), vec![0x0b].into(), - 0, + relay_parent_x_info.number, ); let candidate_a_hash = candidate_a.hash(); - + let candidate_a_entry = + CandidateEntry::new(candidate_a_hash, candidate_a, pvd_a.clone(), CandidateState::Backed) + .unwrap(); + storage.add_candidate_entry(candidate_a_entry.clone()).unwrap(); let (pvd_b, candidate_b) = make_committed_candidate( para_id, - relay_parent_b, - 1, + relay_parent_y_info.hash, + relay_parent_y_info.number, vec![0x0b].into(), vec![0x0c].into(), - 1, + relay_parent_y_info.number, ); let candidate_b_hash = candidate_b.hash(); - + let candidate_b_entry = + CandidateEntry::new(candidate_b_hash, candidate_b, pvd_b, CandidateState::Backed).unwrap(); + storage.add_candidate_entry(candidate_b_entry.clone()).unwrap(); let (pvd_c, candidate_c) = make_committed_candidate( para_id, - relay_parent_c, - 2, + relay_parent_z_info.hash, + relay_parent_z_info.number, vec![0x0c].into(), vec![0x0d].into(), - 2, + relay_parent_z_info.number, ); let candidate_c_hash = candidate_c.hash(); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_c_info = RelayChainBlockInfo { - number: pvd_c.relay_parent_number, - hash: relay_parent_c, - storage_root: pvd_c.relay_parent_storage_root, - }; - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); - - let ancestors = vec![ - // These need to be ordered in reverse. - relay_parent_b_info.clone(), - relay_parent_a_info.clone(), - ]; - - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Backed) - .unwrap(); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Backed) - .unwrap(); + let candidate_c_entry = + CandidateEntry::new(candidate_c_hash, candidate_c, pvd_c, CandidateState::Backed).unwrap(); + storage.add_candidate_entry(candidate_c_entry.clone()).unwrap(); // Candidate A doesn't adhere to the base constraints. { for wrong_constraints in [ // Different required parent - make_constraints(0, vec![0], vec![0x0e].into()), + make_constraints( + relay_parent_x_info.number, + vec![relay_parent_x_info.number], + vec![0x0e].into(), + ), // Min relay parent number is wrong - make_constraints(1, vec![0], vec![0x0a].into()), + make_constraints(relay_parent_y_info.number, vec![0], vec![0x0a].into()), ] { let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), wrong_constraints.clone(), - pending_availability.clone(), + vec![], 4, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); + let chain = populate_chain_from_previous_storage(&scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + assert!(chain.best_chain_vec().is_empty()); // If the min relay parent number is wrong, candidate A can never become valid. // Otherwise, if only the required parent doesn't match, candidate A is still a // potential candidate. - if wrong_constraints.min_relay_parent_number == 1 { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::None + if wrong_constraints.min_relay_parent_number == relay_parent_y_info.number { + // If A is not a potential candidate, its descendants will also not be added. + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) ); + // However, if taken independently, both B and C still have potential, since we + // don't know that A doesn't. + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); } else { assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } - - // All other candidates can always be potential candidates. - for (candidate, pvd) in - [(candidate_b.clone(), pvd_b.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_a_hash, candidate_b_hash, candidate_c_hash].into_iter().collect() ); } } } - // Various max depths. + // Various depths { - // depth is 0, will only allow 1 candidate + // Depth is 0, only allows one candidate, but the others will be kept as potential. let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 0, ancestors.clone(), ) .unwrap(); - // Before populating the chain, all candidates are potential candidates. However, they can - // only be added as connected candidates, because only one candidates is allowed by max - // depth - let chain = FragmentChain::populate(scope.clone(), &CandidateStorage::default()); - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &CandidateStorage::default(), - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::IfConnected - ); - } - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); - // since depth is maxed out, we can't add more potential candidates - // candidate A is no longer a potential candidate because it's already present. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_b_hash, candidate_c_hash].into_iter().collect() + ); // depth is 1, allows two candidates let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 1, ancestors.clone(), ) .unwrap(); - // Before populating the chain, all candidates can be added as potential. - let mut modified_storage = CandidateStorage::default(); - let chain = FragmentChain::populate(scope.clone(), &modified_storage); - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } - // Add an unconnected candidate. We now should only allow a Connected candidate, because max - // depth only allows one more candidate. - modified_storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) - .unwrap(); - let chain = FragmentChain::populate(scope.clone(), &modified_storage); - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::IfConnected - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); - // Now try populating from all candidates. - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // since depth is maxed out, we can't add more potential candidates - // candidate A and B are no longer a potential candidate because they're already present. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_c_hash].into_iter().collect() + ); - // depths larger than 2, allows all candidates + // depth is larger than 2, allows all three candidates for depth in 2..6 { let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], depth, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - // Candidates are no longer potential candidates because they're already part of the - // chain. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); } } - // Wrong relay parents + // Relay parents out of scope { - // Candidates A has relay parent out of scope. - let ancestors_without_a = vec![relay_parent_b_info.clone()]; + // Candidate A has relay parent out of scope. Candidates B and C will also be deleted since + // they form a chain with A. + let ancestors_without_x = vec![relay_parent_y_info.clone()]; let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, - ancestors_without_a, + ancestors_without_x, ) .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); - - // Candidate A is not a potential candidate, but candidates B and C still are. - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::None + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) ); - for (candidate, pvd) in - [(candidate_b.clone(), pvd_b.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } + // However, if taken independently, both B and C still have potential, since we + // don't know that A doesn't. + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); - // Candidate C has the same relay parent as candidate A's parent. Relay parent not allowed - // to move backwards - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_a, - 1, - vec![0x0c].into(), - vec![0x0d].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); + // Candidates A and B have relay parents out of scope. Candidate C will also be deleted + // since it forms a chain with A and B. let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, - ancestors.clone(), + vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); + let chain = populate_chain_from_previous_storage(&scope, &storage); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_b_entry), + Err(Error::RelayParentNotInScope(_, _)) ); + // However, if taken independently, C still has potential, since we + // don't know that A and B don't + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); } - // Parachain fork and cycles are not allowed. + // Parachain cycle is not allowed. Make C have the same parent as A. { - // Candidate C has the same parent as candidate B. - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_c, - 2, - vec![0x0b].into(), - vec![0x0d].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - // We'll either have A->B or A->C. It's not deterministic because CandidateStorage uses - // HashSets and HashMaps. - if chain.to_vec() == vec![candidate_a_hash, candidate_b_hash] { - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } else if chain.to_vec() == vec![candidate_a_hash, wrong_candidate_c.hash()] { - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, wrong_candidate_c.hash()]); - // Candidate B is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate_b.hash(), - &candidate_b.descriptor.relay_parent, - pvd_b.parent_head.hash(), - Some(candidate_b.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } else { - panic!("Unexpected chain: {:?}", chain.to_vec()); - } - - // Candidate C is a 0-length cycle. - // Candidate C has the same parent as candidate B. let mut modified_storage = storage.clone(); modified_storage.remove_candidate(&candidate_c_hash); let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( para_id, - relay_parent_c, - 2, - vec![0x0c].into(), + relay_parent_z_info.hash, + relay_parent_z_info.number, vec![0x0c].into(), - 2, + vec![0x0a].into(), + relay_parent_z_info.number, ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c.hash(), + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - - // Candidate C points back to the pre-state of candidate C. - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_c, - 2, - vec![0x0c].into(), - vec![0x0b].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::Cycle) ); + // However, if taken independently, C still has potential, since we don't know A and B. + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&wrong_candidate_c_entry).is_ok()); } - // Test with candidates pending availability - { - // Valid options - for pending in [ - vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }], - vec![ - PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_b_hash, - relay_parent: relay_parent_b_info.clone(), - }, - ], - vec![ - PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_b_hash, - relay_parent: relay_parent_b_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_c_hash, - relay_parent: relay_parent_c_info.clone(), - }, - ], - ] { - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending, - 3, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + // Candidate C has the same relay parent as candidate A's parent. Relay parent not allowed + // to move backwards + let mut modified_storage = storage.clone(); + modified_storage.remove_candidate(&candidate_c_hash); + let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0c].into(), + vec![0x0d].into(), + 0, + ); + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c.hash(), + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 4, + ancestors.clone(), + ) + .unwrap(); - // Relay parents of pending availability candidates can be out of scope - // Relay parent of candidate A is out of scope. - let ancestors_without_a = vec![relay_parent_b_info.clone()]; - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }], - 4, - ancestors_without_a, - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); - // Even relay parents of pending availability candidates which are out of scope cannot move - // backwards. - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::RelayParentMovedBackwards) + ); + + // Candidate C is an unconnected candidate. + // C's relay parent is allowed to move backwards from B's relay parent, because C may later on + // trigger a reorg and B may get removed. + let mut modified_storage = storage.clone(); + modified_storage.remove_candidate(&candidate_c_hash); + let (unconnected_pvd_c, unconnected_candidate_c) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0d].into(), + vec![0x0e].into(), + 0, + ); + let unconnected_candidate_c_hash = unconnected_candidate_c.hash(); + let unconnected_candidate_c_entry = CandidateEntry::new( + unconnected_candidate_c_hash, + unconnected_candidate_c, + unconnected_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage + .add_candidate_entry(unconnected_candidate_c_entry.clone()) + .unwrap(); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 4, + ancestors.clone(), + ) + .unwrap(); + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&unconnected_candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [unconnected_candidate_c_hash].into_iter().collect() + ); + + // Candidate A is a pending availability candidate and Candidate C is an unconnected candidate, + // C's relay parent is not allowed to move backwards from A's relay parent because we're sure A + // will not get removed in the future, as it's already on-chain (unless it times out + // availability, a case for which we don't care to optimise for) + + modified_storage.remove_candidate(&candidate_a_hash); + let (modified_pvd_a, modified_candidate_a) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0x0a].into(), + vec![0x0b].into(), + relay_parent_y_info.number, + ); + let modified_candidate_a_hash = modified_candidate_a.hash(); + modified_storage + .add_candidate_entry( + CandidateEntry::new( + modified_candidate_a_hash, + modified_candidate_a, + modified_pvd_a, + CandidateState::Backed, + ) + .unwrap(), + ) + .unwrap(); + + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: modified_candidate_a_hash, + relay_parent: relay_parent_y_info.clone(), + }], + 4, + ancestors.clone(), + ) + .unwrap(); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![modified_candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&unconnected_candidate_c_entry), + Err(Error::RelayParentPrecedesCandidatePendingAvailability(_, _)) + ); + + // Not allowed to fork from a candidate pending availability + let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0x0a].into(), + vec![0x0b2].into(), + 0, + ); + let wrong_candidate_c_hash = wrong_candidate_c.hash(); + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c_hash, + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); + + // Does not even matter if the fork selection rule would have picked up the new candidate, as + // the other is already pending availability. + assert_eq!( + fork_selection_rule(&wrong_candidate_c_hash, &modified_candidate_a_hash), + Ordering::Less + ); + + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: modified_candidate_a_hash, + relay_parent: relay_parent_y_info.clone(), + }], + 4, + ancestors.clone(), + ) + .unwrap(); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![modified_candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::ForkWithCandidatePendingAvailability(_)) + ); + + // Test with candidates pending availability + { + // Valid options + for pending in [ + vec![PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }], + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info.clone(), + }, + ], + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_c_hash, + relay_parent: relay_parent_z_info.clone(), + }, + ], + ] { + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + pending, + 3, + ancestors.clone(), + ) + .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); + } + + // Relay parents of pending availability candidates can be out of scope + // Relay parent of candidate A is out of scope. + let ancestors_without_x = vec![relay_parent_y_info.clone()]; + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }], + 4, + ancestors_without_x, + ) + .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); + + // Even relay parents of pending availability candidates which are out of scope cannot + // move backwards. + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), base_constraints.clone(), vec![ PendingAvailability { candidate_hash: candidate_a_hash, relay_parent: RelayChainBlockInfo { - hash: relay_parent_a_info.hash, + hash: relay_parent_x_info.hash, number: 1, - storage_root: relay_parent_a_info.storage_root, + storage_root: relay_parent_x_info.storage_root, }, }, PendingAvailability { candidate_hash: candidate_b_hash, relay_parent: RelayChainBlockInfo { - hash: relay_parent_b_info.hash, + hash: relay_parent_y_info.hash, number: 0, - storage_root: relay_parent_b_info.storage_root, + storage_root: relay_parent_y_info.storage_root, }, }, ], @@ -976,271 +961,418 @@ fn populate_and_extend_from_storage_with_existing_empty_to_vec() { vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); } -} -#[test] -fn extend_from_storage_with_existing_to_vec() { - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_d = Hash::repeat_byte(3); + // More complex case: + // max_depth is 2 (a chain of max depth 3). + // A -> B -> C are the best backable chain. + // D is backed but would exceed the max depth. + // F is unconnected and seconded. + // A1 has same parent as A, is backed but has a higher candidate hash. It'll therefore be + // deleted. + // A1 has underneath a subtree that will all need to be trimmed. A1 -> B1. B1 -> C1 + // and B1 -> C2. (C1 is backed). + // A2 is seconded but is kept because it has a lower candidate hash than A. + // A2 points to B2, which is backed. + // + // Check that D, F, A2 and B2 are kept as unconnected potential candidates. - let (pvd_a, candidate_a) = make_committed_candidate( + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 2, + ancestors.clone(), + ) + .unwrap(); + + // Candidate D + let (pvd_d, candidate_d) = make_committed_candidate( para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0d].into(), + vec![0x0e].into(), + relay_parent_z_info.number, ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( + let candidate_d_hash = candidate_d.hash(); + let candidate_d_entry = + CandidateEntry::new(candidate_d_hash, candidate_d, pvd_d, CandidateState::Backed).unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_d_entry) + .is_ok()); + storage.add_candidate_entry(candidate_d_entry).unwrap(); + + // Candidate F + let (pvd_f, candidate_f) = make_committed_candidate( para_id, - relay_parent_b, - 1, - vec![0x0b].into(), - vec![0x0c].into(), - 1, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0f].into(), + vec![0xf1].into(), + 1000, ); - let candidate_b_hash = candidate_b.hash(); + let candidate_f_hash = candidate_f.hash(); + let candidate_f_entry = + CandidateEntry::new(candidate_f_hash, candidate_f, pvd_f, CandidateState::Seconded) + .unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_f_entry) + .is_ok()); + storage.add_candidate_entry(candidate_f_entry.clone()).unwrap(); - let (pvd_c, candidate_c) = make_committed_candidate( + // Candidate A1 + let (pvd_a1, candidate_a1) = make_committed_candidate( para_id, - // Use the same relay parent number as B to test that it doesn't need to change between - // candidates. - relay_parent_b, - 1, - vec![0x0c].into(), - vec![0x0d].into(), - 1, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0a].into(), + vec![0xb1].into(), + relay_parent_x_info.number, ); - let candidate_c_hash = candidate_c.hash(); + let candidate_a1_hash = candidate_a1.hash(); + let candidate_a1_entry = + CandidateEntry::new(candidate_a1_hash, candidate_a1, pvd_a1, CandidateState::Backed) + .unwrap(); + // Candidate A1 is created so that its hash is greater than the candidate A hash. + assert_eq!(fork_selection_rule(&candidate_a_hash, &candidate_a1_hash), Ordering::Less); - // Candidate D will never be added to the chain. - let (pvd_d, candidate_d) = make_committed_candidate( - para_id, - relay_parent_d, - 2, - vec![0x0e].into(), - vec![0x0f].into(), - 1, + assert_matches!( + populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_a1_entry), + Err(Error::ForkChoiceRule(other)) if candidate_a_hash == other ); - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_d_info = RelayChainBlockInfo { - number: pvd_d.relay_parent_number, - hash: relay_parent_d, - storage_root: pvd_d.relay_parent_storage_root, - }; + storage.add_candidate_entry(candidate_a1_entry.clone()).unwrap(); - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); + // Candidate B1. + let (pvd_b1, candidate_b1) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xb1].into(), + vec![0xc1].into(), + relay_parent_x_info.number, + ); + let candidate_b1_hash = candidate_b1.hash(); + let candidate_b1_entry = + CandidateEntry::new(candidate_b1_hash, candidate_b1, pvd_b1, CandidateState::Seconded) + .unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_b1_entry) + .is_ok()); - let ancestors = vec![ - // These need to be ordered in reverse. - relay_parent_b_info.clone(), - relay_parent_a_info.clone(), - ]; + storage.add_candidate_entry(candidate_b1_entry).unwrap(); - // Already had A and C in the storage. Introduce B, which should add both B and C to the chain - // now. - { - let mut storage = CandidateStorage::default(); - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) + // Candidate C1. + let (pvd_c1, candidate_c1) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xc1].into(), + vec![0xd1].into(), + relay_parent_x_info.number, + ); + let candidate_c1_hash = candidate_c1.hash(); + let candidate_c1_entry = + CandidateEntry::new(candidate_c1_hash, candidate_c1, pvd_c1, CandidateState::Backed) .unwrap(); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Seconded) + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_c1_entry) + .is_ok()); + + storage.add_candidate_entry(candidate_c1_entry).unwrap(); + + // Candidate C2. + let (pvd_c2, candidate_c2) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xc1].into(), + vec![0xd2].into(), + relay_parent_x_info.number, + ); + let candidate_c2_hash = candidate_c2.hash(); + let candidate_c2_entry = + CandidateEntry::new(candidate_c2_hash, candidate_c2, pvd_c2, CandidateState::Seconded) .unwrap(); - storage - .add_candidate(candidate_d.clone(), pvd_d.clone(), CandidateState::Seconded) + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_c2_entry) + .is_ok()); + storage.add_candidate_entry(candidate_c2_entry).unwrap(); + + // Candidate A2. + let (pvd_a2, candidate_a2) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0a].into(), + vec![0xb3].into(), + relay_parent_x_info.number, + ); + let candidate_a2_hash = candidate_a2.hash(); + let candidate_a2_entry = + CandidateEntry::new(candidate_a2_hash, candidate_a2, pvd_a2, CandidateState::Seconded) .unwrap(); + // Candidate A2 is created so that its hash is greater than the candidate A hash. + assert_eq!(fork_selection_rule(&candidate_a2_hash, &candidate_a_hash), Ordering::Less); - let scope = Scope::with_ancestors( - para_id, - relay_parent_d_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_a2_entry) + .is_ok()); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) + storage.add_candidate_entry(candidate_a2_entry).unwrap(); + + // Candidate B2. + let (pvd_b2, candidate_b2) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0xb3].into(), + vec![0xb4].into(), + relay_parent_y_info.number, + ); + let candidate_b2_hash = candidate_b2.hash(); + let candidate_b2_entry = + CandidateEntry::new(candidate_b2_hash, candidate_b2, pvd_b2, CandidateState::Backed) .unwrap(); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_b2_entry) + .is_ok()); + storage.add_candidate_entry(candidate_b2_entry).unwrap(); - // Already had A and B in the chain. Introduce C. + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_d_hash, candidate_f_hash, candidate_a2_hash, candidate_b2_hash] + .into_iter() + .collect() + ); + // Cannot add as potential an already present candidate (whether it's in the best chain or in + // unconnected storage) + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::CandidateAlreadyKnown) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_f_entry), + Err(Error::CandidateAlreadyKnown) + ); + + // Simulate a best chain reorg by backing a2. { - let mut storage = CandidateStorage::default(); - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_d.clone(), pvd_d.clone(), CandidateState::Seconded) - .unwrap(); + let mut chain = chain.clone(); + chain.candidate_backed(&candidate_a2_hash); + assert_eq!(chain.best_chain_vec(), vec![candidate_a2_hash, candidate_b2_hash]); + // F is kept as it was truly unconnected. The rest will be trimmed. + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_f_hash].into_iter().collect() + ); - let scope = Scope::with_ancestors( - para_id, - relay_parent_d_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), + // A and A1 will never have potential again. + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a1_entry), + Err(Error::ForkChoiceRule(_)) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::ForkChoiceRule(_)) + ); + } + + // Candidate F has an invalid hrmp watermark. however, it was not checked beforehand as we don't + // have its parent yet. Add its parent now. This will not impact anything as E is not yet part + // of the best chain. + + let (pvd_e, candidate_e) = make_committed_candidate( + para_id, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0e].into(), + vec![0x0f].into(), + relay_parent_z_info.number, + ); + let candidate_e_hash = candidate_e.hash(); + storage + .add_candidate_entry( + CandidateEntry::new(candidate_e_hash, candidate_e, pvd_e, CandidateState::Seconded) + .unwrap(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [ + candidate_d_hash, + candidate_f_hash, + candidate_a2_hash, + candidate_b2_hash, + candidate_e_hash + ] + .into_iter() + .collect() + ); + + // Simulate the fact that candidates A, B, C are now pending availability. + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info, + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info, + }, + PendingAvailability { + candidate_hash: candidate_c_hash, + relay_parent: relay_parent_z_info.clone(), + }, + ], + 2, + ancestors.clone(), + ) + .unwrap(); + + // A2 and B2 will now be trimmed + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_d_hash, candidate_f_hash, candidate_e_hash].into_iter().collect() + ); + // Cannot add as potential an already pending availability candidate + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::CandidateAlreadyKnown) + ); + + // Simulate the fact that candidates A, B and C have been included. + + let base_constraints = make_constraints(0, vec![0], HeadData(vec![0x0d])); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 2, + ancestors.clone(), + ) + .unwrap(); + + let prev_chain = chain; + let mut chain = FragmentChain::init(scope, CandidateStorage::default()); + chain.populate_from_previous(&prev_chain); + assert_eq!(chain.best_chain_vec(), vec![candidate_d_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_e_hash, candidate_f_hash].into_iter().collect() + ); + + // Mark E as backed. F will be dropped for invalid watermark. No other unconnected candidates. + chain.candidate_backed(&candidate_e_hash); + assert_eq!(chain.best_chain_vec(), vec![candidate_d_hash, candidate_e_hash]); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_f_entry), + Err(Error::CheckAgainstConstraints(_)) + ); } #[test] -fn test_find_ancestor_path_and_find_backable_chain_empty_to_vec() { - let para_id = ParaId::from(5u32); +fn test_find_ancestor_path_and_find_backable_chain_empty_best_chain() { let relay_parent = Hash::repeat_byte(1); let required_parent: HeadData = vec![0xff].into(); let max_depth = 10; // Empty chain - let storage = CandidateStorage::default(); let base_constraints = make_constraints(0, vec![0], required_parent.clone()); let relay_parent_info = RelayChainBlockInfo { number: 0, hash: relay_parent, storage_root: Hash::zero() }; - let scope = Scope::with_ancestors( - para_id, - relay_parent_info, - base_constraints, - vec![], - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); + let scope = + Scope::with_ancestors(relay_parent_info, base_constraints, vec![], max_depth, vec![]) + .unwrap(); + let chain = FragmentChain::init(scope, CandidateStorage::default()); + assert_eq!(chain.best_chain_len(), 0); assert_eq!(chain.find_ancestor_path(Ancestors::new()), 0); - assert_eq!(chain.find_backable_chain(Ancestors::new(), 2, |_| true), vec![]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 2), vec![]); // Invalid candidate. let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!(chain.find_backable_chain(ancestors, 2, |_| true), vec![]); + assert_eq!(chain.find_backable_chain(ancestors, 2), vec![]); } #[test] -fn test_find_ancestor_path_and_find_backable_to_vec() { +fn test_find_ancestor_path_and_find_backable_chain() { let para_id = ParaId::from(5u32); let relay_parent = Hash::repeat_byte(1); let required_parent: HeadData = vec![0xff].into(); let max_depth = 5; let relay_parent_number = 0; - let relay_parent_storage_root = Hash::repeat_byte(69); + let relay_parent_storage_root = Hash::zero(); let mut candidates = vec![]; - - // Candidate 0 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - required_parent.clone(), - vec![0].into(), - 0, - )); - // Candidate 1 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![0].into(), - vec![1].into(), - 0, - )); - // Candidate 2 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![1].into(), - vec![2].into(), - 0, - )); - // Candidate 3 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![2].into(), - vec![3].into(), - 0, - )); - // Candidate 4 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![3].into(), - vec![4].into(), - 0, - )); - // Candidate 5 + + // Candidate 0 candidates.push(make_committed_candidate( para_id, relay_parent, 0, - vec![4].into(), - vec![5].into(), + required_parent.clone(), + vec![0].into(), 0, )); - let base_constraints = make_constraints(0, vec![0], required_parent.clone()); + // Candidates 1..=5 + for index in 1..=5 { + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![index - 1].into(), + vec![index].into(), + 0, + )); + } + let mut storage = CandidateStorage::default(); + for (pvd, candidate) in candidates.iter() { + storage + .add_candidate_entry( + CandidateEntry::new_seconded(candidate.hash(), candidate.clone(), pvd.clone()) + .unwrap(), + ) + .unwrap(); + } + + let candidates = candidates + .into_iter() + .map(|(_pvd, candidate)| candidate.hash()) + .collect::>(); + let hashes = + |range: Range| range.map(|i| (candidates[i], relay_parent)).collect::>(); + let relay_parent_info = RelayChainBlockInfo { number: relay_parent_number, hash: relay_parent, storage_root: relay_parent_storage_root, }; - for (pvd, candidate) in candidates.iter() { - storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded) - .unwrap(); - } - let candidates = candidates.into_iter().map(|(_pvd, candidate)| candidate).collect::>(); + let base_constraints = make_constraints(0, vec![0], required_parent.clone()); let scope = Scope::with_ancestors( - para_id, relay_parent_info.clone(), base_constraints.clone(), vec![], @@ -1248,506 +1380,140 @@ fn test_find_ancestor_path_and_find_backable_to_vec() { vec![], ) .unwrap(); - let chain = FragmentChain::populate(scope, &storage); + let mut chain = populate_chain_from_previous_storage(&scope, &storage); + // For now, candidates are only seconded, not backed. So the best chain is empty and no + // candidate will be returned. assert_eq!(candidates.len(), 6); - assert_eq!(chain.to_vec().len(), 6); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 6); + + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + // Do tests with only a couple of candidates being backed. + { + let mut chain = chain.clone(); + chain.candidate_backed(&&candidates[5]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + chain.candidate_backed(&&candidates[3]); + chain.candidate_backed(&&candidates[4]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + chain.candidate_backed(&&candidates[1]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + chain.candidate_backed(&&candidates[0]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 1), hashes(0..1)); + for count in 2..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count), hashes(0..2)); + } + + // Now back the missing piece. + chain.candidate_backed(&&candidates[2]); + assert_eq!(chain.best_chain_len(), 6); + for count in 0..10 { + assert_eq!( + chain.find_backable_chain(Ancestors::new(), count), + (0..6) + .take(count as usize) + .map(|i| (candidates[i], relay_parent)) + .collect::>() + ); + } + } + + // Now back all candidates. Back them in a random order. The result should always be the same. + let mut candidates_shuffled = candidates.clone(); + candidates_shuffled.shuffle(&mut thread_rng()); + for candidate in candidates.iter() { + chain.candidate_backed(candidate); + storage.mark_backed(candidate); + } // No ancestors supplied. assert_eq!(chain.find_ancestor_path(Ancestors::new()), 0); - assert_eq!(chain.find_backable_chain(Ancestors::new(), 0, |_| true), vec![]); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 1, |_| true), - [0].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 2, |_| true), - [0, 1].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 5, |_| true), - [0, 1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 0), vec![]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 1), hashes(0..1)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 2), hashes(0..2)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 5), hashes(0..5)); for count in 6..10 { - assert_eq!( - chain.find_backable_chain(Ancestors::new(), count, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), count), hashes(0..6)); } - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 7, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 10, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 7), hashes(0..6)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 10), hashes(0..6)); // Ancestor which is not part of the chain. Will be ignored. let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - let ancestors: Ancestors = - [candidates[1].hash(), CandidateHash::default()].into_iter().collect(); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); + + let ancestors: Ancestors = [candidates[1], CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - let ancestors: Ancestors = - [candidates[0].hash(), CandidateHash::default()].into_iter().collect(); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); + + let ancestors: Ancestors = [candidates[0], CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 1); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(1..5)); // Ancestors which are part of the chain but don't form a path from root. Will be ignored. - let ancestors: Ancestors = [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + let ancestors: Ancestors = [candidates[1], candidates[2]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); // Valid ancestors. - let ancestors: Ancestors = [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] - .into_iter() - .collect(); + let ancestors: Ancestors = [candidates[2], candidates[0], candidates[1]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 3); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 2, |_| true), - [3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 2), hashes(3..5)); for count in 3..10 { - assert_eq!( - chain.find_backable_chain(ancestors.clone(), count, |_| true), - [3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), count), hashes(3..6)); } // Valid ancestors with candidates which have been omitted due to timeouts - let ancestors: Ancestors = [candidates[0].hash(), candidates[2].hash()].into_iter().collect(); + let ancestors: Ancestors = [candidates[0], candidates[2]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 1); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 3, |_| true), - [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 4, |_| true), - [1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 3), hashes(1..4)); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 4), hashes(1..5)); for count in 5..10 { - assert_eq!( - chain.find_backable_chain(ancestors.clone(), count, |_| true), - [1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), count), hashes(1..6)); } - let ancestors: Ancestors = [candidates[0].hash(), candidates[1].hash(), candidates[3].hash()] - .into_iter() - .collect(); + let ancestors: Ancestors = [candidates[0], candidates[1], candidates[3]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 2); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 4, |_| true), - [2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 4), hashes(2..6)); // Requested count is 0. - assert_eq!(chain.find_backable_chain(ancestors, 0, |_| true), vec![]); - - // Stop when we've found a candidate for which pred returns false. - let ancestors: Ancestors = [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] - .into_iter() - .collect(); - for count in 1..10 { - assert_eq!( - // Stop at 4. - chain.find_backable_chain(ancestors.clone(), count, |hash| hash != - &candidates[4].hash()), - [3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - } + assert_eq!(chain.find_backable_chain(ancestors, 0), vec![]); // Stop when we've found a candidate which is pending availability { let scope = Scope::with_ancestors( - para_id, relay_parent_info.clone(), base_constraints, // Mark the third candidate as pending availability vec![PendingAvailability { - candidate_hash: candidates[3].hash(), + candidate_hash: candidates[3], relay_parent: relay_parent_info, }], max_depth, vec![], ) .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - let ancestors: Ancestors = - [candidates[0].hash(), candidates[1].hash()].into_iter().collect(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + let ancestors: Ancestors = [candidates[0], candidates[1]].into_iter().collect(); assert_eq!( // Stop at 4. - chain.find_backable_chain(ancestors.clone(), 3, |_| true), - [2].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - } -} - -#[test] -fn hypothetical_membership() { - let mut storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, - ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0b].into(), - vec![0x0c].into(), - 0, - ); - let candidate_b_hash = candidate_b.hash(); - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - - let max_depth = 4; - storage.add_candidate(candidate_a, pvd_a, CandidateState::Seconded).unwrap(); - storage.add_candidate(candidate_b, pvd_b, CandidateState::Seconded).unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info.clone(), - base_constraints.clone(), - vec![], - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert_eq!(chain.to_vec().len(), 2); - - // Check candidates which are already present - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_a_hash, - }, - &storage, - )); - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_b_hash, - }, - &storage, - )); - - // Forks not allowed. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(21)), - }, - &storage, - )); - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(22)), - }, - &storage, - )); - - // Unknown candidate which builds on top of the current chain. - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0c]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); - - // Unknown unconnected candidate which may be valid. - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0e]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); - - // The number of unconnected candidates is limited (chain.len() + unconnected) <= max_depth - { - // C will be an unconnected candidate. - let (pvd_c, candidate_c) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0e].into(), - vec![0x0f].into(), - 0, - ); - let candidate_c_hash = candidate_c.hash(); - - // Add an invalid candidate in the storage. This would introduce a fork. Just to test that - // it's ignored. - let (invalid_pvd, invalid_candidate) = make_committed_candidate( - para_id, - relay_parent_a, - 1, - vec![0x0a].into(), - vec![0x0b].into(), - 0, + chain.find_backable_chain(ancestors.clone(), 3), + hashes(2..3) ); - - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info, - base_constraints, - vec![], - 2, - vec![], - ) - .unwrap(); - let mut storage = storage.clone(); - storage.add_candidate(candidate_c, pvd_c, CandidateState::Seconded).unwrap(); - - let chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - - storage - .add_candidate(invalid_candidate, invalid_pvd, CandidateState::Seconded) - .unwrap(); - - // Check that C is accepted as a potential unconnected candidate. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0e]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_hash: candidate_c_hash, - candidate_para: para_id - }, - &storage, - )); - - // Since C is already an unconnected candidate in the storage. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0f]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); } } - -#[test] -fn hypothetical_membership_stricter_on_complete_candidates() { - let storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 1000, // watermark is illegal - ); - - let candidate_a_hash = candidate_a.hash(); - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - - let max_depth = 4; - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info, - base_constraints, - pending_availability, - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_a_hash, - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Complete { - receipt: Arc::new(candidate_a), - persisted_validation_data: pvd_a, - candidate_hash: candidate_a_hash, - }, - &storage, - )); -} - -#[test] -fn hypothetical_membership_with_pending_availability_in_scope() { - let mut storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_c = Hash::repeat_byte(3); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, - ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( - para_id, - relay_parent_b, - 1, - vec![0x0b].into(), - vec![0x0c].into(), - 1, - ); - - // Note that relay parent `a` is not allowed. - let base_constraints = make_constraints(1, vec![], vec![0x0a].into()); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let pending_availability = vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info, - }]; - - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_c_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number + 1, - hash: relay_parent_c, - storage_root: Hash::zero(), - }; - - let max_depth = 4; - storage.add_candidate(candidate_a, pvd_a, CandidateState::Seconded).unwrap(); - storage.add_candidate(candidate_b, pvd_b, CandidateState::Backed).unwrap(); - storage.mark_backed(&candidate_a_hash); - - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info, - base_constraints, - pending_availability, - max_depth, - vec![relay_parent_b_info], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert_eq!(chain.to_vec().len(), 2); - - let candidate_d_hash = CandidateHash(Hash::repeat_byte(0xAA)); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_hash: candidate_a_hash, - candidate_para: para_id - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_c, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_c, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0c]).hash(), - candidate_relay_parent: relay_parent_b, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); -} diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index e4b6deffdf4a5280e23c8c4a81bdf2ae56bbb422..b8b5f159e71cdd9a6f01f1a4a30bad5f9f2dc912 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -26,9 +26,11 @@ //! //! This subsystem also handles concerns such as the relay-chain being forkful and session changes. +#![deny(unused_crate_dependencies)] + use std::collections::{HashMap, HashSet}; -use fragment_chain::{FragmentChain, PotentialAddition}; +use fragment_chain::CandidateStorage; use futures::{channel::oneshot, prelude::*}; use polkadot_node_subsystem::{ @@ -41,10 +43,10 @@ use polkadot_node_subsystem::{ overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::{ + backing_implicit_view::{BlockInfoProspectiveParachains as BlockInfo, View as ImplicitView}, inclusion_emulator::{Constraints, RelayChainBlockInfo}, request_session_index_for_child, - runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, - vstaging::fetch_claim_queue, + runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ async_backing::CandidatePendingAvailability, BlockNumber, CandidateHash, @@ -55,8 +57,7 @@ use polkadot_primitives::{ use crate::{ error::{FatalError, FatalResult, JfyiError, JfyiErrorResult, Result}, fragment_chain::{ - CandidateState, CandidateStorage, CandidateStorageInsertionError, - Scope as FragmentChainScope, + CandidateEntry, Error as FragmentChainError, FragmentChain, Scope as FragmentChainScope, }, }; @@ -71,20 +72,33 @@ use self::metrics::Metrics; const LOG_TARGET: &str = "parachain::prospective-parachains"; struct RelayBlockViewData { - // Scheduling info for paras and upcoming paras. + // The fragment chains for current and upcoming scheduled paras. fragment_chains: HashMap, - pending_availability: HashSet, } struct View { - // Active or recent relay-chain blocks by block hash. - active_leaves: HashMap, - candidate_storage: HashMap, + // Per relay parent fragment chains. These includes all relay parents under the implicit view. + per_relay_parent: HashMap, + // The hashes of the currently active leaves. This is a subset of the keys in + // `per_relay_parent`. + active_leaves: HashSet, + // The backing implicit view. + implicit_view: ImplicitView, } impl View { + // Initialize with empty values. fn new() -> Self { - View { active_leaves: HashMap::new(), candidate_storage: HashMap::new() } + View { + per_relay_parent: HashMap::new(), + active_leaves: HashSet::new(), + implicit_view: ImplicitView::default(), + } + } + + // Get the fragment chains of this leaf. + fn get_fragment_chains(&self, leaf: &Hash) -> Option<&HashMap> { + self.per_relay_parent.get(&leaf).map(|view_data| &view_data.fragment_chains) } } @@ -142,9 +156,9 @@ async fn run_iteration( FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {}, FromOrchestra::Communication { msg } => match msg { ProspectiveParachainsMessage::IntroduceSecondedCandidate(request, tx) => - handle_introduce_seconded_candidate(&mut *ctx, view, request, tx, metrics).await, + handle_introduce_seconded_candidate(view, request, tx, metrics).await, ProspectiveParachainsMessage::CandidateBacked(para, candidate_hash) => - handle_candidate_backed(&mut *ctx, view, para, candidate_hash).await, + handle_candidate_backed(view, para, candidate_hash, metrics).await, ProspectiveParachainsMessage::GetBackableCandidates( relay_parent, para, @@ -170,17 +184,32 @@ async fn handle_active_leaves_update( update: ActiveLeavesUpdate, metrics: &Metrics, ) -> JfyiErrorResult<()> { - // 1. clean up inactive leaves - // 2. determine all scheduled paras at the new block - // 3. construct new fragment chain for each para for each new leaf - // 4. prune candidate storage. + // For any new active leaf: + // - determine the scheduled paras + // - pre-populate the candidate storage with pending availability candidates and candidates from + // the parent leaf + // - populate the fragment chain + // - add it to the implicit view + // + // Then mark the newly-deactivated leaves as deactivated and update the implicit view. + // Finally, remove any relay parents that are no longer part of the implicit view. + + let _timer = metrics.time_handle_active_leaves_update(); - for deactivated in &update.deactivated { - view.active_leaves.remove(deactivated); - } + gum::trace!( + target: LOG_TARGET, + activated = ?update.activated, + deactivated = ?update.deactivated, + "Handle ActiveLeavesUpdate" + ); let mut temp_header_cache = HashMap::new(); + // There can only be one newly activated leaf, `update.activated` is an `Option`. for activated in update.activated.into_iter() { + if update.deactivated.contains(&activated.hash) { + continue + } + let hash = activated.hash; let mode = prospective_parachains_mode(ctx.sender(), hash) @@ -199,38 +228,34 @@ async fn handle_active_leaves_update( return Ok(()) }; - let scheduled_paras = fetch_upcoming_paras(&mut *ctx, hash).await?; + let scheduled_paras = fetch_upcoming_paras(ctx, hash).await?; - let block_info: RelayChainBlockInfo = - match fetch_block_info(&mut *ctx, &mut temp_header_cache, hash).await? { - None => { - gum::warn!( - target: LOG_TARGET, - block_hash = ?hash, - "Failed to get block info for newly activated leaf block." - ); + let block_info = match fetch_block_info(ctx, &mut temp_header_cache, hash).await? { + None => { + gum::warn!( + target: LOG_TARGET, + block_hash = ?hash, + "Failed to get block info for newly activated leaf block." + ); - // `update.activated` is an option, but we can use this - // to exit the 'loop' and skip this block without skipping - // pruning logic. - continue - }, - Some(info) => info, - }; + // `update.activated` is an option, but we can use this + // to exit the 'loop' and skip this block without skipping + // pruning logic. + continue + }, + Some(info) => info, + }; let ancestry = - fetch_ancestry(&mut *ctx, &mut temp_header_cache, hash, allowed_ancestry_len).await?; + fetch_ancestry(ctx, &mut temp_header_cache, hash, allowed_ancestry_len).await?; - let mut all_pending_availability = HashSet::new(); + let prev_fragment_chains = + ancestry.first().and_then(|prev_leaf| view.get_fragment_chains(&prev_leaf.hash)); - // Find constraints. let mut fragment_chains = HashMap::new(); for para in scheduled_paras { - let candidate_storage = - view.candidate_storage.entry(para).or_insert_with(CandidateStorage::default); - - let backing_state = fetch_backing_state(&mut *ctx, hash, para).await?; - + // Find constraints and pending availability candidates. + let backing_state = fetch_backing_state(ctx, hash, para).await?; let Some((constraints, pending_availability)) = backing_state else { // This indicates a runtime conflict of some kind. gum::debug!( @@ -243,8 +268,6 @@ async fn handle_active_leaves_update( continue }; - all_pending_availability.extend(pending_availability.iter().map(|c| c.candidate_hash)); - let pending_availability = preprocess_candidates_pending_availability( ctx, &mut temp_header_cache, @@ -254,16 +277,18 @@ async fn handle_active_leaves_update( .await?; let mut compact_pending = Vec::with_capacity(pending_availability.len()); + let mut pending_availability_storage = CandidateStorage::default(); + for c in pending_availability { - let res = candidate_storage.add_candidate( + let candidate_hash = c.compact.candidate_hash; + let res = pending_availability_storage.add_pending_availability_candidate( + candidate_hash, c.candidate, c.persisted_validation_data, - CandidateState::Backed, ); - let candidate_hash = c.compact.candidate_hash; match res { - Ok(_) | Err(CandidateStorageInsertionError::CandidateAlreadyKnown(_)) => {}, + Ok(_) | Err(FragmentChainError::CandidateAlreadyKnown) => {}, Err(err) => { gum::warn!( target: LOG_TARGET, @@ -280,119 +305,138 @@ async fn handle_active_leaves_update( compact_pending.push(c.compact); } - let scope = FragmentChainScope::with_ancestors( - para, - block_info.clone(), + let scope = match FragmentChainScope::with_ancestors( + block_info.clone().into(), constraints, compact_pending, max_candidate_depth, - ancestry.iter().cloned(), - ) - .expect("ancestors are provided in reverse order and correctly; qed"); + ancestry + .iter() + .map(|a| RelayChainBlockInfo::from(a.clone())) + .collect::>(), + ) { + Ok(scope) => scope, + Err(unexpected_ancestors) => { + gum::warn!( + target: LOG_TARGET, + para_id = ?para, + max_candidate_depth, + ?ancestry, + leaf = ?hash, + "Relay chain ancestors have wrong order: {:?}", + unexpected_ancestors + ); + continue + }, + }; gum::trace!( target: LOG_TARGET, relay_parent = ?hash, min_relay_parent = scope.earliest_relay_parent().number, para_id = ?para, + ancestors = ?ancestry, "Creating fragment chain" ); - let chain = FragmentChain::populate(scope, &*candidate_storage); + let number_of_pending_candidates = pending_availability_storage.len(); + + // Init the fragment chain with the pending availability candidates. + let mut chain = FragmentChain::init(scope, pending_availability_storage); + + if chain.best_chain_len() < number_of_pending_candidates { + gum::warn!( + target: LOG_TARGET, + relay_parent = ?hash, + para_id = ?para, + "Not all pending availability candidates could be introduced. Actual vs expected count: {}, {}", + chain.best_chain_len(), + number_of_pending_candidates + ) + } + + // If we know the previous fragment chain, use that for further populating the fragment + // chain. + if let Some(prev_fragment_chain) = + prev_fragment_chains.and_then(|chains| chains.get(¶)) + { + chain.populate_from_previous(prev_fragment_chain); + } gum::trace!( target: LOG_TARGET, relay_parent = ?hash, para_id = ?para, - "Populated fragment chain with {} candidates", - chain.len() + "Populated fragment chain with {} candidates: {:?}", + chain.best_chain_len(), + chain.best_chain_vec() + ); + + gum::trace!( + target: LOG_TARGET, + relay_parent = ?hash, + para_id = ?para, + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() ); fragment_chains.insert(para, chain); } - view.active_leaves.insert( - hash, - RelayBlockViewData { fragment_chains, pending_availability: all_pending_availability }, - ); - } + view.per_relay_parent.insert(hash, RelayBlockViewData { fragment_chains }); - if !update.deactivated.is_empty() { - // This has potential to be a hotspot. - prune_view_candidate_storage(view, metrics); - } + view.active_leaves.insert(hash); - Ok(()) -} + view.implicit_view + .activate_leaf_from_prospective_parachains(block_info, &ancestry); + } -fn prune_view_candidate_storage(view: &mut View, metrics: &Metrics) { - let _timer = metrics.time_prune_view_candidate_storage(); + for deactivated in update.deactivated { + view.active_leaves.remove(&deactivated); + view.implicit_view.deactivate_leaf(deactivated); + } - let active_leaves = &view.active_leaves; - let mut live_candidates = HashSet::new(); - let mut live_paras = HashSet::new(); - for sub_view in active_leaves.values() { - live_candidates.extend(sub_view.pending_availability.iter().cloned()); + { + let remaining: HashSet<_> = view.implicit_view.all_allowed_relay_parents().collect(); - for (para_id, fragment_chain) in &sub_view.fragment_chains { - live_candidates.extend(fragment_chain.to_vec()); - live_paras.insert(*para_id); - } + view.per_relay_parent.retain(|r, _| remaining.contains(&r)); } - let connected_candidates_count = live_candidates.len(); - for (leaf, sub_view) in active_leaves.iter() { - for (para_id, fragment_chain) in &sub_view.fragment_chains { - if let Some(storage) = view.candidate_storage.get(para_id) { - let unconnected_potential = - fragment_chain.find_unconnected_potential_candidates(storage, None); - if !unconnected_potential.is_empty() { - gum::trace!( - target: LOG_TARGET, - ?leaf, - "Keeping {} unconnected candidates for paraid {} in storage: {:?}", - unconnected_potential.len(), - para_id, - unconnected_potential - ); + if metrics.0.is_some() { + let mut active_connected = 0; + let mut active_unconnected = 0; + let mut candidates_in_implicit_view = 0; + + for (hash, RelayBlockViewData { fragment_chains, .. }) in view.per_relay_parent.iter() { + if view.active_leaves.contains(hash) { + for chain in fragment_chains.values() { + active_connected += chain.best_chain_len(); + active_unconnected += chain.unconnected_len(); + } + } else { + for chain in fragment_chains.values() { + candidates_in_implicit_view += chain.best_chain_len(); + candidates_in_implicit_view += chain.unconnected_len(); } - live_candidates.extend(unconnected_potential); } } - } - - view.candidate_storage.retain(|para_id, storage| { - if !live_paras.contains(¶_id) { - return false - } - - storage.retain(|h| live_candidates.contains(&h)); - - // Even if `storage` is now empty, we retain. - // This maintains a convenient invariant that para-id storage exists - // as long as there's an active head which schedules the para. - true - }); - for (para_id, storage) in view.candidate_storage.iter() { - gum::trace!( - target: LOG_TARGET, - "Keeping a total of {} connected candidates for paraid {} in storage", - storage.candidates().count(), - para_id, - ); + metrics.record_candidate_count(active_connected as u64, active_unconnected as u64); + metrics.record_candidate_count_in_implicit_view(candidates_in_implicit_view as u64); } - metrics.record_candidate_storage_size( - connected_candidates_count as u64, - live_candidates.len().saturating_sub(connected_candidates_count) as u64, - ); + let num_active_leaves = view.active_leaves.len() as u64; + let num_inactive_leaves = + (view.per_relay_parent.len() as u64).saturating_sub(num_active_leaves); + metrics.record_leaves_count(num_active_leaves, num_inactive_leaves); + + Ok(()) } struct ImportablePendingAvailability { candidate: CommittedCandidateReceipt, persisted_validation_data: PersistedValidationData, - compact: crate::fragment_chain::PendingAvailability, + compact: fragment_chain::PendingAvailability, } #[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] @@ -435,9 +479,9 @@ async fn preprocess_candidates_pending_availability( relay_parent_number: relay_parent.number, relay_parent_storage_root: relay_parent.storage_root, }, - compact: crate::fragment_chain::PendingAvailability { + compact: fragment_chain::PendingAvailability { candidate_hash: pending.candidate_hash, - relay_parent, + relay_parent: relay_parent.into(), }, }); @@ -447,9 +491,7 @@ async fn preprocess_candidates_pending_availability( Ok(importable) } -#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] -async fn handle_introduce_seconded_candidate( - _ctx: &mut Context, +async fn handle_introduce_seconded_candidate( view: &mut View, request: IntroduceSecondedCandidateRequest, tx: oneshot::Sender, @@ -463,167 +505,163 @@ async fn handle_introduce_seconded_candidate( persisted_validation_data: pvd, } = request; - let Some(storage) = view.candidate_storage.get_mut(¶) else { - gum::warn!( - target: LOG_TARGET, - para_id = ?para, - candidate_hash = ?candidate.hash(), - "Received seconded candidate for inactive para", - ); + let candidate_hash = candidate.hash(); + let candidate_entry = match CandidateEntry::new_seconded(candidate_hash, candidate, pvd) { + Ok(candidate) => candidate, + Err(err) => { + gum::warn!( + target: LOG_TARGET, + para = ?para, + "Cannot add seconded candidate: {}", + err + ); - let _ = tx.send(false); - return + let _ = tx.send(false); + return + }, }; - let parent_head_hash = pvd.parent_head.hash(); - let output_head_hash = Some(candidate.commitments.head_data.hash()); + let mut added = false; + let mut para_scheduled = false; + // We don't iterate only through the active leaves. We also update the deactivated parents in + // the implicit view, so that their upcoming children may see these candidates. + for (relay_parent, rp_data) in view.per_relay_parent.iter_mut() { + let Some(chain) = rp_data.fragment_chains.get_mut(¶) else { continue }; + let is_active_leaf = view.active_leaves.contains(relay_parent); - // We first introduce the candidate in the storage and then try to extend the chain. - // If the candidate gets included in the chain, we can keep it in storage. - // If it doesn't, check that it's still a potential candidate in at least one fragment chain. - // If it's not, we can remove it. + para_scheduled = true; - let candidate_hash = - match storage.add_candidate(candidate.clone(), pvd, CandidateState::Seconded) { - Ok(c) => c, - Err(CandidateStorageInsertionError::CandidateAlreadyKnown(_)) => { + match chain.try_adding_seconded_candidate(&candidate_entry) { + Ok(()) => { gum::debug!( target: LOG_TARGET, - para = ?para, - "Attempting to introduce an already known candidate: {:?}", - candidate.hash() + ?para, + ?relay_parent, + ?is_active_leaf, + "Added seconded candidate {:?}", + candidate_hash ); - // Candidate already known. - let _ = tx.send(true); - return + added = true; }, - Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) => { - // We can't log the candidate hash without either doing more ~expensive - // hashing but this branch indicates something is seriously wrong elsewhere - // so it's doubtful that it would affect debugging. - - gum::warn!( + Err(FragmentChainError::CandidateAlreadyKnown) => { + gum::debug!( target: LOG_TARGET, - para = ?para, - "Received seconded candidate had mismatching validation data", + ?para, + ?relay_parent, + ?is_active_leaf, + "Attempting to introduce an already known candidate: {:?}", + candidate_hash ); - - let _ = tx.send(false); - return + added = true; }, - }; - - let mut keep_in_storage = false; - for (relay_parent, leaf_data) in view.active_leaves.iter_mut() { - if let Some(chain) = leaf_data.fragment_chains.get_mut(¶) { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - "Candidates in chain before trying to introduce a new one: {:?}", - chain.to_vec() - ); - chain.extend_from_storage(&*storage); - if chain.contains_candidate(&candidate_hash) { - keep_in_storage = true; - - gum::trace!( + Err(err) => { + gum::debug!( target: LOG_TARGET, + ?para, ?relay_parent, - para = ?para, ?candidate_hash, - "Added candidate to chain.", - ); - } else { - match chain.can_add_candidate_as_potential( - &storage, - &candidate_hash, - &candidate.descriptor.relay_parent, - parent_head_hash, - output_head_hash, - ) { - PotentialAddition::Anyhow => { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - ?candidate_hash, - "Kept candidate as unconnected potential.", - ); - - keep_in_storage = true; - }, - _ => { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - "Not introducing a new candidate: {:?}", - candidate_hash - ); - }, - } - } + ?is_active_leaf, + "Cannot introduce seconded candidate: {}", + err + ) + }, } } - // If there is at least one leaf where this candidate can be added or potentially added in the - // future, keep it in storage. - if !keep_in_storage { - storage.remove_candidate(&candidate_hash); + if !para_scheduled { + gum::warn!( + target: LOG_TARGET, + para_id = ?para, + ?candidate_hash, + "Received seconded candidate for inactive para", + ); + } + if !added { gum::debug!( target: LOG_TARGET, para = ?para, candidate = ?candidate_hash, - "Newly-seconded candidate cannot be kept under any active leaf", + "Newly-seconded candidate cannot be kept under any relay parent", ); } - let _ = tx.send(keep_in_storage); + let _ = tx.send(added); } -#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] -async fn handle_candidate_backed( - _ctx: &mut Context, +async fn handle_candidate_backed( view: &mut View, para: ParaId, candidate_hash: CandidateHash, + metrics: &Metrics, ) { - let Some(storage) = view.candidate_storage.get_mut(¶) else { - gum::warn!( - target: LOG_TARGET, - para_id = ?para, - ?candidate_hash, - "Received instruction to back a candidate for unscheduled para", - ); + let _timer = metrics.time_candidate_backed(); - return - }; + let mut found_candidate = false; + let mut found_para = false; + + // We don't iterate only through the active leaves. We also update the deactivated parents in + // the implicit view, so that their upcoming children may see these candidates. + for (relay_parent, rp_data) in view.per_relay_parent.iter_mut() { + let Some(chain) = rp_data.fragment_chains.get_mut(¶) else { continue }; + let is_active_leaf = view.active_leaves.contains(relay_parent); + + found_para = true; + if chain.is_candidate_backed(&candidate_hash) { + gum::debug!( + target: LOG_TARGET, + ?para, + ?candidate_hash, + ?is_active_leaf, + "Received redundant instruction to mark as backed an already backed candidate", + ); + found_candidate = true; + } else if chain.contains_unconnected_candidate(&candidate_hash) { + found_candidate = true; + // Mark the candidate as backed. This can recreate the fragment chain. + chain.candidate_backed(&candidate_hash); + + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?para, + ?is_active_leaf, + "Candidate backed. Candidate chain for para: {:?}", + chain.best_chain_vec() + ); + + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?para, + ?is_active_leaf, + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() + ); + } + } - if !storage.contains(&candidate_hash) { + if !found_para { gum::warn!( target: LOG_TARGET, - para_id = ?para, + ?para, ?candidate_hash, - "Received instruction to back unknown candidate", + "Received instruction to back a candidate for unscheduled para", ); return } - if storage.is_backed(&candidate_hash) { + if !found_candidate { + // This can be harmless. It can happen if we received a better backed candidate before and + // dropped this other candidate already. gum::debug!( target: LOG_TARGET, - para_id = ?para, + ?para, ?candidate_hash, - "Received redundant instruction to mark candidate as backed", + "Received instruction to back unknown candidate", ); - - return } - - storage.mark_backed(&candidate_hash); } fn answer_get_backable_candidates( @@ -634,7 +672,7 @@ fn answer_get_backable_candidates( ancestors: Ancestors, tx: oneshot::Sender>, ) { - let Some(data) = view.active_leaves.get(&relay_parent) else { + if !view.active_leaves.contains(&relay_parent) { gum::debug!( target: LOG_TARGET, ?relay_parent, @@ -644,26 +682,25 @@ fn answer_get_backable_candidates( let _ = tx.send(vec![]); return - }; - - let Some(chain) = data.fragment_chains.get(¶) else { + } + let Some(data) = view.per_relay_parent.get(&relay_parent) else { gum::debug!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Requested backable candidate for inactive para." + "Requested backable candidate for inexistent relay-parent." ); let _ = tx.send(vec![]); return }; - let Some(storage) = view.candidate_storage.get(¶) else { - gum::warn!( + let Some(chain) = data.fragment_chains.get(¶) else { + gum::debug!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "No candidate storage for active para", + "Requested backable candidate for inactive para." ); let _ = tx.send(vec![]); @@ -674,38 +711,19 @@ fn answer_get_backable_candidates( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Candidate storage for para: {:?}", - storage.candidates().map(|candidate| candidate.hash()).collect::>() + "Candidate chain for para: {:?}", + chain.best_chain_vec() ); gum::trace!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Candidate chain for para: {:?}", - chain.to_vec() + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() ); - let backable_candidates: Vec<_> = chain - .find_backable_chain(ancestors.clone(), count, |candidate| storage.is_backed(candidate)) - .into_iter() - .filter_map(|child_hash| { - storage.relay_parent_of_candidate(&child_hash).map_or_else( - || { - // Here, we'd actually need to trim all of the candidates that follow. Or - // not, the runtime will do this. Impossible scenario anyway. - gum::error!( - target: LOG_TARGET, - ?child_hash, - para_id = ?para, - "Candidate is present in fragment chain but not in candidate's storage!", - ); - None - }, - |parent_hash| Some((child_hash, parent_hash)), - ) - }) - .collect(); + let backable_candidates = chain.find_backable_chain(ancestors.clone(), count); if backable_candidates.is_empty() { gum::trace!( @@ -742,19 +760,32 @@ fn answer_hypothetical_membership_request( } let required_active_leaf = request.fragment_chain_relay_parent; - for (active_leaf, leaf_view) in view + for active_leaf in view .active_leaves .iter() - .filter(|(h, _)| required_active_leaf.as_ref().map_or(true, |x| h == &x)) + .filter(|h| required_active_leaf.as_ref().map_or(true, |x| h == &x)) { + let Some(leaf_view) = view.per_relay_parent.get(&active_leaf) else { continue }; for &mut (ref candidate, ref mut membership) in &mut response { let para_id = &candidate.candidate_para(); let Some(fragment_chain) = leaf_view.fragment_chains.get(para_id) else { continue }; - let Some(candidate_storage) = view.candidate_storage.get(para_id) else { continue }; - if fragment_chain.hypothetical_membership(candidate.clone(), candidate_storage) { - membership.push(*active_leaf); - } + let res = fragment_chain.can_add_candidate_as_potential(candidate); + match res { + Err(FragmentChainError::CandidateAlreadyKnown) | Ok(()) => { + membership.push(*active_leaf); + }, + Err(err) => { + gum::debug!( + target: LOG_TARGET, + para = ?para_id, + leaf = ?active_leaf, + candidate = ?candidate.candidate_hash(), + "Candidate is not a hypothetical member: {}", + err + ) + }, + }; } } @@ -767,9 +798,11 @@ fn answer_minimum_relay_parents_request( tx: oneshot::Sender>, ) { let mut v = Vec::new(); - if let Some(leaf_data) = view.active_leaves.get(&relay_parent) { - for (para_id, fragment_chain) in &leaf_data.fragment_chains { - v.push((*para_id, fragment_chain.scope().earliest_relay_parent().number)); + if view.active_leaves.contains(&relay_parent) { + if let Some(leaf_data) = view.per_relay_parent.get(&relay_parent) { + for (para_id, fragment_chain) in &leaf_data.fragment_chains { + v.push((*para_id, fragment_chain.scope().earliest_relay_parent().number)); + } } } @@ -781,37 +814,21 @@ fn answer_prospective_validation_data_request( request: ProspectiveValidationDataRequest, tx: oneshot::Sender>, ) { - // 1. Try to get the head-data from the candidate store if known. - // 2. Otherwise, it might exist as the base in some relay-parent and we can find it by iterating - // fragment chains. - // 3. Otherwise, it is unknown. - // 4. Also try to find the relay parent block info by scanning fragment chains. - // 5. If head data and relay parent block info are found - success. Otherwise, failure. - - let storage = match view.candidate_storage.get(&request.para_id) { - None => { - let _ = tx.send(None); - return - }, - Some(s) => s, - }; + // Try getting the needed data from any fragment chain. let (mut head_data, parent_head_data_hash) = match request.parent_head_data { - ParentHeadData::OnlyHash(parent_head_data_hash) => ( - storage.head_data_by_hash(&parent_head_data_hash).map(|x| x.clone()), - parent_head_data_hash, - ), + ParentHeadData::OnlyHash(parent_head_data_hash) => (None, parent_head_data_hash), ParentHeadData::WithData { head_data, hash } => (Some(head_data), hash), }; let mut relay_parent_info = None; let mut max_pov_size = None; - for fragment_chain in view - .active_leaves - .values() - .filter_map(|x| x.fragment_chains.get(&request.para_id)) - { + for fragment_chain in view.active_leaves.iter().filter_map(|x| { + view.per_relay_parent + .get(&x) + .and_then(|data| data.fragment_chains.get(&request.para_id)) + }) { if head_data.is_some() && relay_parent_info.is_some() && max_pov_size.is_some() { break } @@ -819,10 +836,7 @@ fn answer_prospective_validation_data_request( relay_parent_info = fragment_chain.scope().ancestor(&request.candidate_relay_parent); } if head_data.is_none() { - let required_parent = &fragment_chain.scope().base_constraints().required_parent; - if required_parent.hash() == parent_head_data_hash { - head_data = Some(required_parent.clone()); - } + head_data = fragment_chain.get_head_data_by_hash(&parent_head_data_hash); } if max_pov_size.is_none() { let contains_ancestor = @@ -925,7 +939,7 @@ async fn fetch_ancestry( cache: &mut HashMap, relay_hash: Hash, ancestors: usize, -) -> JfyiErrorResult> { +) -> JfyiErrorResult> { if ancestors == 0 { return Ok(Vec::new()) } @@ -1004,12 +1018,13 @@ async fn fetch_block_info( ctx: &mut Context, cache: &mut HashMap, relay_hash: Hash, -) -> JfyiErrorResult> { +) -> JfyiErrorResult> { let header = fetch_block_header_with_cache(ctx, cache, relay_hash).await?; - Ok(header.map(|header| RelayChainBlockInfo { + Ok(header.map(|header| BlockInfo { hash: relay_hash, number: header.number, + parent_hash: header.parent_hash, storage_root: header.state_root, })) } diff --git a/polkadot/node/core/prospective-parachains/src/metrics.rs b/polkadot/node/core/prospective-parachains/src/metrics.rs index 5abd9f56f306cdad515b93c794a51de516417b88..78561bc878ac8f76b54ac772a65469f5c06a4c0a 100644 --- a/polkadot/node/core/prospective-parachains/src/metrics.rs +++ b/polkadot/node/core/prospective-parachains/src/metrics.rs @@ -17,15 +17,18 @@ use polkadot_node_subsystem::prometheus::Opts; use polkadot_node_subsystem_util::metrics::{ self, - prometheus::{self, GaugeVec, U64}, + prometheus::{self, Gauge, GaugeVec, U64}, }; #[derive(Clone)] pub(crate) struct MetricsInner { - prune_view_candidate_storage: prometheus::Histogram, - introduce_seconded_candidate: prometheus::Histogram, - hypothetical_membership: prometheus::Histogram, - candidate_storage_count: prometheus::GaugeVec, + time_active_leaves_update: prometheus::Histogram, + time_introduce_seconded_candidate: prometheus::Histogram, + time_candidate_backed: prometheus::Histogram, + time_hypothetical_membership: prometheus::Histogram, + candidate_count: prometheus::GaugeVec, + active_leaves_count: prometheus::GaugeVec, + implicit_view_candidate_count: prometheus::Gauge, } /// Candidate backing metrics. @@ -33,13 +36,11 @@ pub(crate) struct MetricsInner { pub struct Metrics(pub(crate) Option); impl Metrics { - /// Provide a timer for handling `prune_view_candidate_storage` which observes on drop. - pub fn time_prune_view_candidate_storage( + /// Provide a timer for handling `ActiveLeavesUpdate` which observes on drop. + pub fn time_handle_active_leaves_update( &self, ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.prune_view_candidate_storage.start_timer()) + self.0.as_ref().map(|metrics| metrics.time_active_leaves_update.start_timer()) } /// Provide a timer for handling `IntroduceSecondedCandidate` which observes on drop. @@ -48,31 +49,47 @@ impl Metrics { ) -> Option { self.0 .as_ref() - .map(|metrics| metrics.introduce_seconded_candidate.start_timer()) + .map(|metrics| metrics.time_introduce_seconded_candidate.start_timer()) + } + + /// Provide a timer for handling `CandidateBacked` which observes on drop. + pub fn time_candidate_backed(&self) -> Option { + self.0.as_ref().map(|metrics| metrics.time_candidate_backed.start_timer()) } /// Provide a timer for handling `GetHypotheticalMembership` which observes on drop. pub fn time_hypothetical_membership_request( &self, ) -> Option { - self.0.as_ref().map(|metrics| metrics.hypothetical_membership.start_timer()) + self.0 + .as_ref() + .map(|metrics| metrics.time_hypothetical_membership.start_timer()) } - /// Record the size of the candidate storage. First param is the connected candidates count, - /// second param is the unconnected candidates count. - pub fn record_candidate_storage_size(&self, connected_count: u64, unconnected_count: u64) { + /// Record number of candidates across all fragment chains. First param is the connected + /// candidates count, second param is the unconnected candidates count. + pub fn record_candidate_count(&self, connected_count: u64, unconnected_count: u64) { self.0.as_ref().map(|metrics| { + metrics.candidate_count.with_label_values(&["connected"]).set(connected_count); metrics - .candidate_storage_count - .with_label_values(&["connected"]) - .set(connected_count) + .candidate_count + .with_label_values(&["unconnected"]) + .set(unconnected_count); }); + } + /// Record the number of candidates present in the implicit view of the subsystem. + pub fn record_candidate_count_in_implicit_view(&self, count: u64) { self.0.as_ref().map(|metrics| { - metrics - .candidate_storage_count - .with_label_values(&["unconnected"]) - .set(unconnected_count) + metrics.implicit_view_candidate_count.set(count); + }); + } + + /// Record the number of active/inactive leaves kept by the subsystem. + pub fn record_leaves_count(&self, active_count: u64, inactive_count: u64) { + self.0.as_ref().map(|metrics| { + metrics.active_leaves_count.with_label_values(&["active"]).set(active_count); + metrics.active_leaves_count.with_label_values(&["inactive"]).set(inactive_count); }); } } @@ -80,37 +97,61 @@ impl Metrics { impl metrics::Metrics for Metrics { fn try_register(registry: &prometheus::Registry) -> Result { let metrics = MetricsInner { - prune_view_candidate_storage: prometheus::register( + time_active_leaves_update: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_prune_view_candidate_storage", - "Time spent within `prospective_parachains::prune_view_candidate_storage`", + "polkadot_parachain_prospective_parachains_time_active_leaves_update", + "Time spent within `prospective_parachains::handle_active_leaves_update`", ))?, registry, )?, - introduce_seconded_candidate: prometheus::register( + time_introduce_seconded_candidate: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_introduce_seconded_candidate", + "polkadot_parachain_prospective_parachains_time_introduce_seconded_candidate", "Time spent within `prospective_parachains::handle_introduce_seconded_candidate`", ))?, registry, )?, - hypothetical_membership: prometheus::register( + time_candidate_backed: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_hypothetical_membership", + "polkadot_parachain_prospective_parachains_time_candidate_backed", + "Time spent within `prospective_parachains::handle_candidate_backed`", + ))?, + registry, + )?, + time_hypothetical_membership: prometheus::register( + prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( + "polkadot_parachain_prospective_parachains_time_hypothetical_membership", "Time spent responding to `GetHypotheticalMembership`", ))?, registry, )?, - candidate_storage_count: prometheus::register( + candidate_count: prometheus::register( GaugeVec::new( Opts::new( - "polkadot_parachain_prospective_parachains_candidate_storage_count", - "Number of candidates present in the candidate storage, split by connected and unconnected" + "polkadot_parachain_prospective_parachains_candidate_count", + "Number of candidates present across all fragment chains, split by connected and unconnected" ), &["type"], )?, registry, )?, + active_leaves_count: prometheus::register( + GaugeVec::new( + Opts::new( + "polkadot_parachain_prospective_parachains_active_leaves_count", + "Number of leaves kept by the subsystem, split by active/inactive" + ), + &["type"], + )?, + registry, + )?, + implicit_view_candidate_count: prometheus::register( + Gauge::new( + "polkadot_parachain_prospective_parachains_implicit_view_candidate_count", + "Number of candidates present in the implicit view" + )?, + registry + )?, }; Ok(Metrics(Some(metrics))) } diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 221fbf4c4e60365855b24bcfff7d89cfbcc6d205..14a093239e8ecbaa1fd6c4e3c294fddb21795af7 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -111,6 +111,8 @@ fn get_parent_hash(hash: Hash) -> Hash { fn test_harness>( test: impl FnOnce(VirtualOverseer) -> T, ) -> View { + sp_tracing::init_for_tests(); + let pool = sp_core::testing::TaskExecutor::new(); let (mut context, virtual_overseer) = @@ -203,6 +205,32 @@ async fn activate_leaf( activate_leaf_with_params(virtual_overseer, leaf, test_state, ASYNC_BACKING_PARAMETERS).await; } +async fn activate_leaf_with_parent_hash_fn( + virtual_overseer: &mut VirtualOverseer, + leaf: &TestLeaf, + test_state: &TestState, + parent_hash_fn: impl Fn(Hash) -> Hash, +) { + let TestLeaf { number, hash, .. } = leaf; + + let activated = new_leaf(*hash, *number); + + virtual_overseer + .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work( + activated, + )))) + .await; + + handle_leaf_activation( + virtual_overseer, + leaf, + test_state, + ASYNC_BACKING_PARAMETERS, + parent_hash_fn, + ) + .await; +} + async fn activate_leaf_with_params( virtual_overseer: &mut VirtualOverseer, leaf: &TestLeaf, @@ -219,7 +247,14 @@ async fn activate_leaf_with_params( )))) .await; - handle_leaf_activation(virtual_overseer, leaf, test_state, async_backing_params).await; + handle_leaf_activation( + virtual_overseer, + leaf, + test_state, + async_backing_params, + get_parent_hash, + ) + .await; } async fn handle_leaf_activation( @@ -227,6 +262,7 @@ async fn handle_leaf_activation( leaf: &TestLeaf, test_state: &TestState, async_backing_params: AsyncBackingParams, + parent_hash_fn: impl Fn(Hash) -> Hash, ) { let TestLeaf { number, hash, para_data } = leaf; @@ -281,7 +317,7 @@ async fn handle_leaf_activation( let min_min = para_data.iter().map(|(_, data)| data.min_relay_parent).min().unwrap_or(*number); let ancestry_len = number - min_min; let ancestry_hashes: Vec = - std::iter::successors(Some(*hash), |h| Some(get_parent_hash(*h))) + std::iter::successors(Some(*hash), |h| Some(parent_hash_fn(*h))) .skip(1) .take(ancestry_len as usize) .collect(); @@ -307,16 +343,20 @@ async fn handle_leaf_activation( ); } + let mut used_relay_parents = HashSet::new(); for (hash, number) in ancestry_iter { - send_block_header(virtual_overseer, hash, number).await; - assert_matches!( - virtual_overseer.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx)) - ) if parent == hash => { - tx.send(Ok(1)).unwrap(); - } - ); + if !used_relay_parents.contains(&hash) { + send_block_header(virtual_overseer, hash, number).await; + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx)) + ) if parent == hash => { + tx.send(Ok(1)).unwrap(); + } + ); + used_relay_parents.insert(hash); + } } let paras: HashSet<_> = test_state.claim_queue.values().flatten().collect(); @@ -353,12 +393,16 @@ async fn handle_leaf_activation( ); for pending in pending_availability { - send_block_header( - virtual_overseer, - pending.descriptor.relay_parent, - pending.relay_parent_number, - ) - .await; + if !used_relay_parents.contains(&pending.descriptor.relay_parent) { + send_block_header( + virtual_overseer, + pending.descriptor.relay_parent, + pending.relay_parent_number, + ) + .await; + + used_relay_parents.insert(pending.descriptor.relay_parent); + } } } @@ -513,6 +557,26 @@ async fn get_pvd( assert_eq!(resp, expected_pvd); } +macro_rules! make_and_back_candidate { + ($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{ + let (mut candidate, pvd) = make_candidate( + $leaf.hash, + $leaf.number, + 1.into(), + $parent.commitments.head_data.clone(), + HeadData(vec![$index]), + $test_state.validation_code_hash, + ); + // Set a field to make this candidate unique. + candidate.descriptor.para_head = Hash::from_low_u64_le($index); + let candidate_hash = candidate.hash(); + introduce_seconded_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; + back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; + + (candidate, candidate_hash) + }}; +} + #[test] fn should_do_no_work_if_async_backing_disabled_for_leaf() { async fn activate_leaf_async_backing_disabled(virtual_overseer: &mut VirtualOverseer) { @@ -542,7 +606,6 @@ fn should_do_no_work_if_async_backing_disabled_for_leaf() { }); assert!(view.active_leaves.is_empty()); - assert!(view.candidate_storage.is_empty()); } // Send some candidates and make sure all are found: @@ -718,10 +781,6 @@ fn introduce_candidates_basic() { }); assert_eq!(view.active_leaves.len(), 3); - assert_eq!(view.candidate_storage.len(), 2); - // Two parents and two candidates per para. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (2, 2)); } #[test] @@ -766,28 +825,36 @@ fn introduce_candidate_multiple_times() { 1.into(), Ancestors::default(), 5, - response_a, + response_a.clone(), ) .await; - // Introduce the same candidate multiple times. It'll return true but it won't be added. - // We'll check below that the candidate count remains 1. + // Introduce the same candidate multiple times. It'll return true but it will only be added + // once. for _ in 0..5 { introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) .await; } + // Check candidate tree membership. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::default(), + 5, + response_a, + ) + .await; + virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } #[test] -fn fragment_chain_length_is_bounded() { +fn fragment_chain_best_chain_length_is_bounded() { let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -835,12 +902,11 @@ fn fragment_chain_length_is_bounded() { ); // Introduce candidates A and B. Since max depth is 1, only these two will be allowed. - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) - .await; - introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()) - .await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; - // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates. + // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates and + // they won't be part of the best chain. back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_b.hash()).await; @@ -855,103 +921,25 @@ fn fragment_chain_length_is_bounded() { ) .await; - // Introducing C will fail. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c.clone()) - .await; - - virtual_overseer - }); - - assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); -} - -#[test] -fn unconnected_candidate_count_is_bounded() { - let test_state = TestState::default(); - let view = test_harness(|mut virtual_overseer| async move { - // Leaf A - let leaf_a = TestLeaf { - number: 100, - hash: Hash::from_low_u64_be(130), - para_data: vec![ - (1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))), - (2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))), - ], - }; - // Activate leaves. - activate_leaf_with_params( - &mut virtual_overseer, - &leaf_a, - &test_state, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 3 }, - ) - .await; - - // Candidates A, B and C are all potential candidates but don't form a chain. - let (candidate_a, pvd_a) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![1]), - HeadData(vec![2]), - test_state.validation_code_hash, - ); - let (candidate_b, pvd_b) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![3]), - HeadData(vec![4]), - test_state.validation_code_hash, - ); - let (candidate_c, pvd_c) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![4]), - HeadData(vec![5]), - test_state.validation_code_hash, - ); - - // Introduce candidates A and B. Although max depth is 1 (which should allow for two - // candidates), only 1 is allowed, because the last candidate must be a connected candidate. - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) - .await; - introduce_seconded_candidate_failed( - &mut virtual_overseer, - candidate_b.clone(), - pvd_b.clone(), - ) - .await; - - // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates. - back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; + // Introducing C will not fail. It will be kept as unconnected storage. + introduce_seconded_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c).await; + // When being backed, candidate C will be dropped. + back_candidate(&mut virtual_overseer, &candidate_c, candidate_c.hash()).await; - // Check candidate tree membership. Should be empty. get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), Ancestors::default(), 5, - vec![], + vec![(candidate_a.hash(), leaf_a.hash), (candidate_b.hash(), leaf_a.hash)], ) .await; - // Introducing C will also fail. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c.clone()) - .await; - virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Send some candidates, check if the candidate won't be found once its relay parent leaves the @@ -1178,7 +1166,6 @@ fn introduce_candidate_parent_leaving_view() { }); assert_eq!(view.active_leaves.len(), 0); - assert_eq!(view.candidate_storage.len(), 0); } // Introduce a candidate to multiple forks, see how the membership is returned. @@ -1249,13 +1236,12 @@ fn introduce_candidate_on_multiple_forks() { }); assert_eq!(view.active_leaves.len(), 2); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } #[test] fn unconnected_candidates_become_connected() { + // This doesn't test all the complicated cases with many unconnected candidates, as it's more + // extensively tested in the `fragment_chain::tests` module. let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -1351,9 +1337,6 @@ fn unconnected_candidates_become_connected() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (4, 4)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Backs some candidates and tests `GetBackableCandidates` when requesting a single candidate. @@ -1435,6 +1418,10 @@ fn check_backable_query_single_candidate() { back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; + // Back an unknown candidate. It doesn't return anything but it's ignored. Will not have any + // effect on the backable candidates. + back_candidate(&mut virtual_overseer, &candidate_b, CandidateHash(Hash::random())).await; + // Should not get any backable candidates for the other para. get_backable_candidates( &mut virtual_overseer, @@ -1490,35 +1477,11 @@ fn check_backable_query_single_candidate() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // Two parents and two candidates on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Backs some candidates and tests `GetBackableCandidates` when requesting a multiple candidates. #[test] fn check_backable_query_multiple_candidates() { - macro_rules! make_and_back_candidate { - ($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{ - let (mut candidate, pvd) = make_candidate( - $leaf.hash, - $leaf.number, - 1.into(), - $parent.commitments.head_data.clone(), - HeadData(vec![$index]), - $test_state.validation_code_hash, - ); - // Set a field to make this candidate unique. - candidate.descriptor.para_head = Hash::from_low_u64_le($index); - let candidate_hash = candidate.hash(); - introduce_seconded_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; - back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; - - (candidate, candidate_hash) - }}; - } - let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -1786,10 +1749,6 @@ fn check_backable_query_multiple_candidates() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // 4 candidates on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (4, 4)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Test hypothetical membership query. @@ -1885,11 +1844,13 @@ fn check_hypothetical_membership_query() { introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) .await; - // Get membership of candidates after adding A. C is not a potential candidate because we - // may only add one more candidate, which must be a connected candidate. - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_b.clone(), pvd_b.clone())] - { + // Get membership of candidates after adding A. They all are still unconnected candidates + // (not part of the best backable chain). + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { get_hypothetical_membership( &mut virtual_overseer, candidate.hash(), @@ -1900,14 +1861,24 @@ fn check_hypothetical_membership_query() { .await; } - get_hypothetical_membership( - &mut virtual_overseer, - candidate_c.hash(), - candidate_c.clone(), - pvd_c.clone(), - vec![], - ) - .await; + // Back A. Now A is part of the best chain the rest can be added as unconnected. + + back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; + + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { + get_hypothetical_membership( + &mut virtual_overseer, + candidate.hash(), + candidate, + pvd, + vec![leaf_a.hash, leaf_b.hash], + ) + .await; + } // Candidate D has invalid relay parent. let (candidate_d, pvd_d) = make_candidate( @@ -1920,14 +1891,17 @@ fn check_hypothetical_membership_query() { ); introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_d, pvd_d).await; - // Add candidate B. + // Add candidate B and back it. introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()) .await; + back_candidate(&mut virtual_overseer, &candidate_b, candidate_b.hash()).await; // Get membership of candidates after adding B. - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_b.clone(), pvd_b.clone())] - { + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { get_hypothetical_membership( &mut virtual_overseer, candidate.hash(), @@ -1938,24 +1912,10 @@ fn check_hypothetical_membership_query() { .await; } - get_hypothetical_membership( - &mut virtual_overseer, - candidate_c.hash(), - candidate_c.clone(), - pvd_c.clone(), - vec![], - ) - .await; - - // Add candidate C. It will fail because we have enough candidates for the configured depth. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c).await; - virtual_overseer }); assert_eq!(view.active_leaves.len(), 2); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); } #[test] @@ -2005,6 +1965,16 @@ fn check_pvd_query() { test_state.validation_code_hash, ); + // Candidate E. + let (candidate_e, pvd_e) = make_candidate( + leaf_a.hash, + leaf_a.number, + 1.into(), + HeadData(vec![5]), + HeadData(vec![6]), + test_state.validation_code_hash, + ); + // Get pvd of candidate A before adding it. get_pvd( &mut virtual_overseer, @@ -2067,20 +2037,20 @@ fn check_pvd_query() { introduce_seconded_candidate(&mut virtual_overseer, candidate_c, pvd_c.clone()).await; // Get pvd of candidate C after adding it. - get_pvd( - &mut virtual_overseer, - 1.into(), - leaf_a.hash, - HeadData(vec![2]), - Some(pvd_c.clone()), - ) - .await; + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![2]), Some(pvd_c)).await; + + // Get pvd of candidate E before adding it. It won't be found, as we don't have its parent. + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![5]), None).await; + + // Add candidate E and check again. Should succeed this time. + introduce_seconded_candidate(&mut virtual_overseer, candidate_e, pvd_e.clone()).await; + + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![5]), Some(pvd_e)).await; virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); } // Test simultaneously activating and deactivating leaves, and simultaneously deactivating @@ -2150,6 +2120,7 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { &leaf_c, &test_state, ASYNC_BACKING_PARAMETERS, + get_parent_hash, ) .await; @@ -2171,13 +2142,6 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { virtual_overseer .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update))) .await; - handle_leaf_activation( - &mut virtual_overseer, - &leaf_a, - &test_state, - ASYNC_BACKING_PARAMETERS, - ) - .await; // Remove the leaf again. Send some unnecessary hashes. let update = ActiveLeavesUpdate { @@ -2192,7 +2156,322 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { }); assert_eq!(view.active_leaves.len(), 0); - assert_eq!(view.candidate_storage.len(), 0); +} + +#[test] +fn handle_active_leaves_update_gets_candidates_from_parent() { + let para_id = ParaId::from(1); + let mut test_state = TestState::default(); + test_state.claim_queue = test_state + .claim_queue + .into_iter() + .filter(|(_, paras)| matches!(paras.front(), Some(para) if para == ¶_id)) + .collect(); + assert_eq!(test_state.claim_queue.len(), 1); + let view = test_harness(|mut virtual_overseer| async move { + // Leaf A + let leaf_a = TestLeaf { + number: 100, + hash: Hash::from_low_u64_be(130), + para_data: vec![(para_id, PerParaData::new(97, HeadData(vec![1, 2, 3])))], + }; + // Activate leaf A. + activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await; + + // Candidates A, B, C and D all form a chain + let (candidate_a, pvd_a) = make_candidate( + leaf_a.hash, + leaf_a.number, + para_id, + HeadData(vec![1, 2, 3]), + HeadData(vec![1]), + test_state.validation_code_hash, + ); + let candidate_hash_a = candidate_a.hash(); + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; + + let (candidate_b, candidate_hash_b) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_a, 2); + let (candidate_c, candidate_hash_c) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 3); + let (candidate_d, candidate_hash_d) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_c, 4); + + let mut all_candidates_resp = vec![ + (candidate_hash_a, leaf_a.hash), + (candidate_hash_b, leaf_a.hash), + (candidate_hash_c, leaf_a.hash), + (candidate_hash_d, leaf_a.hash), + ]; + + // Check candidate tree membership. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::default(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Activate leaf B, which makes candidates A and B pending availability. + // Leaf B + let leaf_b = TestLeaf { + number: 101, + hash: Hash::from_low_u64_be(129), + para_data: vec![( + para_id, + PerParaData::new_with_pending( + 98, + HeadData(vec![1, 2, 3]), + vec![ + CandidatePendingAvailability { + candidate_hash: candidate_a.hash(), + descriptor: candidate_a.descriptor.clone(), + commitments: candidate_a.commitments.clone(), + relay_parent_number: leaf_a.number, + max_pov_size: MAX_POV_SIZE, + }, + CandidatePendingAvailability { + candidate_hash: candidate_b.hash(), + descriptor: candidate_b.descriptor.clone(), + commitments: candidate_b.commitments.clone(), + relay_parent_number: leaf_a.number, + max_pov_size: MAX_POV_SIZE, + }, + ], + ), + )], + }; + // Activate leaf B. + activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::default(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Now deactivate leaf A. + deactivate_leaf(&mut virtual_overseer, leaf_a.hash).await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + // Now add leaf C, which will be a sibling (fork) of leaf B. It should also inherit the + // candidates of leaf A (their common parent). + let leaf_c = TestLeaf { + number: 101, + hash: Hash::from_low_u64_be(12), + para_data: vec![( + para_id, + PerParaData::new_with_pending(98, HeadData(vec![1, 2, 3]), vec![]), + )], + }; + + activate_leaf_with_parent_hash_fn(&mut virtual_overseer, &leaf_c, &test_state, |hash| { + if hash == leaf_c.hash { + leaf_a.hash + } else { + get_parent_hash(hash) + } + }) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_c, + para_id, + Ancestors::new(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Deactivate C and add another candidate that will be present on the deactivated parent A. + // When activating C again it should also get the new candidate. Deactivated leaves are + // still updated with new candidates. + deactivate_leaf(&mut virtual_overseer, leaf_c.hash).await; + + let (candidate_e, _) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_d, 5); + activate_leaf_with_parent_hash_fn(&mut virtual_overseer, &leaf_c, &test_state, |hash| { + if hash == leaf_c.hash { + leaf_a.hash + } else { + get_parent_hash(hash) + } + }) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![ + (candidate_c.hash(), leaf_a.hash), + (candidate_d.hash(), leaf_a.hash), + (candidate_e.hash(), leaf_a.hash), + ], + ) + .await; + + all_candidates_resp.push((candidate_e.hash(), leaf_a.hash)); + get_backable_candidates( + &mut virtual_overseer, + &leaf_c, + para_id, + Ancestors::new(), + 5, + all_candidates_resp, + ) + .await; + + // Querying the backable candidates for deactivated leaf won't work. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::new(), + 5, + vec![], + ) + .await; + + virtual_overseer + }); + + assert_eq!(view.active_leaves.len(), 2); + assert_eq!(view.per_relay_parent.len(), 3); +} + +#[test] +fn handle_active_leaves_update_bounded_implicit_view() { + let para_id = ParaId::from(1); + let mut test_state = TestState::default(); + test_state.claim_queue = test_state + .claim_queue + .into_iter() + .filter(|(_, paras)| matches!(paras.front(), Some(para) if para == ¶_id)) + .collect(); + assert_eq!(test_state.claim_queue.len(), 1); + + let mut leaves = vec![TestLeaf { + number: 100, + hash: Hash::from_low_u64_be(130), + para_data: vec![( + para_id, + PerParaData::new(100 - ALLOWED_ANCESTRY_LEN, HeadData(vec![1, 2, 3])), + )], + }]; + + for index in 1..10 { + let prev_leaf = &leaves[index - 1]; + leaves.push(TestLeaf { + number: prev_leaf.number - 1, + hash: get_parent_hash(prev_leaf.hash), + para_data: vec![( + para_id, + PerParaData::new( + prev_leaf.number - 1 - ALLOWED_ANCESTRY_LEN, + HeadData(vec![1, 2, 3]), + ), + )], + }); + } + leaves.reverse(); + + let view = test_harness(|mut virtual_overseer| async { + // Activate first 10 leaves. + for leaf in &leaves[0..10] { + activate_leaf(&mut virtual_overseer, leaf, &test_state).await; + } + + // Now deactivate first 9 leaves. + for leaf in &leaves[0..9] { + deactivate_leaf(&mut virtual_overseer, leaf.hash).await; + } + + virtual_overseer + }); + + // Only latest leaf is active. + assert_eq!(view.active_leaves.len(), 1); + // We keep allowed_ancestry_len implicit leaves. The latest leaf is also present here. + assert_eq!( + view.per_relay_parent.len() as u32, + ASYNC_BACKING_PARAMETERS.allowed_ancestry_len + 1 + ); + + assert_eq!(view.active_leaves, [leaves[9].hash].into_iter().collect()); + assert_eq!( + view.per_relay_parent.into_keys().collect::>(), + leaves[6..].into_iter().map(|l| l.hash).collect::>() + ); } #[test] @@ -2251,7 +2530,8 @@ fn persists_pending_availability_candidate() { ); let candidate_hash_b = candidate_b.hash(); - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) + .await; back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; let candidate_a_pending_av = CandidatePendingAvailability { @@ -2275,6 +2555,15 @@ fn persists_pending_availability_candidate() { }; activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await; + get_hypothetical_membership( + &mut virtual_overseer, + candidate_hash_a, + candidate_a, + pvd_a, + vec![leaf_a.hash, leaf_b.hash], + ) + .await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 3f622a60a059bbdb3062a90bd93d2661ad96d326..ffc5859b7756cad5a2818138d6c8921a617f36ea 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -273,7 +273,7 @@ async fn handle_communication( let span = state.span.child("provisionable-data"); let _timer = metrics.time_provisionable_data(); - gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data."); + gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data: {:?}", &data); note_provisionable_data(state, &span, data); } @@ -794,9 +794,11 @@ async fn select_candidates( relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { - gum::trace!(target: LOG_TARGET, + gum::trace!( + target: LOG_TARGET, leaf_hash=?relay_parent, - "before GetBackedCandidates"); + "before GetBackedCandidates" + ); let selected_candidates = match prospective_parachains_mode { ProspectiveParachainsMode::Enabled { .. } => diff --git a/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs b/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs index 97a03e6596d16e2d10219331e58dd2d19647b531..342128b7cca21d5fd00544062e1cb35ba4126239 100644 --- a/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs +++ b/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs @@ -116,7 +116,7 @@ fn host_prepare_rococo_runtime(c: &mut Criterion) { cfg.prepare_workers_hard_max_num = 1; }) .await, - pvf.clone().code(), + pvf.clone().maybe_compressed_code(), ) }, |result| async move { diff --git a/polkadot/node/core/pvf/common/Cargo.toml b/polkadot/node/core/pvf/common/Cargo.toml index 18b3f959c95513763addc9ddf4ff82783e5c1a02..903c8dd1af2970adca2ff4f62b8d243f87bc88e0 100644 --- a/polkadot/node/core/pvf/common/Cargo.toml +++ b/polkadot/node/core/pvf/common/Cargo.toml @@ -17,9 +17,7 @@ libc = { workspace = true } nix = { features = ["resource", "sched"], workspace = true } thiserror = { workspace = true } -codec = { features = [ - "derive", -], workspace = true } +codec = { features = ["derive"], workspace = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } @@ -42,6 +40,8 @@ seccompiler = "0.4.0" [dev-dependencies] assert_matches = { workspace = true } + +[target.'cfg(target_os = "linux")'.dev-dependencies] tempfile = { workspace = true } [features] diff --git a/polkadot/node/core/pvf/common/src/error.rs b/polkadot/node/core/pvf/common/src/error.rs index 7ee05448d3c5cb64f54d8ed64dd865da1ad8c87a..b0cdba9501dbeec396c2262da8d02f2627992ed0 100644 --- a/polkadot/node/core/pvf/common/src/error.rs +++ b/polkadot/node/core/pvf/common/src/error.rs @@ -94,6 +94,10 @@ pub enum PrepareError { #[codec(index = 11)] #[error("prepare: error interfacing with the kernel: {0}")] Kernel(String), + /// Code blob failed to decompress + #[codec(index = 12)] + #[error("prepare: could not decompress code blob: {0}")] + CouldNotDecompressCodeBlob(String), } impl PrepareError { @@ -106,7 +110,11 @@ impl PrepareError { pub fn is_deterministic(&self) -> bool { use PrepareError::*; match self { - Prevalidation(_) | Preparation(_) | JobError(_) | OutOfMemory => true, + Prevalidation(_) | + Preparation(_) | + JobError(_) | + OutOfMemory | + CouldNotDecompressCodeBlob(_) => true, IoErr(_) | JobDied { .. } | CreateTmpFile(_) | diff --git a/polkadot/node/core/pvf/common/src/execute.rs b/polkadot/node/core/pvf/common/src/execute.rs index 46862f9f80b60324185363c595e221582ce53b1e..cff3f3b86e95265956ef935427a570e8565a7d7f 100644 --- a/polkadot/node/core/pvf/common/src/execute.rs +++ b/polkadot/node/core/pvf/common/src/execute.rs @@ -35,6 +35,8 @@ pub struct WorkerResponse { pub job_response: JobResponse, /// The amount of CPU time taken by the job. pub duration: Duration, + /// The uncompressed PoV size. + pub pov_size: u32, } /// An error occurred in the worker process. @@ -77,6 +79,8 @@ pub enum JobResponse { RuntimeConstruction(String), /// The candidate is invalid. InvalidCandidate(String), + /// PoV decompression failed + PoVDecompressionFailure, } impl JobResponse { diff --git a/polkadot/node/core/pvf/common/src/prepare.rs b/polkadot/node/core/pvf/common/src/prepare.rs index 81e165a7b8a499432b4b766b43b9400172ba5822..4cd1beb309918b1dedb7e3a0e7ee2dd03794c50d 100644 --- a/polkadot/node/core/pvf/common/src/prepare.rs +++ b/polkadot/node/core/pvf/common/src/prepare.rs @@ -44,6 +44,8 @@ pub struct PrepareStats { pub cpu_time_elapsed: std::time::Duration, /// The observed memory statistics for the preparation job. pub memory_stats: MemoryStats, + /// The decompressed Wasm code length observed during the preparation. + pub observed_wasm_code_len: u32, } /// Helper struct to contain all the memory stats, including `MemoryAllocationStats` and, if diff --git a/polkadot/node/core/pvf/common/src/pvf.rs b/polkadot/node/core/pvf/common/src/pvf.rs index e2ac36a2406ac28213107d1e0b42d8f215e04ea3..4019a8d8b0d0031a1a44b345b59124a994606d32 100644 --- a/polkadot/node/core/pvf/common/src/pvf.rs +++ b/polkadot/node/core/pvf/common/src/pvf.rs @@ -26,9 +26,9 @@ use std::{fmt, sync::Arc, time::Duration}; /// Should be cheap to clone. #[derive(Clone, Encode, Decode)] pub struct PvfPrepData { - /// Wasm code (uncompressed) - code: Arc>, - /// Wasm code hash + /// Wasm code (maybe compressed) + maybe_compressed_code: Arc>, + /// Wasm code hash. code_hash: ValidationCodeHash, /// Executor environment parameters for the session for which artifact is prepared executor_params: Arc, @@ -46,20 +46,20 @@ impl PvfPrepData { prep_timeout: Duration, prep_kind: PrepareJobKind, ) -> Self { - let code = Arc::new(code); - let code_hash = sp_crypto_hashing::blake2_256(&code).into(); + let maybe_compressed_code = Arc::new(code); + let code_hash = sp_crypto_hashing::blake2_256(&maybe_compressed_code).into(); let executor_params = Arc::new(executor_params); - Self { code, code_hash, executor_params, prep_timeout, prep_kind } + Self { maybe_compressed_code, code_hash, executor_params, prep_timeout, prep_kind } } - /// Returns validation code hash for the PVF + /// Returns validation code hash pub fn code_hash(&self) -> ValidationCodeHash { self.code_hash } - /// Returns PVF code - pub fn code(&self) -> Arc> { - self.code.clone() + /// Returns PVF code blob + pub fn maybe_compressed_code(&self) -> Arc> { + self.maybe_compressed_code.clone() } /// Returns executor params diff --git a/polkadot/node/core/pvf/execute-worker/Cargo.toml b/polkadot/node/core/pvf/execute-worker/Cargo.toml index f24b66dc4a0e8ba20f6b7a8205c7092a47c30245..6ad340d25612860f87469497c332079e11eda266 100644 --- a/polkadot/node/core/pvf/execute-worker/Cargo.toml +++ b/polkadot/node/core/pvf/execute-worker/Cargo.toml @@ -19,8 +19,11 @@ libc = { workspace = true } codec = { features = ["derive"], workspace = true } polkadot-node-core-pvf-common = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } + [features] builder = [] diff --git a/polkadot/node/core/pvf/execute-worker/src/lib.rs b/polkadot/node/core/pvf/execute-worker/src/lib.rs index 35858ab36cec3fd2990403ae0afcf848e5cb317e..4b7c167cc9ec322f070e9e964fecc5b782d68063 100644 --- a/polkadot/node/core/pvf/execute-worker/src/lib.rs +++ b/polkadot/node/core/pvf/execute-worker/src/lib.rs @@ -22,6 +22,7 @@ pub use polkadot_node_core_pvf_common::{ error::ExecuteError, executor_interface::execute_artifact, }; +use polkadot_parachain_primitives::primitives::ValidationParams; // NOTE: Initializing logging in e.g. tests will not have an effect in the workers, as they are // separate spawned processes. Run with e.g. `RUST_LOG=parachain::pvf-execute-worker=trace`. @@ -50,8 +51,9 @@ use polkadot_node_core_pvf_common::{ }, worker_dir, }; +use polkadot_node_primitives::{BlockData, PoV, POV_BOMB_LIMIT}; use polkadot_parachain_primitives::primitives::ValidationResult; -use polkadot_primitives::ExecutorParams; +use polkadot_primitives::{ExecutorParams, PersistedValidationData}; use std::{ io::{self, Read}, os::{ @@ -85,8 +87,23 @@ fn recv_execute_handshake(stream: &mut UnixStream) -> io::Result { Ok(handshake) } -fn recv_request(stream: &mut UnixStream) -> io::Result<(Vec, Duration)> { - let params = framed_recv_blocking(stream)?; +fn recv_request(stream: &mut UnixStream) -> io::Result<(PersistedValidationData, PoV, Duration)> { + let pvd = framed_recv_blocking(stream)?; + let pvd = PersistedValidationData::decode(&mut &pvd[..]).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "execute pvf recv_request: failed to decode persisted validation data".to_string(), + ) + })?; + + let pov = framed_recv_blocking(stream)?; + let pov = PoV::decode(&mut &pov[..]).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "execute pvf recv_request: failed to decode PoV".to_string(), + ) + })?; + let execution_timeout = framed_recv_blocking(stream)?; let execution_timeout = Duration::decode(&mut &execution_timeout[..]).map_err(|_| { io::Error::new( @@ -94,7 +111,7 @@ fn recv_request(stream: &mut UnixStream) -> io::Result<(Vec, Duration)> { "execute pvf recv_request: failed to decode duration".to_string(), ) })?; - Ok((params, execution_timeout)) + Ok((pvd, pov, execution_timeout)) } /// Sends an error to the host and returns the original error wrapped in `io::Error`. @@ -149,7 +166,7 @@ pub fn worker_entrypoint( let execute_thread_stack_size = max_stack_size(&executor_params); loop { - let (params, execution_timeout) = recv_request(&mut stream).map_err(|e| { + let (pvd, pov, execution_timeout) = recv_request(&mut stream).map_err(|e| { map_and_send_err!( e, InternalValidationError::HostCommunication, @@ -197,7 +214,33 @@ pub fn worker_entrypoint( let stream_fd = stream.as_raw_fd(); let compiled_artifact_blob = Arc::new(compiled_artifact_blob); - let params = Arc::new(params); + + let raw_block_data = + match sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT) { + Ok(data) => data, + Err(_) => { + send_result::( + &mut stream, + Ok(WorkerResponse { + job_response: JobResponse::PoVDecompressionFailure, + duration: Duration::ZERO, + pov_size: 0, + }), + worker_info, + )?; + continue; + }, + }; + + let pov_size = raw_block_data.len() as u32; + + let params = ValidationParams { + parent_head: pvd.parent_head.clone(), + block_data: BlockData(raw_block_data.to_vec()), + relay_parent_number: pvd.relay_parent_number, + relay_parent_storage_root: pvd.relay_parent_storage_root, + }; + let params = Arc::new(params.encode()); cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { @@ -214,6 +257,7 @@ pub fn worker_entrypoint( worker_info, security_status.can_unshare_user_namespace_and_change_root, usage_before, + pov_size, )? } else { // Fall back to using fork. @@ -228,6 +272,7 @@ pub fn worker_entrypoint( execute_thread_stack_size, worker_info, usage_before, + pov_size, )? }; } else { @@ -242,6 +287,7 @@ pub fn worker_entrypoint( execute_thread_stack_size, worker_info, usage_before, + pov_size, )?; } } @@ -300,6 +346,7 @@ fn handle_clone( worker_info: &WorkerInfo, have_unshare_newuser: bool, usage_before: Usage, + pov_size: u32, ) -> io::Result> { use polkadot_node_core_pvf_common::worker::security; @@ -329,6 +376,7 @@ fn handle_clone( worker_info, child, usage_before, + pov_size, execution_timeout, ), Err(security::clone::Error::Clone(errno)) => @@ -347,6 +395,7 @@ fn handle_fork( execute_worker_stack_size: usize, worker_info: &WorkerInfo, usage_before: Usage, + pov_size: u32, ) -> io::Result> { // SAFETY: new process is spawned within a single threaded process. This invariant // is enforced by tests. @@ -367,6 +416,7 @@ fn handle_fork( worker_info, child, usage_before, + pov_size, execution_timeout, ), Err(errno) => Ok(Err(internal_error_from_errno("fork", errno))), @@ -513,6 +563,7 @@ fn handle_parent_process( worker_info: &WorkerInfo, job_pid: Pid, usage_before: Usage, + pov_size: u32, timeout: Duration, ) -> io::Result> { // the read end will wait until all write ends have been closed, @@ -578,7 +629,7 @@ fn handle_parent_process( )))); } - Ok(Ok(WorkerResponse { job_response, duration: cpu_tv })) + Ok(Ok(WorkerResponse { job_response, pov_size, duration: cpu_tv })) }, Err(job_error) => { gum::warn!( diff --git a/polkadot/node/core/pvf/prepare-worker/Cargo.toml b/polkadot/node/core/pvf/prepare-worker/Cargo.toml index 9e0d01fc438b0257b87b9bf6791f466d8691049e..56235bd82192f2e671eb0b9e08006a186966a814 100644 --- a/polkadot/node/core/pvf/prepare-worker/Cargo.toml +++ b/polkadot/node/core/pvf/prepare-worker/Cargo.toml @@ -23,10 +23,12 @@ nix = { features = ["process", "resource", "sched"], workspace = true } codec = { features = ["derive"], workspace = true } polkadot-node-core-pvf-common = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } sc-executor-common = { workspace = true, default-features = true } sc-executor-wasmtime = { workspace = true, default-features = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } [target.'cfg(target_os = "linux")'.dependencies] tikv-jemallocator = "0.5.0" diff --git a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs index d531c90b64b578e31f42a13f2399b4343469fa6d..49b30dc33ceb7695f0c6aba19279d6ed870edb7b 100644 --- a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs +++ b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs @@ -24,7 +24,11 @@ use polkadot_primitives::ExecutorParams; use std::time::Duration; fn do_prepare_runtime(pvf: PvfPrepData) { - let blob = match prevalidate(&pvf.code()) { + let maybe_compressed_code = pvf.maybe_compressed_code(); + let raw_validation_code = + sp_maybe_compressed_blob::decompress(&maybe_compressed_code, usize::MAX).unwrap(); + + let blob = match prevalidate(&raw_validation_code) { Err(err) => panic!("{:?}", err), Ok(b) => b, }; diff --git a/polkadot/node/core/pvf/prepare-worker/src/lib.rs b/polkadot/node/core/pvf/prepare-worker/src/lib.rs index ef33d11720eb5a7c4b7fa11b3484acbd5ad84d43..f8ebb6effcecdc4d3e71a95bbdaecfe51705352e 100644 --- a/polkadot/node/core/pvf/prepare-worker/src/lib.rs +++ b/polkadot/node/core/pvf/prepare-worker/src/lib.rs @@ -38,6 +38,7 @@ use polkadot_node_core_pvf_common::{ executor_interface::{prepare, prevalidate}, worker::{pipe2_cloexec, PipeFd, WorkerInfo}, }; +use polkadot_node_primitives::VALIDATION_CODE_BOMB_LIMIT; use codec::{Decode, Encode}; use polkadot_node_core_pvf_common::{ @@ -105,6 +106,12 @@ impl AsRef<[u8]> for CompiledArtifact { } } +#[derive(Encode, Decode)] +pub struct PrepareOutcome { + pub compiled_artifact: CompiledArtifact, + pub observed_wasm_code_len: u32, +} + /// Get a worker request. fn recv_request(stream: &mut UnixStream) -> io::Result { let pvf = framed_recv_blocking(stream)?; @@ -294,14 +301,23 @@ pub fn worker_entrypoint( ); } -fn prepare_artifact(pvf: PvfPrepData) -> Result { - let blob = match prevalidate(&pvf.code()) { +fn prepare_artifact(pvf: PvfPrepData) -> Result { + let maybe_compressed_code = pvf.maybe_compressed_code(); + let raw_validation_code = + sp_maybe_compressed_blob::decompress(&maybe_compressed_code, VALIDATION_CODE_BOMB_LIMIT) + .map_err(|e| PrepareError::CouldNotDecompressCodeBlob(e.to_string()))?; + let observed_wasm_code_len = raw_validation_code.len() as u32; + + let blob = match prevalidate(&raw_validation_code) { Err(err) => return Err(PrepareError::Prevalidation(format!("{:?}", err))), Ok(b) => b, }; match prepare(blob, &pvf.executor_params()) { - Ok(compiled_artifact) => Ok(CompiledArtifact::new(compiled_artifact)), + Ok(compiled_artifact) => Ok(PrepareOutcome { + compiled_artifact: CompiledArtifact::new(compiled_artifact), + observed_wasm_code_len, + }), Err(err) => Err(PrepareError::Preparation(format!("{:?}", err))), } } @@ -322,6 +338,7 @@ fn runtime_construction_check( struct JobResponse { artifact: CompiledArtifact, memory_stats: MemoryStats, + observed_wasm_code_len: u32, } #[cfg(target_os = "linux")] @@ -500,11 +517,11 @@ fn handle_child_process( "prepare worker", move || { #[allow(unused_mut)] - let mut result = prepare_artifact(pvf); + let mut result = prepare_artifact(pvf).map(|o| (o,)); // Get the `ru_maxrss` stat. If supported, call getrusage for the thread. #[cfg(target_os = "linux")] - let mut result = result.map(|artifact| (artifact, get_max_rss_thread())); + let mut result = result.map(|outcome| (outcome.0, get_max_rss_thread())); // If we are pre-checking, check for runtime construction errors. // @@ -513,7 +530,10 @@ fn handle_child_process( // anyway. if let PrepareJobKind::Prechecking = prepare_job_kind { result = result.and_then(|output| { - runtime_construction_check(output.0.as_ref(), &executor_params)?; + runtime_construction_check( + output.0.compiled_artifact.as_ref(), + &executor_params, + )?; Ok(output) }); } @@ -553,9 +573,9 @@ fn handle_child_process( Ok(ok) => { cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - let (artifact, max_rss) = ok; + let (PrepareOutcome { compiled_artifact, observed_wasm_code_len }, max_rss) = ok; } else { - let artifact = ok; + let (PrepareOutcome { compiled_artifact, observed_wasm_code_len },) = ok; } } @@ -574,7 +594,11 @@ fn handle_child_process( peak_tracked_alloc: if peak_alloc > 0 { peak_alloc as u64 } else { 0u64 }, }; - Ok(JobResponse { artifact, memory_stats }) + Ok(JobResponse { + artifact: compiled_artifact, + observed_wasm_code_len, + memory_stats, + }) }, } }, @@ -665,7 +689,7 @@ fn handle_parent_process( match result { Err(err) => Err(err), - Ok(JobResponse { artifact, memory_stats }) => { + Ok(JobResponse { artifact, memory_stats, observed_wasm_code_len }) => { // The exit status should have been zero if no error occurred. if exit_status != 0 { return Err(PrepareError::JobError(format!( @@ -696,7 +720,11 @@ fn handle_parent_process( let checksum = blake3::hash(&artifact.as_ref()).to_hex().to_string(); Ok(PrepareWorkerSuccess { checksum, - stats: PrepareStats { memory_stats, cpu_time_elapsed: cpu_tv }, + stats: PrepareStats { + memory_stats, + cpu_time_elapsed: cpu_tv, + observed_wasm_code_len, + }, }) }, } diff --git a/polkadot/node/core/pvf/src/error.rs b/polkadot/node/core/pvf/src/error.rs index 8dc96305eadb8f3ced1231889f5fa6c5ffaeac57..a0634106052d7fb197c0662854415190a59a0e70 100644 --- a/polkadot/node/core/pvf/src/error.rs +++ b/polkadot/node/core/pvf/src/error.rs @@ -52,6 +52,9 @@ pub enum InvalidCandidate { /// PVF execution (compilation is not included) took more time than was allotted. #[error("invalid: hard timeout")] HardTimeout, + /// Proof-of-validity failed to decompress correctly + #[error("invalid: PoV failed to decompress")] + PoVDecompressionFailure, } /// Possibly transient issue that may resolve after retries. diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index bb00a5a652d6434cd06327803d9ce51a3d5c1e93..11031bf1074a297e246a4e341efe8488a2692b1b 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -34,12 +34,14 @@ use polkadot_node_core_pvf_common::{ execute::{JobResponse, WorkerError, WorkerResponse}, SecurityStatus, }; -use polkadot_primitives::{ExecutorParams, ExecutorParamsHash}; +use polkadot_node_primitives::PoV; +use polkadot_primitives::{ExecutorParams, ExecutorParamsHash, PersistedValidationData}; use slotmap::HopSlotMap; use std::{ collections::VecDeque, fmt, path::PathBuf, + sync::Arc, time::{Duration, Instant}, }; @@ -68,7 +70,8 @@ pub enum FromQueue { #[derive(Debug)] pub struct PendingExecutionRequest { pub exec_timeout: Duration, - pub params: Vec, + pub pvd: Arc, + pub pov: Arc, pub executor_params: ExecutorParams, pub result_tx: ResultSender, } @@ -76,7 +79,8 @@ pub struct PendingExecutionRequest { struct ExecuteJob { artifact: ArtifactPathId, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, executor_params: ExecutorParams, result_tx: ResultSender, waiting_since: Instant, @@ -293,18 +297,20 @@ async fn purge_dead(metrics: &Metrics, workers: &mut Workers) { fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) { let ToQueue::Enqueue { artifact, pending_execution_request } = to_queue; - let PendingExecutionRequest { exec_timeout, params, executor_params, result_tx } = + let PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } = pending_execution_request; gum::debug!( target: LOG_TARGET, validation_code_hash = ?artifact.id.code_hash, "enqueueing an artifact for execution", ); + queue.metrics.observe_pov_size(pov.block_data.0.len(), true); queue.metrics.execute_enqueued(); let job = ExecuteJob { artifact, exec_timeout, - params, + pvd, + pov, executor_params, result_tx, waiting_since: Instant::now(), @@ -352,15 +358,19 @@ async fn handle_job_finish( artifact_id: ArtifactId, result_tx: ResultSender, ) { - let (idle_worker, result, duration, sync_channel) = match worker_result { + let (idle_worker, result, duration, sync_channel, pov_size) = match worker_result { Ok(WorkerInterfaceResponse { worker_response: - WorkerResponse { job_response: JobResponse::Ok { result_descriptor }, duration }, + WorkerResponse { + job_response: JobResponse::Ok { result_descriptor }, + duration, + pov_size, + }, idle_worker, }) => { // TODO: propagate the soft timeout - (Some(idle_worker), Ok(result_descriptor), Some(duration), None) + (Some(idle_worker), Ok(result_descriptor), Some(duration), None, Some(pov_size)) }, Ok(WorkerInterfaceResponse { worker_response: WorkerResponse { job_response: JobResponse::InvalidCandidate(err), .. }, @@ -370,6 +380,18 @@ async fn handle_job_finish( Err(ValidationError::Invalid(InvalidCandidate::WorkerReportedInvalid(err))), None, None, + None, + ), + Ok(WorkerInterfaceResponse { + worker_response: + WorkerResponse { job_response: JobResponse::PoVDecompressionFailure, .. }, + idle_worker, + }) => ( + Some(idle_worker), + Err(ValidationError::Invalid(InvalidCandidate::PoVDecompressionFailure)), + None, + None, + None, ), Ok(WorkerInterfaceResponse { worker_response: @@ -393,39 +415,46 @@ async fn handle_job_finish( ))), None, Some(result_rx), + None, ) }, Err(WorkerInterfaceError::InternalError(err)) | Err(WorkerInterfaceError::WorkerError(WorkerError::InternalError(err))) => - (None, Err(ValidationError::Internal(err)), None, None), + (None, Err(ValidationError::Internal(err)), None, None, None), // Either the worker or the job timed out. Kill the worker in either case. Treated as // definitely-invalid, because if we timed out, there's no time left for a retry. Err(WorkerInterfaceError::HardTimeout) | Err(WorkerInterfaceError::WorkerError(WorkerError::JobTimedOut)) => - (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None), + (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None, None), // "Maybe invalid" errors (will retry). Err(WorkerInterfaceError::CommunicationErr(_err)) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)), None, None, + None, ), Err(WorkerInterfaceError::WorkerError(WorkerError::JobDied { err, .. })) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))), None, None, + None, ), Err(WorkerInterfaceError::WorkerError(WorkerError::JobError(err))) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err.to_string()))), None, None, + None, ), }; queue.metrics.execute_finished(); + if let Some(pov_size) = pov_size { + queue.metrics.observe_pov_size(pov_size as usize, false) + } if let Err(ref err) = result { gum::warn!( target: LOG_TARGET, @@ -573,7 +602,8 @@ fn assign(queue: &mut Queue, worker: Worker, job: ExecuteJob) { idle, job.artifact.clone(), job.exec_timeout, - job.params, + job.pvd, + job.pov, ) .await; QueueEvent::StartWork(worker, result, job.artifact.id, job.result_tx) diff --git a/polkadot/node/core/pvf/src/execute/worker_interface.rs b/polkadot/node/core/pvf/src/execute/worker_interface.rs index d15d7c15426ebd63d33aaf1166bc8f000e28eb7a..77bd6bedd75c7f9932bc548d019870f87a1e201e 100644 --- a/polkadot/node/core/pvf/src/execute/worker_interface.rs +++ b/polkadot/node/core/pvf/src/execute/worker_interface.rs @@ -32,8 +32,9 @@ use polkadot_node_core_pvf_common::{ execute::{Handshake, WorkerError, WorkerResponse}, worker_dir, SecurityStatus, }; -use polkadot_primitives::ExecutorParams; -use std::{path::Path, time::Duration}; +use polkadot_node_primitives::PoV; +use polkadot_primitives::{ExecutorParams, PersistedValidationData}; +use std::{path::Path, sync::Arc, time::Duration}; use tokio::{io, net::UnixStream}; /// Spawns a new worker with the given program path that acts as the worker and the spawn timeout. @@ -123,7 +124,8 @@ pub async fn start_work( worker: IdleWorker, artifact: ArtifactPathId, execution_timeout: Duration, - validation_params: Vec, + pvd: Arc, + pov: Arc, ) -> Result { let IdleWorker { mut stream, pid, worker_dir } = worker; @@ -137,18 +139,16 @@ pub async fn start_work( ); with_worker_dir_setup(worker_dir, pid, &artifact.path, |worker_dir| async move { - send_request(&mut stream, &validation_params, execution_timeout).await.map_err( - |error| { - gum::warn!( - target: LOG_TARGET, - worker_pid = %pid, - validation_code_hash = ?artifact.id.code_hash, - "failed to send an execute request: {}", - error, - ); - Error::InternalError(InternalValidationError::HostCommunication(error.to_string())) - }, - )?; + send_request(&mut stream, pvd, pov, execution_timeout).await.map_err(|error| { + gum::warn!( + target: LOG_TARGET, + worker_pid = %pid, + validation_code_hash = ?artifact.id.code_hash, + "failed to send an execute request: {}", + error, + ); + Error::InternalError(InternalValidationError::HostCommunication(error.to_string())) + })?; // We use a generous timeout here. This is in addition to the one in the child process, in // case the child stalls. We have a wall clock timeout here in the host, but a CPU timeout @@ -288,10 +288,12 @@ async fn send_execute_handshake(stream: &mut UnixStream, handshake: Handshake) - async fn send_request( stream: &mut UnixStream, - validation_params: &[u8], + pvd: Arc, + pov: Arc, execution_timeout: Duration, ) -> io::Result<()> { - framed_send(stream, validation_params).await?; + framed_send(stream, &pvd.encode()).await?; + framed_send(stream, &pov.encode()).await?; framed_send(stream, &execution_timeout.encode()).await } diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index 462631d33b525a7d77817de87711a650517d4204..44a4cba2fbf864d16117ee98ff4034e2469f55a2 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -36,11 +36,14 @@ use polkadot_node_core_pvf_common::{ prepare::PrepareSuccess, pvf::PvfPrepData, }; +use polkadot_node_primitives::PoV; use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_parachain_primitives::primitives::ValidationResult; +use polkadot_primitives::PersistedValidationData; use std::{ collections::HashMap, path::PathBuf, + sync::Arc, time::{Duration, SystemTime}, }; @@ -108,7 +111,8 @@ impl ValidationHost { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, priority: Priority, result_tx: ResultSender, ) -> Result<(), String> { @@ -116,7 +120,8 @@ impl ValidationHost { .send(ToHost::ExecutePvf(ExecutePvfInputs { pvf, exec_timeout, - params, + pvd, + pov, priority, result_tx, })) @@ -147,7 +152,8 @@ enum ToHost { struct ExecutePvfInputs { pvf: PvfPrepData, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, priority: Priority, result_tx: ResultSender, } @@ -539,7 +545,7 @@ async fn handle_execute_pvf( awaiting_prepare: &mut AwaitingPrepare, inputs: ExecutePvfInputs, ) -> Result<(), Fatal> { - let ExecutePvfInputs { pvf, exec_timeout, params, priority, result_tx } = inputs; + let ExecutePvfInputs { pvf, exec_timeout, pvd, pov, priority, result_tx } = inputs; let artifact_id = ArtifactId::from_pvf_prep_data(&pvf); let executor_params = (*pvf.executor_params()).clone(); @@ -558,7 +564,8 @@ async fn handle_execute_pvf( artifact: ArtifactPathId::new(artifact_id, path), pending_execution_request: PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -587,7 +594,8 @@ async fn handle_execute_pvf( artifact_id, PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -598,7 +606,7 @@ async fn handle_execute_pvf( ArtifactState::Preparing { .. } => { awaiting_prepare.add( artifact_id, - PendingExecutionRequest { exec_timeout, params, executor_params, result_tx }, + PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, ); }, ArtifactState::FailedToProcess { last_time_failed, num_failures, error } => { @@ -627,7 +635,8 @@ async fn handle_execute_pvf( artifact_id, PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -648,7 +657,7 @@ async fn handle_execute_pvf( pvf, priority, artifact_id, - PendingExecutionRequest { exec_timeout, params, executor_params, result_tx }, + PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, ) .await?; } @@ -770,7 +779,7 @@ async fn handle_prepare_done( // It's finally time to dispatch all the execution requests that were waiting for this artifact // to be prepared. let pending_requests = awaiting_prepare.take(&artifact_id); - for PendingExecutionRequest { exec_timeout, params, executor_params, result_tx } in + for PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } in pending_requests { if result_tx.is_canceled() { @@ -793,7 +802,8 @@ async fn handle_prepare_done( artifact: ArtifactPathId::new(artifact_id.clone(), &path), pending_execution_request: PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -967,6 +977,8 @@ pub(crate) mod tests { use assert_matches::assert_matches; use futures::future::BoxFuture; use polkadot_node_core_pvf_common::prepare::PrepareStats; + use polkadot_node_primitives::BlockData; + use sp_core::H256; const TEST_EXECUTION_TIMEOUT: Duration = Duration::from_secs(3); pub(crate) const TEST_PREPARATION_TIMEOUT: Duration = Duration::from_secs(30); @@ -1223,12 +1235,21 @@ pub(crate) mod tests { async fn execute_pvf_requests() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov1 = Arc::new(PoV { block_data: BlockData(b"pov1".to_vec()) }); + let pov2 = Arc::new(PoV { block_data: BlockData(b"pov2".to_vec()) }); let (result_tx, result_rx_pvf_1_1) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd.clone(), + pov1.clone(), Priority::Normal, result_tx, ) @@ -1239,7 +1260,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd.clone(), + pov1, Priority::Critical, result_tx, ) @@ -1250,7 +1272,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(2), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd, + pov2, Priority::Normal, result_tx, ) @@ -1382,6 +1405,13 @@ pub(crate) mod tests { async fn test_prepare_done() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Test mixed cases of receiving execute and precheck requests // for the same PVF. @@ -1391,7 +1421,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1438,7 +1469,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(2), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd, + pov, Priority::Critical, result_tx, ) @@ -1534,13 +1566,21 @@ pub(crate) mod tests { async fn test_execute_prepare_retry() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Submit a execute request that fails. let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1570,7 +1610,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_2, ) @@ -1592,7 +1633,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_3, ) @@ -1636,13 +1678,21 @@ pub(crate) mod tests { async fn test_execute_prepare_no_retry() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Submit an execute request that fails. let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1672,7 +1722,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_2, ) @@ -1694,7 +1745,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_3, ) @@ -1755,12 +1807,20 @@ pub(crate) mod tests { async fn cancellation() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd, + pov, Priority::Normal, result_tx, ) diff --git a/polkadot/node/core/pvf/src/metrics.rs b/polkadot/node/core/pvf/src/metrics.rs index bc8d300037fe8e1d7c70b575d99b101b8343e94b..c59cab4641805eca5dac8d60e554e21b79c97606 100644 --- a/polkadot/node/core/pvf/src/metrics.rs +++ b/polkadot/node/core/pvf/src/metrics.rs @@ -105,6 +105,21 @@ impl Metrics { .observe((memory_stats.peak_tracked_alloc / 1024) as f64); } } + + pub(crate) fn observe_code_size(&self, code_size: usize) { + if let Some(metrics) = &self.0 { + metrics.code_size.observe(code_size as f64); + } + } + + pub(crate) fn observe_pov_size(&self, pov_size: usize, compressed: bool) { + if let Some(metrics) = &self.0 { + metrics + .pov_size + .with_label_values(&[if compressed { "true" } else { "false" }]) + .observe(pov_size as f64); + } + } } #[derive(Clone)] @@ -129,6 +144,8 @@ struct MetricsInner { preparation_max_resident: prometheus::Histogram, // Peak allocation value, tracked by tracking-allocator preparation_peak_tracked_allocation: prometheus::Histogram, + pov_size: prometheus::HistogramVec, + code_size: prometheus::Histogram, } impl metrics::Metrics for Metrics { @@ -323,6 +340,35 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + // The following metrics was moved here from the candidate valiidation subsystem. + // Names are kept to avoid breaking dashboards and stuff. + pov_size: prometheus::register( + prometheus::HistogramVec::new( + prometheus::HistogramOpts::new( + "polkadot_parachain_candidate_validation_pov_size", + "The compressed and decompressed size of the proof of validity of a candidate", + ) + .buckets( + prometheus::exponential_buckets(16384.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + &["compressed"], + )?, + registry, + )?, + code_size: prometheus::register( + prometheus::Histogram::with_opts( + prometheus::HistogramOpts::new( + "polkadot_parachain_candidate_validation_code_size", + "The size of the decompressed WASM validation blob used for checking a candidate", + ) + .buckets( + prometheus::exponential_buckets(16384.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + )?, + registry, + )?, }; Ok(Metrics(Some(inner))) } diff --git a/polkadot/node/core/pvf/src/prepare/worker_interface.rs b/polkadot/node/core/pvf/src/prepare/worker_interface.rs index 22ee93319d84db14ed53a89efe872c7121d87cbd..d29d2717c4b6e38239c8357a711a2899f4010274 100644 --- a/polkadot/node/core/pvf/src/prepare/worker_interface.rs +++ b/polkadot/node/core/pvf/src/prepare/worker_interface.rs @@ -211,7 +211,7 @@ async fn handle_response( // https://github.com/paritytech/polkadot-sdk/issues/2399 let PrepareWorkerSuccess { checksum: _, - stats: PrepareStats { cpu_time_elapsed, memory_stats }, + stats: PrepareStats { cpu_time_elapsed, memory_stats, observed_wasm_code_len }, } = match result.clone() { Ok(result) => result, // Timed out on the child. This should already be logged by the child. @@ -221,6 +221,8 @@ async fn handle_response( Err(err) => return Outcome::Concluded { worker, result: Err(err) }, }; + metrics.observe_code_size(observed_wasm_code_len as usize); + if cpu_time_elapsed > preparation_timeout { // The job didn't complete within the timeout. gum::warn!( @@ -267,7 +269,11 @@ async fn handle_response( result: Ok(PrepareSuccess { path: artifact_path, size, - stats: PrepareStats { cpu_time_elapsed, memory_stats: memory_stats.clone() }, + stats: PrepareStats { + cpu_time_elapsed, + memory_stats: memory_stats.clone(), + observed_wasm_code_len, + }, }), }, Err(err) => { diff --git a/polkadot/node/core/pvf/tests/it/adder.rs b/polkadot/node/core/pvf/tests/it/adder.rs index 455e8c36c88d7c6718a2ce949c0fc961455d06f4..1a95a28fe07732f51d5a8bdd988ee51c79e66a3e 100644 --- a/polkadot/node/core/pvf/tests/it/adder.rs +++ b/polkadot/node/core/pvf/tests/it/adder.rs @@ -18,29 +18,33 @@ use super::TestHost; use codec::{Decode, Encode}; +use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ - BlockData as GenericBlockData, HeadData as GenericHeadData, RelayChainBlockNumber, - ValidationParams, + BlockData as GenericBlockData, HeadData as GenericHeadData, }; +use polkadot_primitives::PersistedValidationData; +use sp_core::H256; use test_parachain_adder::{hash_state, BlockData, HeadData}; #[tokio::test] async fn execute_good_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; - let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -63,18 +67,20 @@ async fn execute_good_chain_on_parent() { for (number, add) in (0..10).enumerate() { let parent_head = HeadData { number: number as u64, parent_hash, post_state: hash_state(last_state) }; - let block_data = BlockData { state: last_state, add }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: number as RelayChainBlockNumber + 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -94,23 +100,25 @@ async fn execute_good_chain_on_parent() { #[tokio::test] async fn execute_bad_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; - let block_data = BlockData { state: 256, // start state is wrong. add: 256, }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let _err = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -124,15 +132,18 @@ async fn stress_spawn() { async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -161,15 +172,18 @@ async fn execute_can_run_serially() { async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index 9ad486657512c7a6dda6c3b907500c074aef7433..a4a0853189579ce96cf96d49f685c3de7de5af00 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -17,7 +17,6 @@ //! General PVF host integration tests checking the functionality of the PVF host itself. use assert_matches::assert_matches; -use codec::Encode as _; #[cfg(all(feature = "ci-only-tests", target_os = "linux"))] use polkadot_node_core_pvf::SecurityStatus; use polkadot_node_core_pvf::{ @@ -25,10 +24,14 @@ use polkadot_node_core_pvf::{ PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; -use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult}; -use polkadot_primitives::{ExecutorParam, ExecutorParams, PvfExecKind, PvfPrepKind}; +use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT}; +use polkadot_parachain_primitives::primitives::{BlockData, ValidationResult}; +use polkadot_primitives::{ + ExecutorParam, ExecutorParams, PersistedValidationData, PvfExecKind, PvfPrepKind, +}; +use sp_core::H256; -use std::{io::Write, time::Duration}; +use std::{io::Write, sync::Arc, time::Duration}; use tokio::sync::Mutex; mod adder; @@ -80,9 +83,6 @@ impl TestHost { ) -> Result<(), PrepareError> { let (result_tx, result_rx) = futures::channel::oneshot::channel(); - let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024) - .expect("Compression works"); - self.host .lock() .await @@ -103,14 +103,12 @@ impl TestHost { async fn validate_candidate( &self, code: &[u8], - params: ValidationParams, + pvd: PersistedValidationData, + pov: PoV, executor_params: ExecutorParams, ) -> Result { let (result_tx, result_rx) = futures::channel::oneshot::channel(); - let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024) - .expect("Compression works"); - self.host .lock() .await @@ -122,7 +120,8 @@ impl TestHost { PrepareJobKind::Compilation, ), TEST_EXECUTION_TIMEOUT, - params.encode(), + Arc::new(pvd), + Arc::new(pov), polkadot_node_core_pvf::Priority::Normal, result_tx, ) @@ -159,19 +158,17 @@ async fn prepare_job_terminates_on_timeout() { #[tokio::test] async fn execute_job_terminates_on_timeout() { let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let start = std::time::Instant::now(); let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; match result { @@ -189,24 +186,23 @@ async fn execute_job_terminates_on_timeout() { async fn ensure_parallel_execution() { // Run some jobs that do not complete, thus timing out. let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let execute_pvf_future_1 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), Default::default(), ); let execute_pvf_future_2 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ); @@ -237,6 +233,13 @@ async fn execute_queue_doesnt_stall_if_workers_died() { cfg.execute_workers_max_num = 5; }) .await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; // Here we spawn 8 validation jobs for the `halt` PVF and share those between 5 workers. The // first five jobs should timeout and the workers killed. For the next 3 jobs a new batch of @@ -245,12 +248,8 @@ async fn execute_queue_doesnt_stall_if_workers_died() { futures::future::join_all((0u8..=8).map(|_| { host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), Default::default(), ) })) @@ -275,6 +274,13 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { cfg.execute_workers_max_num = 2; }) .await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let executor_params_1 = ExecutorParams::default(); let executor_params_2 = ExecutorParams::from(&[ExecutorParam::StackLogicalMax(1024)][..]); @@ -288,12 +294,8 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { futures::future::join_all((0u8..6).map(|i| { host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), match i % 3 { 0 => executor_params_1.clone(), _ => executor_params_2.clone(), @@ -324,6 +326,13 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { async fn deleting_prepared_artifact_does_not_dispute() { let host = TestHost::new().await; let cache_dir = host.cache_dir.path(); + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), Default::default()) @@ -347,16 +356,7 @@ async fn deleting_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get recreated. let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; assert_matches!(result, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout))); @@ -367,6 +367,13 @@ async fn deleting_prepared_artifact_does_not_dispute() { async fn corrupted_prepared_artifact_does_not_dispute() { let host = TestHost::new().await; let cache_dir = host.cache_dir.path(); + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), Default::default()) @@ -400,16 +407,7 @@ async fn corrupted_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get removed because of the corruption. let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; assert_matches!( @@ -652,3 +650,65 @@ async fn artifact_does_reprepare_on_meaningful_exec_parameter_change() { assert_eq!(cache_dir_contents.len(), 3); // new artifact has been added } + +// Checks that we cannot prepare oversized compressed code +#[tokio::test] +async fn invalid_compressed_code_fails_prechecking() { + let host = TestHost::new().await; + let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; + let validation_code = + sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); + + let res = host.precheck_pvf(&validation_code, Default::default()).await; + + assert_matches!(res, Err(PrepareError::CouldNotDecompressCodeBlob(_))); +} + +// Checks that we cannot validate with oversized compressed code +#[tokio::test] +async fn invalid_compressed_code_fails_validation() { + let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; + + let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; + let validation_code = + sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); + + let result = host.validate_candidate(&validation_code, pvd, pov, Default::default()).await; + + assert_matches!( + result, + Err(ValidationError::Preparation(PrepareError::CouldNotDecompressCodeBlob(_))) + ); +} + +// Checks that we cannot validate with an oversized PoV +#[tokio::test] +async fn invalid_compressed_pov_fails_validation() { + let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let raw_block_data = vec![1u8; POV_BOMB_LIMIT + 1]; + let block_data = + sp_maybe_compressed_blob::compress(&raw_block_data, POV_BOMB_LIMIT + 1).unwrap(); + let pov = PoV { block_data: BlockData(block_data) }; + + let result = host + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .await; + + assert_matches!( + result, + Err(ValidationError::Invalid(InvalidCandidate::PoVDecompressionFailure)) + ); +} diff --git a/polkadot/node/core/pvf/tests/it/process.rs b/polkadot/node/core/pvf/tests/it/process.rs index b8fd2cdce0ce67e0b1cfbfc57634bfa4d8c63d46..b3023c8a45c3b3087015e714c22f9bd0474d251e 100644 --- a/polkadot/node/core/pvf/tests/it/process.rs +++ b/polkadot/node/core/pvf/tests/it/process.rs @@ -23,11 +23,14 @@ use codec::Encode; use polkadot_node_core_pvf::{ InvalidCandidate, PossiblyInvalidError, PrepareError, ValidationError, }; +use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ - BlockData as GenericBlockData, HeadData as GenericHeadData, ValidationParams, + BlockData as GenericBlockData, HeadData as GenericHeadData, }; +use polkadot_primitives::PersistedValidationData; use procfs::process; use rusty_fork::rusty_fork_test; +use sp_core::H256; use std::{future::Future, sync::Arc, time::Duration}; use test_parachain_adder::{hash_state, BlockData, HeadData}; @@ -125,15 +128,18 @@ rusty_fork_test! { test_wrapper(|host, _sid| async move { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -166,17 +172,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose an job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Send a stop signal to pause the worker. @@ -218,17 +227,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose an job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that kills the job while it's running. @@ -274,17 +286,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose a job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that kills the job while it's running. @@ -342,17 +357,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let _ = futures::join!( // Choose a job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that tests the thread count while the worker is running. diff --git a/polkadot/node/network/approval-distribution/Cargo.toml b/polkadot/node/network/approval-distribution/Cargo.toml index 1bd3d51b5c933f2b7fe5bf5396687f3e7c59bca7..51478dfa4a4f6836cd533d62e3819922abdd92e0 100644 --- a/polkadot/node/network/approval-distribution/Cargo.toml +++ b/polkadot/node/network/approval-distribution/Cargo.toml @@ -26,6 +26,8 @@ gum = { workspace = true, default-features = true } bitvec = { features = ["alloc"], workspace = true } [dev-dependencies] +sc-keystore = { workspace = true } +sp-application-crypto = { workspace = true, default-features = true } sp-authority-discovery = { workspace = true, default-features = true } sp-core = { features = ["std"], workspace = true, default-features = true } diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index 3462aaef1f6969c3fe0a4d93845c732675b418e5..971b6de5f8f60c1ffcc02fd497516e45d846b4e1 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -24,7 +24,7 @@ #![warn(missing_docs)] use self::metrics::Metrics; -use futures::{channel::oneshot, select, FutureExt as _}; +use futures::{select, FutureExt as _}; use itertools::Itertools; use net_protocol::peer_set::{ProtocolVersion, ValidationVersion}; use polkadot_node_jaeger as jaeger; @@ -35,29 +35,41 @@ use polkadot_node_network_protocol::{ v1 as protocol_v1, v2 as protocol_v2, v3 as protocol_v3, PeerId, UnifiedReputationChange as Rep, Versioned, View, }; -use polkadot_node_primitives::approval::{ - v1::{ - AssignmentCertKind, BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote, - }, - v2::{ - AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, - IndirectSignedApprovalVoteV2, +use polkadot_node_primitives::{ + approval::{ + criteria::{AssignmentCriteria, InvalidAssignment}, + time::{Clock, ClockExt, SystemClock, TICK_TOO_FAR_IN_FUTURE}, + v1::{ + AssignmentCertKind, BlockApprovalMeta, DelayTranche, IndirectAssignmentCert, + IndirectSignedApprovalVote, RelayVRFStory, + }, + v2::{ + AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, + IndirectSignedApprovalVoteV2, + }, }, + DISPUTE_WINDOW, }; use polkadot_node_subsystem::{ messages::{ - ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, - AssignmentCheckResult, NetworkBridgeEvent, NetworkBridgeTxMessage, + ApprovalDistributionMessage, ApprovalVotingMessage, CheckedIndirectAssignment, + CheckedIndirectSignedApprovalVote, NetworkBridgeEvent, NetworkBridgeTxMessage, + RuntimeApiMessage, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_node_subsystem_util::reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}; +use polkadot_node_subsystem_util::{ + reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, + runtime::{Config as RuntimeInfoConfig, ExtendedSessionInfo, RuntimeInfo}, +}; use polkadot_primitives::{ - BlockNumber, CandidateIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, DisputeStatement, GroupIndex, Hash, + SessionIndex, Slot, ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature, }; use rand::{CryptoRng, Rng, SeedableRng}; use std::{ collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}, + sync::Arc, time::Duration, }; @@ -86,6 +98,9 @@ const MAX_BITFIELD_SIZE: usize = 500; /// The Approval Distribution subsystem. pub struct ApprovalDistribution { metrics: Metrics, + slot_duration_millis: u64, + clock: Box, + assignment_criteria: Arc, } /// Contains recently finalized @@ -161,7 +176,7 @@ impl ApprovalEntry { Self { validator_index: assignment.validator, assignment, - approvals: HashMap::with_capacity(candidates.len()), + approvals: HashMap::new(), assignment_claimed_candidates: candidates, routing_info, } @@ -354,6 +369,9 @@ pub struct State { /// Aggregated reputation change reputation: ReputationAggregator, + + /// Slot duration in millis + slot_duration_millis: u64, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -488,11 +506,17 @@ struct BlockEntry { knowledge: Knowledge, /// A votes entry for each candidate indexed by [`CandidateIndex`]. candidates: Vec, + /// Information about candidate metadata. + candidates_metadata: Vec<(CandidateHash, CoreIndex, GroupIndex)>, /// The session index of this block. session: SessionIndex, /// Approval entries for whole block. These also contain all approvals in the case of multiple /// candidates being claimed by assignments. approval_entries: HashMap<(ValidatorIndex, CandidateBitfield), ApprovalEntry>, + /// The block vrf story. + vrf_story: RelayVRFStory, + /// The block slot. + slot: Slot, } impl BlockEntry { @@ -646,6 +670,41 @@ enum MessageSource { Local, } +// Encountered error while validating an assignment. +#[derive(Debug)] +enum InvalidAssignmentError { + // The vrf check for the assignment failed. + #[allow(dead_code)] + CryptoCheckFailed(InvalidAssignment), + // The assignment did not claim any valid candidate. + NoClaimedCandidates, + // Claimed invalid candidate. + #[allow(dead_code)] + ClaimedInvalidCandidateIndex { + claimed_index: usize, + max_index: usize, + }, + // The assignment claimes more candidates than the maximum allowed. + OversizedClaimedBitfield, + // `SessionInfo` was not found for the block hash in the assignment. + #[allow(dead_code)] + SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error), +} + +// Encountered error while validating an approval. +#[derive(Debug)] +enum InvalidVoteError { + // The candidate index was out of bounds. + CandidateIndexOutOfBounds, + // The validator index was out of bounds. + ValidatorIndexOutOfBounds, + // The signature of the vote was invalid. + InvalidSignature, + // `SessionInfo` was not found for the block hash in the approval. + #[allow(dead_code)] + SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error), +} + impl MessageSource { fn peer_id(&self) -> Option { match self { @@ -662,16 +721,26 @@ enum PendingMessage { #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl State { + /// Build State with specified slot duration. + pub fn with_config(slot_duration_millis: u64) -> Self { + Self { slot_duration_millis, ..Default::default() } + } + async fn handle_network_msg< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, event: NetworkBridgeEvent, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { match event { NetworkBridgeEvent::PeerConnected(peer_id, role, version, authority_ids) => { @@ -727,10 +796,14 @@ impl State { self.process_incoming_peer_message( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, message, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -776,16 +849,28 @@ impl State { async fn handle_new_blocks< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, metas: Vec, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { let mut new_hashes = HashSet::new(); - for meta in &metas { + + gum::debug!( + target: LOG_TARGET, + "Got new blocks {:?}", + metas.iter().map(|m| (m.hash, m.number)).collect::>(), + ); + + for meta in metas { let mut span = self .spans .get(&meta.hash) @@ -809,6 +894,9 @@ impl State { candidates, session: meta.session, approval_entries: HashMap::new(), + candidates_metadata: meta.candidates, + vrf_story: meta.vrf_story, + slot: meta.slot, }); self.topologies.inc_session_refs(meta.session); @@ -823,12 +911,6 @@ impl State { } } - gum::debug!( - target: LOG_TARGET, - "Got new blocks {:?}", - metas.iter().map(|m| (m.hash, m.number)).collect::>(), - ); - { for (peer_id, PeerEntry { view, version }) in self.peer_views.iter() { let intersection = view.iter().filter(|h| new_hashes.contains(h)); @@ -883,11 +965,15 @@ impl State { self.import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), assignment, claimed_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -895,9 +981,11 @@ impl State { self.import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), approval_vote, + session_info_provider, ) .await; }, @@ -943,17 +1031,22 @@ impl State { .await; } - async fn process_incoming_assignments( + async fn process_incoming_assignments( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { for (assignment, claimed_indices) in assignments { @@ -978,11 +1071,15 @@ impl State { self.import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), assignment, claimed_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; } @@ -992,13 +1089,16 @@ impl State { async fn process_incoming_approvals< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, approvals: Vec, + session_info_provider: &mut RuntimeInfo, ) { gum::trace!( target: LOG_TARGET, @@ -1028,18 +1128,21 @@ impl State { self.import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), approval_vote, + session_info_provider, ) .await; } } - async fn process_incoming_peer_message( + async fn process_incoming_peer_message( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, msg: Versioned< @@ -1048,9 +1151,13 @@ impl State { protocol_v3::ApprovalDistributionMessage, >, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { match msg { @@ -1067,10 +1174,14 @@ impl State { self.process_incoming_assignments( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_assignments, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -1089,10 +1200,14 @@ impl State { self.process_incoming_assignments( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_assignments, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -1102,9 +1217,11 @@ impl State { self.process_incoming_approvals( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_approvals, + session_info_provider, ) .await; }, @@ -1115,9 +1232,11 @@ impl State { self.process_incoming_approvals( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_approvals, + session_info_provider, ) .await; }, @@ -1218,18 +1337,23 @@ impl State { self.enable_aggression(network_sender, Resend::No, metrics).await; } - async fn import_and_circulate_assignment( + async fn import_and_circulate_assignment( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, source: MessageSource, assignment: IndirectAssignmentCertV2, claimed_candidate_indices: CandidateBitfield, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { let _span = self @@ -1355,35 +1479,47 @@ impl State { return } - let (tx, rx) = oneshot::channel(); + let result = Self::check_assignment_valid( + assignment_criteria, + &entry, + &assignment, + &claimed_candidate_indices, + session_info_provider, + runtime_api_sender, + ) + .await; - approval_voting_sender - .send_message(ApprovalVotingMessage::CheckAndImportAssignment( - assignment.clone(), - claimed_candidate_indices.clone(), - tx, - )) - .await; + match result { + Ok(checked_assignment) => { + let current_tranche = clock.tranche_now(self.slot_duration_millis, entry.slot); + let too_far_in_future = + current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; - let timer = metrics.time_awaiting_approval_voting(); - let result = match rx.await { - Ok(result) => result, - Err(_) => { - gum::debug!(target: LOG_TARGET, "The approval voting subsystem is down"); - return - }, - }; - drop(timer); + if checked_assignment.tranche() >= too_far_in_future { + gum::debug!( + target: LOG_TARGET, + hash = ?block_hash, + ?peer_id, + "Got an assignment too far in the future", + ); + modify_reputation( + &mut self.reputation, + network_sender, + peer_id, + COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, + ) + .await; + metrics.on_assignment_far(); - gum::trace!( - target: LOG_TARGET, - ?source, - ?message_subject, - ?result, - "Checked assignment", - ); - match result { - AssignmentCheckResult::Accepted => { + return + } + + approval_voting_sender + .send_message(ApprovalVotingMessage::ImportAssignment( + checked_assignment, + None, + )) + .await; modify_reputation( &mut self.reputation, network_sender, @@ -1396,47 +1532,12 @@ impl State { peer_knowledge.received.insert(message_subject.clone(), message_kind); } }, - AssignmentCheckResult::AcceptedDuplicate => { - // "duplicate" assignments aren't necessarily equal. - // There is more than one way each validator can be assigned to each core. - // cf. https://github.com/paritytech/polkadot/pull/2160#discussion_r557628699 - if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { - peer_knowledge.received.insert(message_subject.clone(), message_kind); - } - gum::debug!( - target: LOG_TARGET, - hash = ?block_hash, - ?peer_id, - "Got an `AcceptedDuplicate` assignment", - ); - metrics.on_assignment_duplicatevoting(); - - return - }, - AssignmentCheckResult::TooFarInFuture => { - gum::debug!( - target: LOG_TARGET, - hash = ?block_hash, - ?peer_id, - "Got an assignment too far in the future", - ); - modify_reputation( - &mut self.reputation, - network_sender, - peer_id, - COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, - ) - .await; - metrics.on_assignment_far(); - - return - }, - AssignmentCheckResult::Bad(error) => { + Err(error) => { gum::info!( target: LOG_TARGET, hash = ?block_hash, ?peer_id, - %error, + ?error, "Got a bad assignment from peer", ); modify_reputation( @@ -1577,6 +1678,64 @@ impl State { } } + async fn check_assignment_valid>( + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + entry: &BlockEntry, + assignment: &IndirectAssignmentCertV2, + claimed_candidate_indices: &CandidateBitfield, + runtime_info: &mut RuntimeInfo, + runtime_api_sender: &mut RA, + ) -> Result { + let ExtendedSessionInfo { ref session_info, .. } = runtime_info + .get_session_info_by_index(runtime_api_sender, assignment.block_hash, entry.session) + .await + .map_err(|err| InvalidAssignmentError::SessionInfoNotFound(err))?; + + if claimed_candidate_indices.len() > session_info.n_cores as usize { + return Err(InvalidAssignmentError::OversizedClaimedBitfield) + } + + let claimed_cores: Vec = claimed_candidate_indices + .iter_ones() + .map(|candidate_index| { + entry.candidates_metadata.get(candidate_index).map(|(_, core, _)| *core).ok_or( + InvalidAssignmentError::ClaimedInvalidCandidateIndex { + claimed_index: candidate_index, + max_index: entry.candidates_metadata.len(), + }, + ) + }) + .collect::, InvalidAssignmentError>>()?; + + let Ok(claimed_cores) = claimed_cores.try_into() else { + return Err(InvalidAssignmentError::NoClaimedCandidates) + }; + + let backing_groups = claimed_candidate_indices + .iter_ones() + .flat_map(|candidate_index| { + entry.candidates_metadata.get(candidate_index).map(|(_, _, group)| *group) + }) + .collect::>(); + + assignment_criteria + .check_assignment_cert( + claimed_cores, + assignment.validator, + &polkadot_node_primitives::approval::criteria::Config::from(session_info), + entry.vrf_story.clone(), + &assignment.cert, + backing_groups, + ) + .map_err(|err| InvalidAssignmentError::CryptoCheckFailed(err)) + .map(|tranche| { + CheckedIndirectAssignment::from_checked( + assignment.clone(), + claimed_candidate_indices.clone(), + tranche, + ) + }) + } // Checks if an approval can be processed. // Returns true if we can continue with processing the approval and false otherwise. async fn check_approval_can_be_processed< @@ -1666,13 +1825,16 @@ impl State { async fn import_and_circulate_approval< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, source: MessageSource, vote: IndirectSignedApprovalVoteV2, + session_info_provider: &mut RuntimeInfo, ) { let _span = self .spans @@ -1740,31 +1902,16 @@ impl State { return } - let (tx, rx) = oneshot::channel(); - - approval_voting_sender - .send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) - .await; - - let timer = metrics.time_awaiting_approval_voting(); - let result = match rx.await { - Ok(result) => result, - Err(_) => { - gum::debug!(target: LOG_TARGET, "The approval voting subsystem is down"); - return - }, - }; - drop(timer); + let result = + Self::check_vote_valid(&vote, &entry, session_info_provider, runtime_api_sender) + .await; - gum::trace!( - target: LOG_TARGET, - ?peer_id, - ?result, - ?vote, - "Checked approval", - ); match result { - ApprovalCheckResult::Accepted => { + Ok(vote) => { + approval_voting_sender + .send_message(ApprovalVotingMessage::ImportApproval(vote, None)) + .await; + modify_reputation( &mut self.reputation, network_sender, @@ -1782,7 +1929,7 @@ impl State { .insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1); } }, - ApprovalCheckResult::Bad(error) => { + Err(err) => { modify_reputation( &mut self.reputation, network_sender, @@ -1790,10 +1937,11 @@ impl State { COST_INVALID_MESSAGE, ) .await; + gum::info!( target: LOG_TARGET, ?peer_id, - %error, + ?err, "Got a bad approval from peer", ); metrics.on_approval_bad(); @@ -1891,6 +2039,50 @@ impl State { } } + // Checks if the approval vote is valid. + async fn check_vote_valid>( + vote: &IndirectSignedApprovalVoteV2, + entry: &BlockEntry, + runtime_info: &mut RuntimeInfo, + runtime_api_sender: &mut RA, + ) -> Result { + if vote.candidate_indices.len() > entry.candidates_metadata.len() { + return Err(InvalidVoteError::CandidateIndexOutOfBounds) + } + + let candidate_hashes = vote + .candidate_indices + .iter_ones() + .flat_map(|candidate_index| { + entry + .candidates_metadata + .get(candidate_index) + .map(|(candidate_hash, _, _)| *candidate_hash) + }) + .collect::>(); + + let ExtendedSessionInfo { ref session_info, .. } = runtime_info + .get_session_info_by_index(runtime_api_sender, vote.block_hash, entry.session) + .await + .map_err(|err| InvalidVoteError::SessionInfoNotFound(err))?; + + let pubkey = session_info + .validators + .get(vote.validator) + .ok_or(InvalidVoteError::ValidatorIndexOutOfBounds)?; + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates( + candidate_hashes.clone(), + )) + .check_signature( + &pubkey, + *candidate_hashes.first().unwrap(), + entry.session, + &vote.signature, + ) + .map_err(|_| InvalidVoteError::InvalidSignature) + .map(|_| CheckedIndirectSignedApprovalVote::from_checked(vote.clone())) + } + /// Retrieve approval signatures from state for the given relay block/indices: fn get_approval_signatures( &mut self, @@ -2468,16 +2660,48 @@ async fn modify_reputation( #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl ApprovalDistribution { /// Create a new instance of the [`ApprovalDistribution`] subsystem. - pub fn new(metrics: Metrics) -> Self { - Self { metrics } + pub fn new( + metrics: Metrics, + slot_duration_millis: u64, + assignment_criteria: Arc, + ) -> Self { + Self::new_with_clock( + metrics, + slot_duration_millis, + Box::new(SystemClock), + assignment_criteria, + ) + } + + /// Create a new instance of the [`ApprovalDistribution`] subsystem, with a custom clock. + pub fn new_with_clock( + metrics: Metrics, + slot_duration_millis: u64, + clock: Box, + assignment_criteria: Arc, + ) -> Self { + Self { metrics, slot_duration_millis, clock, assignment_criteria } } async fn run(self, ctx: Context) { - let mut state = State::default(); + let mut state = + State { slot_duration_millis: self.slot_duration_millis, ..Default::default() }; // According to the docs of `rand`, this is a ChaCha12 RNG in practice // and will always be chosen for strong performance and security properties. let mut rng = rand::rngs::StdRng::from_entropy(); - self.run_inner(ctx, &mut state, REPUTATION_CHANGE_INTERVAL, &mut rng).await + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + self.run_inner( + ctx, + &mut state, + REPUTATION_CHANGE_INTERVAL, + &mut rng, + &mut session_info_provider, + ) + .await } /// Used for testing. @@ -2487,11 +2711,14 @@ impl ApprovalDistribution { state: &mut State, reputation_interval: Duration, rng: &mut (impl CryptoRng + Rng), + session_info_provider: &mut RuntimeInfo, ) { let new_reputation_delay = || futures_timer::Delay::new(reputation_interval).fuse(); let mut reputation_delay = new_reputation_delay(); let mut approval_voting_sender = ctx.sender().clone(); let mut network_sender = ctx.sender().clone(); + let mut runtime_api_sender = ctx.sender().clone(); + loop { select! { _ = reputation_delay => { @@ -2507,8 +2734,9 @@ impl ApprovalDistribution { }, }; - - self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, state, rng).await; + if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, &mut runtime_api_sender, state, rng, session_info_provider).await { + return; + } }, } @@ -2516,26 +2744,35 @@ impl ApprovalDistribution { } /// Handles a from orchestra message received by approval distribution subystem. + /// + /// Returns `true` if the subsystem should be stopped. pub async fn handle_from_orchestra< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + 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)) => { @@ -2555,30 +2792,56 @@ impl ApprovalDistribution { gum::trace!(target: LOG_TARGET, number = %number, "finalized signal"); state.handle_block_finalized(network_sender, &self.metrics, number).await; }, - FromOrchestra::Signal(OverseerSignal::Conclude) => return, + FromOrchestra::Signal(OverseerSignal::Conclude) => return true, } + false } async fn handle_incoming< 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) => { @@ -2602,11 +2865,15 @@ impl ApprovalDistribution { .import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, &metrics, MessageSource::Local, cert, candidate_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -2622,9 +2889,11 @@ impl ApprovalDistribution { .import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Local, vote, + session_info_provider, ) .await; }, diff --git a/polkadot/node/network/approval-distribution/src/metrics.rs b/polkadot/node/network/approval-distribution/src/metrics.rs index 60c7f2f6d3b895c1f406ed8f3dfbbbe5c499eab1..10553c3529661a5d26dce7894f3c7ecdd05861b0 100644 --- a/polkadot/node/network/approval-distribution/src/metrics.rs +++ b/polkadot/node/network/approval-distribution/src/metrics.rs @@ -30,7 +30,6 @@ struct MetricsInner { aggression_l2_messages_total: prometheus::Counter, time_unify_with_peer: prometheus::Histogram, time_import_pending_now_known: prometheus::Histogram, - time_awaiting_approval_voting: prometheus::Histogram, assignments_received_result: prometheus::CounterVec, approvals_received_result: prometheus::CounterVec, } @@ -206,14 +205,6 @@ impl Metrics { } } - pub(crate) fn time_awaiting_approval_voting( - &self, - ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.time_awaiting_approval_voting.start_timer()) - } - pub(crate) fn on_aggression_l1(&self) { if let Some(metrics) = &self.0 { metrics.aggression_l1_messages_total.inc(); @@ -288,13 +279,6 @@ impl MetricsTrait for Metrics { ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, registry, )?, - time_awaiting_approval_voting: prometheus::register( - prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_time_awaiting_approval_voting", - "Time spent awaiting a reply from the Approval Voting Subsystem.", - ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, - registry, - )?, assignments_received_result: prometheus::register( prometheus::CounterVec::new( prometheus::Opts::new( diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 3ea722c51a927defd51bddfdfdd6a55249b91d9c..4ee9320e0e45f105eef2dba13c2aef3bd3a644a7 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -16,7 +16,7 @@ use super::*; use assert_matches::assert_matches; -use futures::{executor, future, Future}; +use futures::{channel::oneshot, executor, future, Future}; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, our_view, @@ -24,6 +24,7 @@ use polkadot_node_network_protocol::{ view, ObservedRole, }; use polkadot_node_primitives::approval::{ + criteria, v1::{ AssignmentCert, AssignmentCertKind, IndirectAssignmentCert, IndirectSignedApprovalVote, VrfPreOutput, VrfProof, VrfSignature, @@ -34,12 +35,17 @@ use polkadot_node_primitives::approval::{ }, }; use polkadot_node_subsystem::messages::{ - network_bridge_event, AllMessages, ApprovalCheckError, ReportPeerMessage, + network_bridge_event, AllMessages, ReportPeerMessage, RuntimeApiRequest, }; use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt as _}; -use polkadot_primitives::{AuthorityDiscoveryId, BlakeTwo256, CoreIndex, HashT}; +use polkadot_primitives::{ + ApprovalVoteMultipleCandidates, AuthorityDiscoveryId, BlakeTwo256, CoreIndex, ExecutorParams, + HashT, NodeFeatures, SessionInfo, ValidatorId, +}; use polkadot_primitives_test_helpers::dummy_signature; use rand::SeedableRng; +use sc_keystore::{Keystore, LocalKeystore}; +use sp_application_crypto::AppCrypto; use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair; use sp_core::crypto::Pair as PairT; use std::time::Duration; @@ -47,6 +53,8 @@ type VirtualOverseer = polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; fn test_harness>( + assignment_criteria: Arc, + clock: Box, mut state: State, test_fn: impl FnOnce(VirtualOverseer) -> T, ) -> State { @@ -56,12 +64,32 @@ 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 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 (tx, rx) = oneshot::channel(); + let subsystem = async { + subsystem + .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"); + }; let test_fut = test_fn(virtual_overseer); @@ -76,6 +104,8 @@ fn test_harness>( .timeout(TIMEOUT) .await .expect("Conclude send timeout"); + let _ = + rx.timeout(Duration::from_secs(2)).await.expect("Subsystem did not conclude"); }, subsystem, )); @@ -115,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(|_| { @@ -329,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, @@ -372,6 +461,85 @@ fn state_with_reputation_delay() -> State { State { reputation: ReputationAggregator::new(|_| false), ..Default::default() } } +fn dummy_session_info_valid( + index: SessionIndex, + keystore: &mut LocalKeystore, + num_validators: usize, +) -> SessionInfo { + let keys = (0..num_validators) + .map(|_| { + keystore + .sr25519_generate_new(ValidatorId::ID, Some("//Node")) + .expect("Insert key into keystore") + }) + .collect_vec(); + + SessionInfo { + validators: keys.clone().into_iter().map(|key| key.into()).collect(), + discovery_keys: keys.clone().into_iter().map(|key| key.into()).collect(), + assignment_keys: keys.clone().into_iter().map(|key| key.into()).collect(), + validator_groups: Default::default(), + n_cores: 20, + zeroth_delay_tranche_width: index as _, + relay_vrf_modulo_samples: index as _, + n_delay_tranches: index as _, + no_show_slots: index as _, + needed_approvals: index as _, + active_validator_indices: Vec::new(), + dispute_period: 6, + random_seed: [0u8; 32], + } +} + +fn signature_for( + keystore: &LocalKeystore, + session: &SessionInfo, + candidate_hashes: Vec, + validator_index: ValidatorIndex, +) -> ValidatorSignature { + let payload = ApprovalVoteMultipleCandidates(&candidate_hashes).signing_payload(1); + let sign_key = session.validators.get(validator_index).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + signature.into() +} + +struct MockAssignmentCriteria { + tranche: + Result, +} + +impl AssignmentCriteria for MockAssignmentCriteria { + fn compute_assignments( + &self, + _keystore: &LocalKeystore, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _config: &criteria::Config, + _leaving_cores: Vec<( + CandidateHash, + polkadot_primitives::CoreIndex, + polkadot_primitives::GroupIndex, + )>, + _enable_assignments_v2: bool, + ) -> HashMap { + HashMap::new() + } + + fn check_assignment_cert( + &self, + _claimed_core_bitfield: polkadot_node_primitives::approval::v2::CoreBitfield, + _validator_index: polkadot_primitives::ValidatorIndex, + _config: &criteria::Config, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _assignment: &polkadot_node_primitives::approval::v2::AssignmentCertV2, + _backing_groups: Vec, + ) -> Result { + self.tranche + } +} + /// import an assignment /// connect a new peer /// the new peer sends us the same assignment @@ -385,89 +553,98 @@ fn try_import_the_same_assignment() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), 0u32)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer_a, msg).await; + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &0u32.into()); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_indices, - tx, - )) => { - assert_eq!(claimed_indices, 0u32.into()); - assert_eq!(assignment, cert.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // setup new peer with V2 - setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; + // setup new peer with V2 + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; - // send the same assignment from peer_d - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_d, msg).await; + // send the same assignment from peer_d + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, &peer_d, msg).await; - expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// Just like `try_import_the_same_assignment` but use `VRFModuloCompact` assignments for multiple @@ -482,97 +659,106 @@ fn try_import_the_same_assignment_v2() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cores = vec![1, 2, 3, 4]; - let core_bitfield: CoreBitfield = cores - .iter() - .map(|index| CoreIndex(*index)) - .collect::>() - .try_into() - .unwrap(); - - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); - let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; - - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer_v3(overseer, &peer_a, msg).await; - - expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; - - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_indices, - tx, - )) => { - assert_eq!(claimed_indices, cores.try_into().unwrap()); - assert_eq!(assignment, cert.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 5], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cores = vec![1, 2, 3, 4]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; + + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &cores.try_into().unwrap()); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // setup new peer - setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; + // setup new peer + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; - // send the same assignment from peer_d - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer_v3(overseer, &peer_d, msg).await; + // send the same assignment from peer_d + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// import an assignment @@ -584,55 +770,65 @@ fn delay_reputation_change() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_with_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Setup peers - setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), 0u32)]; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_with_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Setup peers + setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer, msg).await; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidates, - tx, - )) => { - assert_eq!(assignment.cert, cert.cert.into()); - assert_eq!(claimed_candidates, vec![0u32].try_into().unwrap()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_changes( - overseer, - &peer, - vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST], - ) - .await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment().cert, cert.cert.into()); + assert_eq!(assignment.candidate_indices(), &vec![0u32].try_into().unwrap()); + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_changes( + overseer, + &peer, + vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST], + ) + .await; + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + virtual_overseer + }, + ); } /// @@ -647,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) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + + // new block `hash_b` with 20 candidates + let candidates_count = 20; + let meta = BlockApprovalMeta { + hash: hash_b, + parent_hash, + number: 2, + candidates: vec![Default::default(); candidates_count], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send 20 assignments related to `hash_b` + // to populate our knowledge + let assignments: Vec<_> = (0..candidates_count) + .map(|candidate_index| { + let validator_index = ValidatorIndex(candidate_index as u32); + let cert = fake_assignment_cert(hash_b, validator_index); + (cert, candidate_index as u32) + }) + .collect(); - // 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. @@ -733,86 +940,94 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { let peers = make_peers_and_authority_ids(8); let peer_a = peers.first().unwrap().0; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // new block `hash` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // update peer view to include the hash - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - view![hash], - )), - ) - .await; + // update peer view to include the hash + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange(*peer, view![hash]), + ), + ) + .await; - // we should send them the assignment - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); + // we should send them the assignment + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); - // but if someone else is sending it the same assignment - // the peer could send us it as well - let assignments = vec![(cert, candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, peer, msg.clone()).await; + // but if someone else is sending it the same assignment + // the peer could send us it as well + let assignments = vec![(cert, candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, peer, msg.clone()).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "we should not punish the peer"); + assert!( + overseer.recv().timeout(TIMEOUT).await.is_none(), + "we should not punish the peer" + ); - // send the assignments again - send_message_from_peer(overseer, peer, msg).await; + // send the assignments again + send_message_from_peer(overseer, peer, msg).await; - // now we should - expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; - virtual_overseer - }); + // now we should + expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; + virtual_overseer + }, + ); } #[test] @@ -824,116 +1039,134 @@ fn import_approval_happy_path_v1_v2_peers() { let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V1 and V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V1 and V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // 1 peer is v1 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - - // 1 peer is v2 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg: protocol_v3::ApprovalDistributionMessage = - protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(approvals.len(), 1); - } - ); - virtual_overseer - }); + // 1 peer is v1 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // send the an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash], + validator_index, + ), + }; + let msg: protocol_v3::ApprovalDistributionMessage = + protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(approvals.len(), 1); + } + ); + virtual_overseer + }, + ); } // Test a v2 approval that signs multiple candidate is correctly processed. @@ -946,103 +1179,123 @@ fn import_approval_happy_path_v2() { let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let candidate_bitfields = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_indices.clone(), + ), + ) + .await; - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - let candidate_bitfields = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_indices.clone(), - ), - ) - .await; + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // 1 peer is v2 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(approvals.len(), 1); - } - ); - virtual_overseer - }); + // send the an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, + ), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(approvals.len(), 1); + } + ); + virtual_overseer + }, + ); } // Tests that votes that cover multiple assignments candidates are correctly processed on importing @@ -1056,187 +1309,203 @@ fn multiple_assignments_covered_with_one_approval_vote() { let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 5); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(2); // peer_c is the originator + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(2); // peer_c is the originator - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); - let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); + // send the candidate 0 assignment from peer_b + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (0 as CandidateIndex).into(), + )]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + provide_session(overseer, session.clone()).await; - // send the candidate 0 assignment from peer_b - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (0 as CandidateIndex).into(), - )]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_a)); - assert!(peers.contains(&peer_b)); - assert_eq!(assignments.len(), 1); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_a)); + assert!(peers.contains(&peer_b)); + assert_eq!(assignments.len(), 1); + } + ); - let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - // send the candidate 1 assignment from peer_c - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (1 as CandidateIndex).into(), - )]); - - send_message_from_peer_v3(overseer, &peer_c, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_c, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_b)); - assert!(peers.contains(&peer_a)); - assert_eq!(assignments.len(), 1); - } - ); - - // send an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_b)); - assert!(peers.contains(&peer_a)); - assert_eq!(approvals.len(), 1); - } - ); - for candidate_index in 0..1 { - let (tx_distribution, rx_distribution) = oneshot::channel(); - let mut candidates_requesting_signatures = HashSet::new(); - candidates_requesting_signatures.insert((hash, candidate_index)); - overseer_send( - overseer, - ApprovalDistributionMessage::GetApprovalSignatures( - candidates_requesting_signatures, - tx_distribution, + // send the candidate 1 assignment from peer_c + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (1 as CandidateIndex).into(), + )]); + + send_message_from_peer_v3(overseer, &peer_c, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_c, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_b)); + assert!(peers.contains(&peer_a)); + assert_eq!(assignments.len(), 1); + } + ); + + // send an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, ), - ) - .await; - let signatures = rx_distribution.await.unwrap(); + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - assert_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::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_b)); + assert!(peers.contains(&peer_a)); + assert_eq!(approvals.len(), 1); + } + ); + for candidate_index in 0..1 { + let (tx_distribution, rx_distribution) = oneshot::channel(); + let mut candidates_requesting_signatures = HashSet::new(); + candidates_requesting_signatures.insert((hash, candidate_index)); + overseer_send( + overseer, + ApprovalDistributionMessage::GetApprovalSignatures( + candidates_requesting_signatures, + tx_distribution, + ), + ) + .await; + let signatures = rx_distribution.await.unwrap(); + + assert_eq!(signatures.len(), 1); + for (signing_validator, signature) in signatures { + assert_eq!(validator_index, signing_validator); + assert_eq!(signature.0, hash); + assert_eq!(signature.2, approval.signature); + assert_eq!(signature.1, vec![0, 1]); + } } - } - virtual_overseer - }); + virtual_overseer + }, + ); } // Tests that votes that cover multiple assignments candidates are correctly processed when unify @@ -1250,182 +1519,196 @@ fn unify_with_peer_multiple_assignments_covered_with_one_approval_vote() { let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 5); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(2); // peer_c is the originator + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(2); // peer_c is the originator - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); - let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); + // send the candidate 0 assignment from peer_b + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (0 as CandidateIndex).into(), + )]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + provide_session(overseer, session.clone()).await; - // send the candidate 0 assignment from peer_b - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (0 as CandidateIndex).into(), - )]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - 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_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); - } - ); + // 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(), + )]); - virtual_overseer - }); + send_message_from_peer_v3(overseer, &peer_d, 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; + + // 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; + + 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; + + // 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); + } + ); + + virtual_overseer + }, + ); } #[test] @@ -1434,79 +1717,87 @@ fn import_approval_bad() { let peer_b = PeerId::random(); let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let diff_candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let _ = 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; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); - expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; + // 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; - // 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; + expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; - 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(); - } - ); + // 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; - expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await; - virtual_overseer - }); + 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 @@ -1517,38 +1808,46 @@ fn update_our_view() { let hash_b = Hash::repeat_byte(0xBB); let hash_c = Hash::repeat_byte(0xCC); - let state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&1).is_some()); assert!(state.blocks_by_number.get(&2).is_some()); @@ -1557,12 +1856,17 @@ fn update_our_view() { assert!(state.blocks.get(&hash_b).is_some()); assert!(state.blocks.get(&hash_c).is_some()); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // finalize a block - overseer_signal_block_finalized(overseer, 2).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // finalize a block + overseer_signal_block_finalized(overseer, 2).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&1).is_none()); assert!(state.blocks_by_number.get(&2).is_none()); @@ -1571,12 +1875,17 @@ fn update_our_view() { assert!(state.blocks.get(&hash_b).is_none()); assert!(state.blocks.get(&hash_c).is_some()); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // finalize a very high block - overseer_signal_block_finalized(overseer, 4_000_000_000).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // finalize a very high block + overseer_signal_block_finalized(overseer, 4_000_000_000).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&3).is_none()); assert!(state.blocks.get(&hash_c).is_none()); @@ -1594,81 +1903,89 @@ fn update_peer_view() { let peer_a = peers.first().unwrap().0; let peer = &peer_a; - let state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); + let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); - let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); - let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), - ) - .await; + // connect a peer + setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; - // connect a peer - setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; - - // we should send relevant assignments to the peer - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - virtual_overseer - }); + // we should send relevant assignments to the peer + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(0)); assert_eq!( @@ -1685,42 +2002,49 @@ fn update_peer_view() { 1, ); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // update peer's view - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::new(vec![hash_b, hash_c, hash_d], 2), - )), - ) - .await; + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // update peer's view + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange( + *peer, + View::new(vec![hash_b, hash_c, hash_d], 2), + ), + ), + ) + .await; - let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0)); + let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0)); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), + ) + .await; - // we should send relevant assignments to the peer - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(assignments[0].0, cert_c); - } - ); - virtual_overseer - }); + // we should send relevant assignments to the peer + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(assignments[0].0, cert_c); + } + ); + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(2)); assert_eq!( @@ -1738,19 +2062,26 @@ fn update_peer_view() { ); let finalized_number = 4_000_000_000; - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // update peer's view - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::with_finalized(finalized_number), - )), - ) - .await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // update peer's view + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange( + *peer, + View::with_finalized(finalized_number), + ), + ), + ) + .await; + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(finalized_number)); assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none()); @@ -1773,164 +2104,176 @@ fn update_peer_authority_id() { // Y neighbour, we simulate that PeerId is not known in the beginning. let neighbour_y = peers.get(neighbour_y_index).unwrap().0; - let _state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let _state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .enumerate() + .map(|(index, (peer_id, authority))| { + (if index == 0 { None } else { Some(*peer_id) }, authority.clone()) + }) + .collect_vec(); + + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[neighbour_x_index], + &[neighbour_y_index], + local_index, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(local_index as u32)); + let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(local_index as u32)); - let peers_with_optional_peer_id = peers - .iter() - .enumerate() - .map(|(index, (peer_id, authority))| { - (if index == 0 { None } else { Some(*peer_id) }, authority.clone()) - }) - .collect_vec(); - - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[neighbour_x_index], - &[neighbour_y_index], - local_index, - ), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(local_index as u32)); - let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(local_index as u32)); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), - ) - .await; + // connect a peer + setup_peer_with_view(overseer, &neighbour_x, view![hash_a], ValidationVersion::V1) + .await; + setup_peer_with_view(overseer, &neighbour_y, view![hash_a], ValidationVersion::V1) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), - ) - .await; + setup_peer_with_view(overseer, &neighbour_x, view![hash_b], ValidationVersion::V1) + .await; + setup_peer_with_view(overseer, &neighbour_y, view![hash_b], ValidationVersion::V1) + .await; - // connect a peer - setup_peer_with_view(overseer, &neighbour_x, view![hash_a], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &neighbour_y, view![hash_a], ValidationVersion::V1).await; - - setup_peer_with_view(overseer, &neighbour_x, view![hash_b], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &neighbour_y, view![hash_b], ValidationVersion::V1).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(peers.get(0), Some(&neighbour_y)); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(peers.get(0), Some(&neighbour_y)); - } - ); - - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_x_index].0, - [peers[neighbour_x_index].1.clone()].into_iter().collect(), + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(peers.get(0), Some(&neighbour_y)); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(peers.get(0), Some(&neighbour_y)); + } + ); + + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_x_index].0, + [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; + ) + .await; - // we should send relevant assignments to the peer, after we found it's peer id. - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - gum::info!(target: LOG_TARGET, ?peers, ?assignments); - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 2); - assert_eq!(assignments.get(0).unwrap().0.block_hash, hash_a); - assert_eq!(assignments.get(1).unwrap().0.block_hash, hash_b); - assert_eq!(peers.get(0), Some(&neighbour_x)); - } - ); - - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_y_index].0, - [peers[neighbour_y_index].1.clone()].into_iter().collect(), + // we should send relevant assignments to the peer, after we found it's peer id. + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + gum::info!(target: LOG_TARGET, ?peers, ?assignments); + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 2); + assert_eq!(assignments.get(0).unwrap().0.block_hash, hash_a); + assert_eq!(assignments.get(1).unwrap().0.block_hash, hash_b); + assert_eq!(peers.get(0), Some(&neighbour_x)); + } + ); + + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_y_index].0, + [peers[neighbour_y_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_x_index].0, - [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ) + .await; + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_x_index].0, + [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; - assert!( - overseer.recv().timeout(TIMEOUT).await.is_none(), - "no message should be sent peers are already known" - ); + ) + .await; + assert!( + overseer.recv().timeout(TIMEOUT).await.is_none(), + "no message should be sent peers are already known" + ); - virtual_overseer - }); + virtual_overseer + }, + ); } /// E.g. if someone copies the keys... @@ -1939,89 +2282,105 @@ fn import_remotely_then_locally() { let peer_a = PeerId::random(); let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); let peer = &peer_a; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup the peer - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import the assignment remotely first - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg).await; - - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - i, - tx, - )) => { - assert_eq!(assignment, cert.clone().into()); - assert_eq!(i, candidate_index.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup the peer + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + let mut keystore = LocalKeystore::in_memory(); + + let session = dummy_session_info_valid(1, &mut keystore, 1); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let payload = ApprovalVoteMultipleCandidates(&vec![candidate_hash]).signing_payload(1); + let sign_key = session.validators.get(ValidatorIndex(0)).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import the assignment remotely first + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg).await; + provide_session(overseer, session.clone()).await; + + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &cert.clone().into()); + assert_eq!(assignment.candidate_indices(), &candidate_index.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - // import the same assignment locally - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import the same assignment locally + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - // send the approval remotely - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, peer, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + // send the approval remotely + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature.into(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, peer, msg).await; - // import the same approval locally - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // import the same approval locally + overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)) + .await; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } #[test] @@ -2032,94 +2391,100 @@ fn sends_assignments_even_when_state_is_approved() { let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + + // 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(), - }; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // connect the peer. + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - // 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); - } - ); + 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!(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::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` @@ -2132,112 +2497,118 @@ 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; - - // 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()); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 4], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // Assumes candidate index == core index. - let approvals = cores - .iter() - .map(|core| IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: (*core).into(), - validator: validator_index, - signature: dummy_signature(), - }) - .collect::>(); - - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_bitfield.clone(), - ), - ) - .await; + 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::>(); - for approval in &approvals { overseer_send( overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_bitfield.clone(), + ), ) .await; - } - // connect the peer. - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V3).await; - - let assignments = vec![(cert.clone(), candidate_bitfield.clone())]; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_assignments, assignments); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a - // hashmap as well. - let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); - let approvals = approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); - - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_approvals, approvals); + for approval in &approvals { + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ) + .await; } - ); - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // 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 + }, + ); } /// @@ -2249,276 +2620,466 @@ fn sends_assignments_even_when_state_is_approved_v2() { #[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 peer_a = PeerId::random(); + let hash_b = Hash::repeat_byte(0xBB); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + 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 _ = 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, - }; + let peers = make_peers_and_authority_ids(100); - // This will send a peer view that is ahead of our view - setup_peer_with_view(overseer, peer, view![hash_b], ValidationVersion::V1).await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Send our view update to include a new head - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::OurViewChange( - our_view![hash_b], - )), - ) - .await; + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).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 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_index, + validator: validator_index, + signature: dummy_signature(), + }; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg.clone()).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - // This will handle pending messages being processed - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - for i in 0..candidates_count { - // Previously, this has caused out-of-view assignments/approvals - //expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + let mut assignment_sent_peers = assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_indices.len() + 4); + for &i in &expected_indices { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + sent_peers + } + ); assert_matches!( overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidate_index, - tx, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + mut sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) )) => { - assert_eq!(assignment, assignments[i].0.clone().into()); - assert_eq!(claimed_candidate_index, assignments[i].1.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); + // Random sampling is reused from the assignment. + sent_peers.sort(); + assignment_sent_peers.sort(); + assert_eq!(sent_peers, assignment_sent_peers); + assert_eq!(sent_approvals, approvals); } ); - // Since we have a valid statement pending, this should always occur - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - } - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// Tests that local messages propagate to both dimensions. +// Tests that messages propagate to the unshared dimension. #[test] -fn propagates_locally_generated_assignment_to_both_dimensions() { +fn propagates_assignments_along_unshared_dimension() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30, 40, 60, 70, 80], - &[50, 51, 52, 53, 54, 55, 56, 57], - 1, - ), - ) - .await; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); - let expected_indices = [ - // Both dimensions in the gossip topology - 0, 10, 20, 30, 40, 60, 70, 80, 50, 51, 52, 53, 54, 55, 56, 57, - ]; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // Test messages from X direction go to Y peers + { + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; + + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let expected_y = [50, 51, 52, 53]; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_y.len() + 4); + for &i in &expected_y { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } + ); + }; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // Test messages from X direction go to Y peers + { + let validator_index = ValidatorIndex(50); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let mut assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_indices.len() + 4); - for &i in &expected_indices { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - mut sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - sent_peers.sort(); - assignment_sent_peers.sort(); - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + + let expected_x = [0, 10, 20, 30]; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_x.len() + 4); + for &i in &expected_x { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } + ); + }; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// Tests that messages propagate to the unshared dimension. +// tests that messages are propagated to necessary peers after they connect #[test] -fn propagates_assignments_along_unshared_dimension() { +fn propagates_to_required_after_connect() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let omitted = [0, 10, 50, 51]; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // Connect all peers except omitted. + for (i, (peer, _)) in peers.iter().enumerate() { + if !omitted.contains(&i) { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + } + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let expected_indices = [ + // Both dimensions in the gossip topology, minus omitted. + 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, + ]; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // Test messages from X direction go to Y peers - { let validator_index = ValidatorIndex(0); let candidate_index = 0u32; // import an assignment and approval locally. let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - let expected_y = [50, 51, 52, 53]; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert_matches!( + let mut assignment_sent_peers = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, @@ -2526,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 {}", @@ -2535,1123 +3096,1029 @@ fn propagates_assignments_along_unshared_dimension() { ); } assert_eq!(sent_assignments, assignments); + sent_peers } ); - }; - // Test messages from X direction go to Y peers - { - let validator_index = ValidatorIndex(50); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + mut sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Random sampling is reused from the assignment. + sent_peers.sort(); + assignment_sent_peers.sort(); + assert_eq!(sent_peers, assignment_sent_peers); + assert_eq!(sent_approvals, approvals); + } + ); + + for i in omitted.iter().copied() { + setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + assert_eq!(&sent_peers[0], &peers[i].0); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + assert_eq!(&sent_peers[0], &peers[i].0); + assert_eq!(sent_approvals, approvals); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +// test that new gossip topology triggers send of messages. +#[test] +fn sends_to_more_peers_after_getting_topology() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let peers = make_peers_and_authority_ids(100); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); let candidate_index = 0u32; // import an assignment and approval locally. let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; + + let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; + + let mut expected_indices_assignments = expected_indices.clone(); + let mut expected_indices_approvals = expected_indices.clone(); + + for _ in 0..expected_indices_assignments.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + let pos = expected_indices_assignments.iter() + .position(|i| &peers[*i].0 == &sent_peers[0]) + .unwrap(); + expected_indices_assignments.remove(pos); + } + ); + } - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + for _ in 0..expected_indices_approvals.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_approvals, approvals); - let expected_x = [0, 10, 20, 30]; + let pos = expected_indices_approvals.iter() + .position(|i| &peers[*i].0 == &sent_peers[0]) + .unwrap(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_x.len() + 4); - for &i in &expected_x { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); + expected_indices_approvals.remove(pos); } - assert_eq!(sent_assignments, assignments); - } - ); - }; + ); + } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// tests that messages are propagated to necessary peers after they connect +// test aggression L1 #[test] -fn propagates_to_required_after_connect() { +fn originator_aggression_l1() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let mut state = State::default(); + state.aggression_config.resend_unfinalized_period = None; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - let omitted = [0, 10, 50, 51]; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Connect all peers except omitted. - for (i, (peer, _)) in peers.iter().enumerate() { - if !omitted.contains(&i) { + // Connect all peers except omitted. + for (peer, _) in &peers { setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } - } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30, 40, 60, 70, 80], - &[50, 51, 52, 53, 54, 55, 56, 57], - 1, - ), - ) - .await; - - let expected_indices = [ - // Both dimensions in the gossip topology, minus omitted. - 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, - ]; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let mut assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_indices.len() + 4); - for &i in &expected_indices { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - mut sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - sent_peers.sort(); - assignment_sent_peers.sort(); - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - for i in omitted.iter().copied() { - setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1).await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert_matches!( + let prev_sent_indices = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) )) => { - assert_eq!(sent_peers.len(), 1); - assert_eq!(&sent_peers[0], &peers[i].0); - assert_eq!(sent_assignments, assignments); + sent_peers.into_iter() + .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) + .collect::>() } ); assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + _, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Approvals(_) )) - )) => { - assert_eq!(sent_peers.len(), 1); - assert_eq!(&sent_peers[0], &peers[i].0); - assert_eq!(sent_approvals, approvals); - } + )) => { } ); - } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // Add blocks until aggression L1 is triggered. + { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); + overseer_send(overseer, msg).await; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + parent_hash = hash; + } + } + + let unsent_indices = + (0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::>(); + + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); + + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } + + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_approvals, approvals); + + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// test that new gossip topology triggers send of messages. +// test aggression L1 #[test] -fn sends_to_more_peers_after_getting_topology() { +fn non_originator_aggression_l1() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let mut state = state_without_reputation_delay(); + state.aggression_config.resend_unfinalized_period = None; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let assignments = vec![(cert.clone().into(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - let mut expected_indices_assignments = expected_indices.clone(); - let mut expected_indices_approvals = expected_indices.clone(); + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - for _ in 0..expected_indices_assignments.len() { assert_matches!( overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - let pos = expected_indices_assignments.iter() - .position(|i| &peers[*i].0 == &sent_peers[0]) - .unwrap(); - expected_indices_assignments.remove(pos); + assert_eq!(assignment.tranche(), 0); } ); - } - for _ in 0..expected_indices_approvals.len() { + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + _, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) - )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_approvals, approvals); - - let pos = expected_indices_approvals.iter() - .position(|i| &peers[*i].0 == &sent_peers[0]) - .unwrap(); - - expected_indices_approvals.remove(pos); - } + )) => { } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} -// test aggression L1 -#[test] -fn originator_aggression_l1() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + // Add blocks until aggression L1 is triggered. + { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let peers = make_peers_and_authority_ids(100); + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let mut state = State::default(); - state.aggression_config.resend_unfinalized_period = None; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + parent_hash = hash; + } + } - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + // No-op on non-originator - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; +// test aggression L2 on non-originator +#[test] +fn non_originator_aggression_l2() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let peers = make_peers_and_authority_ids(100); - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let mut state = state_without_reputation_delay(); + state.aggression_config.resend_unfinalized_period = None; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap(); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let prev_sent_indices = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { - sent_peers.into_iter() - .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) - .collect::>() - } - ); + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - _, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(_) - )) - )) => { } - ); - - // Add blocks until aggression L1 is triggered. - { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); - overseer_send(overseer, msg).await; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } - } + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let unsent_indices = - (0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::>(); + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - for _ in 0..unsent_indices.len() { + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; assert_matches!( overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + assert_eq!(assignment.tranche(), 0); } ); - } - for _ in 0..unsent_indices.len() { - assert_matches!( + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + + let prev_sent_indices = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_approvals, approvals); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + sent_peers.into_iter() + .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) + .collect::>() } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} -// test aggression L1 -#[test] -fn non_originator_aggression_l1() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + // Add blocks until aggression L1 is triggered. + let chain_head = { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let peers = make_peers_and_authority_ids(100); + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); + overseer_send(overseer, msg).await; - let mut state = state_without_reputation_delay(); - state.aggression_config.resend_unfinalized_period = None; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + parent_hash = hash; + } - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + parent_hash + }; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // No-op on non-originator - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + // Add blocks until aggression L2 is triggered. + { + let mut parent_hash = chain_head; + for level in 0..aggression_l2_threshold - aggression_l1_threshold { + let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let assignments = vec![(cert.clone().into(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate( + aggression_l1_threshold + level + 1, + ); + overseer_send(overseer, msg).await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); + parent_hash = hash; + } } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + // XY dimension - previously sent. + let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53] + .iter() + .cloned() + .filter(|i| !prev_sent_indices.contains(&i)) + .collect::>(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - _, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { } - ); - - // Add blocks until aggression L1 is triggered. - { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } - } + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); - // No-op on non-originator + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// test aggression L2 on non-originator +// Tests that messages propagate to the unshared dimension. #[test] -fn non_originator_aggression_l2() { +fn resends_messages_periodically() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); let mut state = state_without_reputation_delay(); - state.aggression_config.resend_unfinalized_period = None; + state.aggression_config.l1_threshold = None; + state.aggression_config.l2_threshold = None; + state.aggression_config.resend_unfinalized_period = Some(2); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap(); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + { + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - - let prev_sent_indices = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { - sent_peers.into_iter() - .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) - .collect::>() - } - ); - - // Add blocks until aggression L1 is triggered. - let chain_head = { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); - overseer_send(overseer, msg).await; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - parent_hash - }; + let expected_y = [50, 51, 52, 53]; - // No-op on non-originator - - // Add blocks until aggression L2 is triggered. - { - let mut parent_hash = chain_head; - for level in 0..aggression_l2_threshold - aggression_l1_threshold { - let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate( - aggression_l1_threshold + level + 1, + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_y.len() + 4); + for &i in &expected_y { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } ); - overseer_send(overseer, msg).await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + }; + + let mut number = 1; + for _ in 0..10 { + // Add blocks until resend is done. + { + let mut parent_hash = hash; + for level in 0..2 { + number = number + 1; + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(2); + overseer_send(overseer, msg).await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + parent_hash = hash; + } + } - parent_hash = hash; + let mut expected_y = vec![50, 51, 52, 53]; + + // Expect messages sent only to topology peers, one by one. + for _ in 0..expected_y.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + let expected_pos = expected_y.iter() + .position(|&i| &peers[i].0 == &sent_peers[0]) + .unwrap(); + + expected_y.remove(expected_pos); + assert_eq!(sent_assignments, assignments); + } + ); + } } - } - // XY dimension - previously sent. - let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53] - .iter() - .cloned() - .filter(|i| !prev_sent_indices.contains(&i)) - .collect::>(); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +/// Tests that peers correctly receive versioned messages. +#[test] +fn import_versioned_approval() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let peer_c = peers.get(2).unwrap().0; + + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let state = state_without_reputation_delay(); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // All peers are aware of relay parent. + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V2).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.into(), + candidate_index.into(), + ), + ) + .await; - for _ in 0..unsent_indices.len() { assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) )) )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + assert_eq!(peers, vec![peer_b]); + assert_eq!(assignments.len(), 1); } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -// Tests that messages propagate to the unshared dimension. -#[test] -fn resends_messages_periodically() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); - - let peers = make_peers_and_authority_ids(100); - - let mut state = state_without_reputation_delay(); - state.aggression_config.l1_threshold = None; - state.aggression_config.l2_threshold = None; - state.aggression_config.resend_unfinalized_period = Some(2); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert!(peers.contains(&peer_a)); + assert!(peers.contains(&peer_c)); - { - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + assert_eq!(assignments.len(), 1); + } + ); - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; + // send the an approval from peer_a + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash], + validator_index, + ), + }; + let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_a, msg).await; + provide_session(overseer, session).await; assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, + overseer_recv(overseer).await, + 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) { @@ -3661,11 +4128,26 @@ fn batch_test_round(message_count: usize) { let (mut context, mut virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); - let subsystem = ApprovalDistribution::new(Default::default()); + let subsystem = ApprovalDistribution::new_with_clock( + Default::default(), + Default::default(), + Box::new(SystemClock {}), + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + ); let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345); let mut sender = context.sender().clone(); - let subsystem = - subsystem.run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng); + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + let subsystem = subsystem.run_inner( + context, + &mut state, + REPUTATION_CHANGE_TEST_INTERVAL, + &mut rng, + &mut session_info_provider, + ); let test_fut = async move { let overseer = &mut virtual_overseer; @@ -3811,3 +4293,420 @@ fn const_ensure_size_not_zero() { crate::ensure_size_not_zero(super::MAX_ASSIGNMENT_BATCH_SIZE); crate::ensure_size_not_zero(super::MAX_APPROVAL_BATCH_SIZE); } + +struct DummyClock; +impl Clock for DummyClock { + fn tick_now(&self) -> polkadot_node_primitives::approval::time::Tick { + 0 + } + + fn wait( + &self, + _tick: polkadot_node_primitives::approval::time::Tick, + ) -> std::pin::Pin + Send + 'static>> { + todo!() + } +} + +/// Subsystem rejects assignments too far into the future. +#[test] +fn subsystem_rejects_assignment_in_future() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(89) }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE) + .await; + + virtual_overseer + }, + ); +} + +/// Subsystem rejects bad vrf assignments. +#[test] +fn subsystem_rejects_bad_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { + tranche: Err(InvalidAssignment(criteria::InvalidAssignmentReason::NullAssignment)), + }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await; + + virtual_overseer + }, + ); +} + +/// Subsystem rejects assignments that have invalid claimed candidates. +#[test] +fn subsystem_rejects_wrong_claimed_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + + // Claimed core 1 which does not have a candidate included on it, so the assignment + // should be rejected. + let cores = vec![1]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), cores.try_into().unwrap())]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await; + + virtual_overseer + }, + ); +} + +/// Subsystem accepts tranche0 duplicate assignments, sometimes on validator Compact tranche0 +/// assignment and Delay tranche assignments land on the same candidate. The delay tranche0 can be +/// safely ignored and we don't need to gossip it however, the compact tranche0 assignment should be +/// gossiped, because other candidates are included in it, this test makes sure this invariant is +/// upheld, see https://github.com/paritytech/polkadot/pull/2160#discussion_r557628699, for +/// this edge case. +#[test] +fn subsystem_accepts_tranche0_duplicate_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + let candidate_hash_third = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_fourth = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Box::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![], ValidationVersion::V3).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + (candidate_hash_third, 2.into(), 2.into()), + (candidate_hash_fourth, 3.into(), 3.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + + // 1. Compact assignment with multiple candidates, coming after delay assignment which + // covered just one of the candidate is still imported and gossiped. + let candidate_indices: CandidateBitfield = + vec![1 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_delay(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 2. Delay assignment coming after compact assignment that already covered the + // candidate is not gossiped anymore. + let candidate_indices: CandidateBitfield = + vec![2 as CandidateIndex, 3 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(2), CoreIndex(3)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + let candidate_indices: CandidateBitfield = vec![3].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(3)].try_into().unwrap(); + + let cert = fake_assignment_cert_delay(hash, validator_index, core_bitfield); + + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_DUPLICATE_MESSAGE).await; + + virtual_overseer + }, + ); +} diff --git a/polkadot/node/network/availability-distribution/src/lib.rs b/polkadot/node/network/availability-distribution/src/lib.rs index ec2c01f99b0186f80a42ad635426ace626139ffc..d3185e0af809c3b92df929d265f1962884d3f096 100644 --- a/polkadot/node/network/availability-distribution/src/lib.rs +++ b/polkadot/node/network/availability-distribution/src/lib.rs @@ -25,7 +25,7 @@ use polkadot_node_subsystem::{ jaeger, messages::AvailabilityDistributionMessage, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_primitives::Hash; +use polkadot_primitives::{BlockNumber, Hash}; use std::collections::HashMap; /// Error and [`Result`] type for this subsystem. @@ -104,7 +104,7 @@ impl AvailabilityDistributionSubsystem { /// Start processing work as passed on from the Overseer. async fn run(self, mut ctx: Context) -> std::result::Result<(), FatalError> { let Self { mut runtime, recvs, metrics, req_protocol_names } = self; - let mut spans: HashMap = HashMap::new(); + let mut spans: HashMap = HashMap::new(); let IncomingRequestReceivers { pov_req_receiver, @@ -162,7 +162,7 @@ impl AvailabilityDistributionSubsystem { }; let span = jaeger::PerLeafSpan::new(cloned_leaf.span, "availability-distribution"); - spans.insert(cloned_leaf.hash, span); + spans.insert(cloned_leaf.hash, (cloned_leaf.number, span)); log_error( requester .get_mut() @@ -172,8 +172,8 @@ impl AvailabilityDistributionSubsystem { &mut warn_freq, )?; }, - FromOrchestra::Signal(OverseerSignal::BlockFinalized(hash, _)) => { - spans.remove(&hash); + FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, finalized_number)) => { + spans.retain(|_hash, (block_number, _span)| *block_number > finalized_number); }, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Communication { @@ -189,7 +189,7 @@ impl AvailabilityDistributionSubsystem { } => { let span = spans .get(&relay_parent) - .map(|span| span.child("fetch-pov")) + .map(|(_, span)| span.child("fetch-pov")) .unwrap_or_else(|| jaeger::Span::new(&relay_parent, "fetch-pov")) .with_trace_id(candidate_hash) .with_candidate(candidate_hash) diff --git a/polkadot/node/network/availability-distribution/src/requester/mod.rs b/polkadot/node/network/availability-distribution/src/requester/mod.rs index efbdceb43bddc6c9501fb5ed6dc3c2e42100df7b..0175161af70dfa706e02bad07ca9fabd4bb1109e 100644 --- a/polkadot/node/network/availability-distribution/src/requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/mod.rs @@ -39,7 +39,9 @@ use polkadot_node_subsystem_util::{ availability_chunks::availability_chunk_index, runtime::{get_occupied_cores, RuntimeInfo}, }; -use polkadot_primitives::{CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex}; +use polkadot_primitives::{ + BlockNumber, CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex, +}; use super::{FatalError, Metrics, Result, LOG_TARGET}; @@ -112,14 +114,14 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, update: ActiveLeavesUpdate, - spans: &HashMap, + spans: &HashMap, ) -> Result<()> { gum::trace!(target: LOG_TARGET, ?update, "Update fetching heads"); let ActiveLeavesUpdate { activated, deactivated } = update; if let Some(leaf) = activated { let span = spans .get(&leaf.hash) - .map(|span| span.child("update-fetching-heads")) + .map(|(_, span)| span.child("update-fetching-heads")) .unwrap_or_else(|| jaeger::Span::new(&leaf.hash, "update-fetching-heads")) .with_string_tag("leaf", format!("{:?}", leaf.hash)) .with_stage(jaeger::Stage::AvailabilityDistribution); diff --git a/polkadot/node/network/availability-distribution/src/requester/tests.rs b/polkadot/node/network/availability-distribution/src/requester/tests.rs index 09567a8f87d322f4befca53c14712b72b9b0e0fc..decb3156004ef6dc52332ef6a4998a3e33ed8ed0 100644 --- a/polkadot/node/network/availability-distribution/src/requester/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/tests.rs @@ -208,7 +208,7 @@ fn check_ancestry_lookup_in_same_session() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); + let spans: HashMap = HashMap::new(); let block_number = 1; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), @@ -281,7 +281,7 @@ fn check_ancestry_lookup_in_different_sessions() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); + let spans: HashMap = HashMap::new(); let block_number = 3; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), diff --git a/polkadot/node/network/bridge/src/rx/mod.rs b/polkadot/node/network/bridge/src/rx/mod.rs index 56965ce6ba404c2e6fc2b189ce75ac9122cbf67c..7745c42f78a173b2c835f4b575171bd25146341e 100644 --- a/polkadot/node/network/bridge/src/rx/mod.rs +++ b/polkadot/node/network/bridge/src/rx/mod.rs @@ -962,6 +962,21 @@ fn update_our_view( ) }; + let our_view = OurView::new( + live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| (a.hash, a.span)), + finalized_number, + ); + + dispatch_validation_event_to_all_unbounded( + NetworkBridgeEvent::OurViewChange(our_view.clone()), + ctx.sender(), + ); + + dispatch_collation_event_to_all_unbounded( + NetworkBridgeEvent::OurViewChange(our_view), + ctx.sender(), + ); + let v1_validation_peers = filter_by_peer_version(&validation_peers, ValidationVersion::V1.into()); let v1_collation_peers = filter_by_peer_version(&collation_peers, CollationVersion::V1.into()); @@ -1007,21 +1022,6 @@ fn update_our_view( metrics, notification_sinks, ); - - let our_view = OurView::new( - live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| (a.hash, a.span)), - finalized_number, - ); - - dispatch_validation_event_to_all_unbounded( - NetworkBridgeEvent::OurViewChange(our_view.clone()), - ctx.sender(), - ); - - dispatch_collation_event_to_all_unbounded( - NetworkBridgeEvent::OurViewChange(our_view), - ctx.sender(), - ); } // Handle messages on a specific v1 peer-set. The peer is expected to be connected on that diff --git a/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 5c201542eb560641c39c19cece9cc23fe076c90a..97bc66d6058c564838c15249d9012664b59eb995 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -23,6 +23,7 @@ use bitvec::{bitvec, vec::BitVec}; use futures::{ channel::oneshot, future::Fuse, pin_mut, select, stream::FuturesUnordered, FutureExt, StreamExt, }; +use schnellru::{ByLength, LruMap}; use sp_core::Pair; use polkadot_node_network_protocol::{ @@ -42,16 +43,15 @@ use polkadot_node_subsystem::{ CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeTxMessage, ParentHeadData, RuntimeApiMessage, }, - overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, PerLeafSpan, + overseer, FromOrchestra, OverseerSignal, PerLeafSpan, }; use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, runtime::{ - get_availability_cores, get_group_rotation_info, prospective_parachains_mode, - ProspectiveParachainsMode, RuntimeInfo, + fetch_claim_queue, get_availability_cores, get_group_rotation_info, + prospective_parachains_mode, ProspectiveParachainsMode, RuntimeInfo, }, - vstaging::fetch_claim_queue, TimeoutExt, }; use polkadot_primitives::{ @@ -201,6 +201,11 @@ struct PeerData { view: View, /// Network protocol version. version: CollationVersion, + /// Unknown heads in the view. + /// + /// This can happen when the validator is faster at importing a block and sending out its + /// `View` than the collator is able to import a block. + unknown_heads: LruMap, } /// A type wrapping a collation and it's designated core index. @@ -1199,9 +1204,10 @@ async fn handle_peer_view_change( peer_id: PeerId, view: View, ) { - let PeerData { view: current, version } = match state.peer_data.get_mut(&peer_id) { - Some(peer_data) => peer_data, - None => return, + let Some(PeerData { view: current, version, unknown_heads }) = + state.peer_data.get_mut(&peer_id) + else { + return }; let added: Vec = view.difference(&*current).cloned().collect(); @@ -1229,15 +1235,18 @@ async fn handle_peer_view_change( new_leaf = ?added, "New leaf in peer's view is unknown", ); + + unknown_heads.insert(added, ()); + continue }, }; for block_hash in block_hashes { - let per_relay_parent = match state.per_relay_parent.get_mut(block_hash) { - Some(per_relay_parent) => per_relay_parent, - None => continue, + let Some(per_relay_parent) = state.per_relay_parent.get_mut(block_hash) else { + continue }; + advertise_collation( ctx, *block_hash, @@ -1283,10 +1292,13 @@ async fn handle_network_msg( return Ok(()) }, }; - state - .peer_data - .entry(peer_id) - .or_insert_with(|| PeerData { view: View::default(), version }); + state.peer_data.entry(peer_id).or_insert_with(|| PeerData { + view: View::default(), + version, + // Unlikely that the collator is falling 10 blocks behind and if so, it probably is + // not able to keep up any way. + unknown_heads: LruMap::new(ByLength::new(10)), + }); if let Some(authority_ids) = maybe_authority { gum::trace!( @@ -1311,7 +1323,7 @@ async fn handle_network_msg( }, OurViewChange(view) => { gum::trace!(target: LOG_TARGET, ?view, "Own view change"); - handle_our_view_change(ctx.sender(), state, view).await?; + handle_our_view_change(ctx, state, view).await?; }, PeerMessage(remote, msg) => { handle_incoming_peer_message(ctx, runtime, state, remote, msg).await?; @@ -1333,21 +1345,19 @@ async fn handle_network_msg( } /// Handles our view changes. -async fn handle_our_view_change( - sender: &mut Sender, +#[overseer::contextbounds(CollatorProtocol, prefix = crate::overseer)] +async fn handle_our_view_change( + ctx: &mut Context, state: &mut State, view: OurView, -) -> Result<()> -where - Sender: CollatorProtocolSenderTrait, -{ +) -> Result<()> { let current_leaves = state.active_leaves.clone(); let removed = current_leaves.iter().filter(|(h, _)| !view.contains(h)); let added = view.iter().filter(|h| !current_leaves.contains_key(h)); for leaf in added { - let mode = prospective_parachains_mode(sender, *leaf).await?; + let mode = prospective_parachains_mode(ctx.sender(), *leaf).await?; if let Some(span) = view.span_per_head().get(leaf).cloned() { let per_leaf_span = PerLeafSpan::new(span, "collator-side"); @@ -1360,7 +1370,7 @@ where if mode.is_enabled() { if let Some(ref mut implicit_view) = state.implicit_view { implicit_view - .activate_leaf(sender, *leaf) + .activate_leaf(ctx.sender(), *leaf) .await .map_err(Error::ImplicitViewFetchError)?; @@ -1368,11 +1378,36 @@ where .known_allowed_relay_parents_under(leaf, state.collating_on) .unwrap_or_default(); + // Get the peers that already reported us this head, but we didn't knew it at this + // point. + let peers = state + .peer_data + .iter_mut() + .filter_map(|(id, data)| { + data.unknown_heads.remove(leaf).map(|_| (id, data.version)) + }) + .collect::>(); + for block_hash in allowed_ancestry { - state + let per_relay_parent = state .per_relay_parent .entry(*block_hash) .or_insert_with(|| PerRelayParent::new(mode)); + + // Announce relevant collations to these peers. + for (peer_id, peer_version) in &peers { + advertise_collation( + ctx, + *block_hash, + per_relay_parent, + &peer_id, + *peer_version, + &state.peer_ids, + &mut state.advertisement_timeouts, + &state.metrics, + ) + .await; + } } } } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs index 74a151c168dcab581d66e4f7be3530d703e0fdd9..2f4c768b89e060e73c6fcfc4cca4b16ec119f2c5 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs @@ -299,21 +299,33 @@ async fn overseer_send(overseer: &mut VirtualOverseer, msg: CollatorProtocolMess } async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { - let msg = overseer_recv_with_timeout(overseer, TIMEOUT) + overseer_recv_with_timeout(overseer, TIMEOUT) .await - .expect(&format!("{:?} is more than enough to receive messages", TIMEOUT)); + .expect(&format!("{:?} is more than enough to receive messages", TIMEOUT)) +} + +async fn overseer_recv_with_timeout( + overseer: &mut VirtualOverseer, + timeout: Duration, +) -> Option { + gum::trace!("waiting for message..."); + let msg = overseer.recv().timeout(timeout).await; gum::trace!(?msg, "received message"); msg } -async fn overseer_recv_with_timeout( +async fn overseer_peek_with_timeout( overseer: &mut VirtualOverseer, timeout: Duration, -) -> Option { - gum::trace!("waiting for message..."); - overseer.recv().timeout(timeout).await +) -> Option<&AllMessages> { + gum::trace!("peeking for message..."); + let msg = overseer.peek().timeout(timeout).await; + + gum::trace!(?msg, "received message"); + + msg.flatten() } async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) { @@ -603,7 +615,7 @@ async fn expect_declare_msg( /// Expects v2 message if `expected_candidate_hashes` is `Some`, v1 otherwise. async fn expect_advertise_collation_msg( virtual_overseer: &mut VirtualOverseer, - peer: &PeerId, + any_peers: &[PeerId], expected_relay_parent: Hash, expected_candidate_hashes: Option>, ) { @@ -620,7 +632,7 @@ async fn expect_advertise_collation_msg( wire_message, ) ) => { - assert_eq!(to[0], *peer); + assert!(any_peers.iter().any(|p| to.contains(p))); match (candidate_hashes.as_mut(), wire_message) { (None, Versioned::V1(protocol_v1::CollationProtocol::CollatorProtocol(wire_message))) => { assert_matches!( @@ -739,7 +751,7 @@ fn advertise_and_send_collation() { // advertise it. expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -849,7 +861,7 @@ fn advertise_and_send_collation() { expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -910,7 +922,7 @@ fn delay_reputation_change() { // advertise it. expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -1031,7 +1043,7 @@ fn advertise_collation_v2_protocol() { // Versioned advertisements work. expect_advertise_collation_msg( virtual_overseer, - &peer_ids[0], + &[peer_ids[0]], test_state.relay_parent, None, ) @@ -1039,7 +1051,7 @@ fn advertise_collation_v2_protocol() { for peer_id in peer_ids.iter().skip(1) { expect_advertise_collation_msg( virtual_overseer, - peer_id, + &[*peer_id], test_state.relay_parent, Some(vec![candidate.hash()]), // This is `Some`, advertisement is v2. ) @@ -1142,15 +1154,25 @@ fn collations_are_only_advertised_to_validators_with_correct_view() { distribute_collation(virtual_overseer, &test_state, test_state.relay_parent, true) .await; - expect_advertise_collation_msg(virtual_overseer, &peer2, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer2], + test_state.relay_parent, + None, + ) + .await; // The other validator announces that it changed its view. send_peer_view_change(virtual_overseer, &peer, vec![test_state.relay_parent]).await; // After changing the view we should receive the advertisement - expect_advertise_collation_msg(virtual_overseer, &peer, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer], + test_state.relay_parent, + None, + ) + .await; test_harness }, ) @@ -1199,12 +1221,17 @@ fn collate_on_two_different_relay_chain_blocks() { .await; send_peer_view_change(virtual_overseer, &peer, vec![old_relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer, old_relay_parent, None).await; + expect_advertise_collation_msg(virtual_overseer, &[peer], old_relay_parent, None).await; send_peer_view_change(virtual_overseer, &peer2, vec![test_state.relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer2, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer2], + test_state.relay_parent, + None, + ) + .await; test_harness }, ) @@ -1237,8 +1264,13 @@ fn validator_reconnect_does_not_advertise_a_second_time() { .await; send_peer_view_change(virtual_overseer, &peer, vec![test_state.relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer], + test_state.relay_parent, + None, + ) + .await; // Disconnect and reconnect directly disconnect_peer(virtual_overseer, peer).await; @@ -1361,14 +1393,14 @@ where // advertise it. expect_advertise_collation_msg( virtual_overseer, - &validator_0, + &[validator_0], test_state.relay_parent, None, ) .await; expect_advertise_collation_msg( virtual_overseer, - &validator_1, + &[validator_1], test_state.relay_parent, None, ) @@ -1498,9 +1530,10 @@ fn connect_to_buffered_groups() { } // Update views. - for peed_id in &peers_a { - send_peer_view_change(&mut virtual_overseer, peed_id, vec![head_a]).await; - expect_advertise_collation_msg(&mut virtual_overseer, peed_id, head_a, None).await; + for peer_id in &peers_a { + send_peer_view_change(&mut virtual_overseer, peer_id, vec![head_a]).await; + expect_advertise_collation_msg(&mut virtual_overseer, &[*peer_id], head_a, None) + .await; } let peer = peers_a[0]; diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs index ea8fdb0e04fbe0ba29303b62ae1d45c63d43cdf4..d3eae9dbba6e32cf91c95250a28a0c91e7a45e21 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs @@ -45,7 +45,6 @@ async fn update_view( ) .await; - let mut next_overseer_message = None; for _ in 0..activated { let (leaf_hash, leaf_number) = assert_matches!( overseer_recv(virtual_overseer).await, @@ -147,18 +146,10 @@ async fn update_view( let parent_hash = ancestry_iter.peek().map(|(h, _)| *h).unwrap_or_else(|| get_parent_hash(hash)); - let msg = match next_overseer_message.take() { - Some(msg) => Some(msg), - None => - overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(50)).await, - }; - - let msg = match msg { - Some(msg) => msg, - None => { - // We're done. - return - }, + let Some(msg) = + overseer_peek_with_timeout(virtual_overseer, Duration::from_millis(50)).await + else { + return }; if !matches!( @@ -167,12 +158,11 @@ async fn update_view( if *_hash == hash ) { // Ancestry has already been cached for this leaf. - next_overseer_message.replace(msg); break } assert_matches!( - msg, + overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(50)).await.unwrap(), AllMessages::ChainApi(ChainApiMessage::BlockHeader(.., tx)) => { let header = Header { parent_hash, @@ -238,124 +228,156 @@ fn distribute_collation_from_implicit_view() { let head_c = Hash::from_low_u64_be(130); let head_c_num = 62; - let group_rotation_info = GroupRotationInfo { - session_start_block: head_c_num - 2, - group_rotation_frequency: 3, - now: head_c_num, - }; + // Run once with validators sending their view first and then the collator setting their own + // view first. + for validator_sends_view_first in [true, false] { + let group_rotation_info = GroupRotationInfo { + session_start_block: head_c_num - 2, + group_rotation_frequency: 3, + now: head_c_num, + }; + + let mut test_state = TestState::default(); + test_state.group_rotation_info = group_rotation_info; + + let local_peer_id = test_state.local_peer_id; + let collator_pair = test_state.collator_pair.clone(); + + test_harness( + local_peer_id, + collator_pair, + ReputationAggregator::new(|_| true), + |mut test_harness| async move { + let virtual_overseer = &mut test_harness.virtual_overseer; + + // Set collating para id. + overseer_send( + virtual_overseer, + CollatorProtocolMessage::CollateOn(test_state.para_id), + ) + .await; - let mut test_state = TestState::default(); - test_state.group_rotation_info = group_rotation_info; + if validator_sends_view_first { + // Activate leaf `c` to accept at least the collation. + update_view(virtual_overseer, vec![(head_c, head_c_num)], 1).await; + } else { + // Activated leaf is `b`, but the collation will be based on `c`. + update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; + } - let local_peer_id = test_state.local_peer_id; - let collator_pair = test_state.collator_pair.clone(); + let validator_peer_ids = test_state.current_group_validator_peer_ids(); + for (val, peer) in test_state + .current_group_validator_authority_ids() + .into_iter() + .zip(validator_peer_ids.clone()) + { + connect_peer(virtual_overseer, peer, CollationVersion::V2, Some(val.clone())) + .await; + } - test_harness( - local_peer_id, - collator_pair, - ReputationAggregator::new(|_| true), - |mut test_harness| async move { - let virtual_overseer = &mut test_harness.virtual_overseer; + // Collator declared itself to each peer. + for peer_id in &validator_peer_ids { + expect_declare_msg_v2(virtual_overseer, &test_state, peer_id).await; + } - // Set collating para id. - overseer_send(virtual_overseer, CollatorProtocolMessage::CollateOn(test_state.para_id)) - .await; - // Activated leaf is `b`, but the collation will be based on `c`. - update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; - - let validator_peer_ids = test_state.current_group_validator_peer_ids(); - for (val, peer) in test_state - .current_group_validator_authority_ids() - .into_iter() - .zip(validator_peer_ids.clone()) - { - connect_peer(virtual_overseer, peer, CollationVersion::V2, Some(val.clone())).await; - } + let pov = PoV { block_data: BlockData(vec![1, 2, 3]) }; + let parent_head_data_hash = Hash::repeat_byte(0xAA); + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_c, + pov_hash: pov.hash(), + ..Default::default() + } + .build(); + let DistributeCollation { candidate, pov_block: _ } = + distribute_collation_with_receipt( + virtual_overseer, + &test_state, + head_c, + false, // Check the group manually. + candidate, + pov, + parent_head_data_hash, + ) + .await; + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::NetworkBridgeTx( + NetworkBridgeTxMessage::ConnectToValidators { validator_ids, .. } + ) => { + let expected_validators = test_state.current_group_validator_authority_ids(); - // Collator declared itself to each peer. - for peer_id in &validator_peer_ids { - expect_declare_msg_v2(virtual_overseer, &test_state, peer_id).await; - } + assert_eq!(expected_validators, validator_ids); + } + ); - let pov = PoV { block_data: BlockData(vec![1, 2, 3]) }; - let parent_head_data_hash = Hash::repeat_byte(0xAA); - let candidate = TestCandidateBuilder { - para_id: test_state.para_id, - relay_parent: head_c, - pov_hash: pov.hash(), - ..Default::default() - } - .build(); - let DistributeCollation { candidate, pov_block: _ } = - distribute_collation_with_receipt( - virtual_overseer, - &test_state, - head_c, - false, // Check the group manually. - candidate, - pov, - parent_head_data_hash, - ) - .await; - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::NetworkBridgeTx( - NetworkBridgeTxMessage::ConnectToValidators { validator_ids, .. } - ) => { - let expected_validators = test_state.current_group_validator_authority_ids(); + let candidate_hash = candidate.hash(); + + // Update peer views. + for peer_id in &validator_peer_ids { + send_peer_view_change(virtual_overseer, peer_id, vec![head_b]).await; - assert_eq!(expected_validators, validator_ids); + if !validator_sends_view_first { + expect_advertise_collation_msg( + virtual_overseer, + &[*peer_id], + head_c, + Some(vec![candidate_hash]), + ) + .await; + } } - ); - let candidate_hash = candidate.hash(); + if validator_sends_view_first { + // Activated leaf is `b`, but the collation will be based on `c`. + update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; + + for _ in &validator_peer_ids { + expect_advertise_collation_msg( + virtual_overseer, + &validator_peer_ids, + head_c, + Some(vec![candidate_hash]), + ) + .await; + } + } - // Update peer views. - for peed_id in &validator_peer_ids { - send_peer_view_change(virtual_overseer, peed_id, vec![head_b]).await; - expect_advertise_collation_msg( + // Head `c` goes out of view. + // Build a different candidate for this relay parent and attempt to distribute it. + update_view(virtual_overseer, vec![(head_a, head_a_num)], 1).await; + + let pov = PoV { block_data: BlockData(vec![4, 5, 6]) }; + let parent_head_data_hash = Hash::repeat_byte(0xBB); + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_c, + pov_hash: pov.hash(), + ..Default::default() + } + .build(); + overseer_send( virtual_overseer, - peed_id, - head_c, - Some(vec![candidate_hash]), + CollatorProtocolMessage::DistributeCollation { + candidate_receipt: candidate.clone(), + parent_head_data_hash, + pov: pov.clone(), + parent_head_data: HeadData(vec![1, 2, 3]), + result_sender: None, + core_index: CoreIndex(0), + }, ) .await; - } - - // Head `c` goes out of view. - // Build a different candidate for this relay parent and attempt to distribute it. - update_view(virtual_overseer, vec![(head_a, head_a_num)], 1).await; - - let pov = PoV { block_data: BlockData(vec![4, 5, 6]) }; - let parent_head_data_hash = Hash::repeat_byte(0xBB); - let candidate = TestCandidateBuilder { - para_id: test_state.para_id, - relay_parent: head_c, - pov_hash: pov.hash(), - ..Default::default() - } - .build(); - overseer_send( - virtual_overseer, - CollatorProtocolMessage::DistributeCollation { - candidate_receipt: candidate.clone(), - parent_head_data_hash, - pov: pov.clone(), - parent_head_data: HeadData(vec![1, 2, 3]), - result_sender: None, - core_index: CoreIndex(0), - }, - ) - .await; - // Parent out of view, nothing happens. - assert!(overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(100)) - .await - .is_none()); + // Parent out of view, nothing happens. + assert!(overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(100)) + .await + .is_none()); - test_harness - }, - ) + test_harness + }, + ); + } } /// Tests that collator can distribute up to `MAX_CANDIDATE_DEPTH + 1` candidates @@ -505,7 +527,7 @@ fn send_parent_head_data_for_elastic_scaling() { send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; let hashes: Vec<_> = vec![candidate.hash()]; - expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + expect_advertise_collation_msg(&mut virtual_overseer, &[peer], head_b, Some(hashes)) .await; let (pending_response, rx) = oneshot::channel(); @@ -625,7 +647,7 @@ fn advertise_and_send_collation_by_hash() { // Head `b` is not a leaf, but both advertisements are still relevant. send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; let hashes: Vec<_> = candidates.iter().map(|(candidate, _)| candidate.hash()).collect(); - expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + expect_advertise_collation_msg(&mut virtual_overseer, &[peer], head_b, Some(hashes)) .await; for (candidate, pov_block) in candidates { diff --git a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs index f5c9726f3f6a509a4966720f5e2011d173a9d318..cbf00a9e119d95af0c254df31babf67652d9da25 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -50,8 +50,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, - runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, - vstaging::fetch_claim_queue, + runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ CandidateHash, CollatorId, CoreState, Hash, HeadData, Id as ParaId, OccupiedCoreAssumption, diff --git a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs index 2409e6994f604ecfa905c5300fe7d59c6d9974fa..77c1e41aac050168e8e5b204a9ef1d1528c67014 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs @@ -66,9 +66,12 @@ use self::{ const COST_INVALID_REQUEST: Rep = Rep::CostMajor("Received message could not be decoded."); const COST_INVALID_SIGNATURE: Rep = Rep::Malicious("Signatures were invalid."); -const COST_INVALID_IMPORT: Rep = - Rep::Malicious("Import was deemed invalid by dispute-coordinator."); const COST_NOT_A_VALIDATOR: Rep = Rep::CostMajor("Reporting peer was not a validator."); + +/// Invalid imports can be caused by flooding, e.g. by a disabled validator. +const COST_INVALID_IMPORT: Rep = + Rep::CostMinor("Import was deemed invalid by dispute-coordinator."); + /// Mildly punish peers exceeding their rate limit. /// /// For honest peers this should rarely happen, but if it happens we would not want to disconnect diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index 47d350849b206913b9e493cd9befd6086d0c4e7b..f9c2d0ddbae8b373485f90a510137bc7e42c9271 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -45,8 +45,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::ReputationAggregator, - runtime::{request_min_backing_votes, ProspectiveParachainsMode}, - vstaging::{fetch_claim_queue, ClaimQueueSnapshot}, + runtime::{ + fetch_claim_queue, request_min_backing_votes, ClaimQueueSnapshot, ProspectiveParachainsMode, + }, }; use polkadot_primitives::{ AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, CoreState, GroupIndex, @@ -570,7 +571,7 @@ pub(crate) async fn handle_active_leaves_update( for new_relay_parent in new_relay_parents.iter().cloned() { let disabled_validators: HashSet<_> = - polkadot_node_subsystem_util::vstaging::get_disabled_validators_with_fallback( + polkadot_node_subsystem_util::runtime::get_disabled_validators_with_fallback( ctx.sender(), new_relay_parent, ) @@ -2237,7 +2238,9 @@ async fn fragment_chain_update_inner( // 2. find out which are in the frontier gum::debug!( target: LOG_TARGET, - "Calling getHypotheticalMembership from statement distribution" + active_leaf_hash = ?active_leaf_hash, + "Calling getHypotheticalMembership from statement distribution for candidates: {:?}", + &hypotheticals.iter().map(|hypo| hypo.candidate_hash()).collect::>() ); let candidate_memberships = { let (tx, rx) = oneshot::channel(); diff --git a/polkadot/node/network/statement-distribution/src/v2/statement_store.rs b/polkadot/node/network/statement-distribution/src/v2/statement_store.rs index a3b2636d2ffcb6c761a3f9c2da89f88fecd9d981..56a54f6316c070df0111d22bbc9649fb8de6d0b4 100644 --- a/polkadot/node/network/statement-distribution/src/v2/statement_store.rs +++ b/polkadot/node/network/statement-distribution/src/v2/statement_store.rs @@ -292,7 +292,7 @@ impl GroupStatements { mod tests { use super::*; - use polkadot_primitives::v7::{Hash, SigningContext, ValidatorPair}; + use polkadot_primitives::{Hash, SigningContext, ValidatorPair}; use sp_application_crypto::Pair as PairT; #[test] diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index 4e13d5eda76f61c646ad0814e1ec3d8068a11ca1..26a6a907e324b31d0610f2a664919c9e380926c1 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -466,7 +466,7 @@ pub async fn forward_events>(client: Arc

, mut hand message_capacity=2048, )] pub struct Overseer { - #[subsystem(blocking, CandidateValidationMessage, sends: [ + #[subsystem(CandidateValidationMessage, sends: [ RuntimeApiMessage, ])] candidate_validation: CandidateValidation, @@ -581,6 +581,7 @@ pub struct Overseer { #[subsystem(blocking, message_capacity: 64000, ApprovalDistributionMessage, sends: [ NetworkBridgeTxMessage, ApprovalVotingMessage, + RuntimeApiMessage, ], can_receive_priority_messages)] approval_distribution: ApprovalDistribution, diff --git a/polkadot/node/primitives/Cargo.toml b/polkadot/node/primitives/Cargo.toml index 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..79f4cfa9e0be41fb70d44878d8e3c829a48cfe55 100644 --- a/polkadot/node/primitives/src/approval.rs +++ b/polkadot/node/primitives/src/approval/mod.rs @@ -16,6 +16,12 @@ //! Types relevant for approval. +/// Criteria for assignment. +pub mod criteria; + +/// Time utilities for approval voting. +pub mod time; + /// A list of primitives introduced in v1. pub mod v1 { use sp_consensus_babe as babe_primitives; @@ -25,8 +31,8 @@ pub mod v1 { use codec::{Decode, Encode}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CoreIndex, Hash, Header, SessionIndex, - ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, GroupIndex, Hash, Header, + SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_application_crypto::ByteArray; @@ -128,11 +134,13 @@ pub mod v1 { pub parent_hash: Hash, /// The candidates included by the block. /// Note that these are not the same as the candidates that appear within the block body. - pub candidates: Vec, + pub candidates: Vec<(CandidateHash, CoreIndex, GroupIndex)>, /// The consensus slot of the block. pub slot: Slot, /// The session of the block. pub session: SessionIndex, + /// The vrf story. + pub vrf_story: RelayVRFStory, } /// Errors that can occur during the approvals protocol. diff --git a/polkadot/node/core/approval-voting/src/time.rs b/polkadot/node/primitives/src/approval/time.rs similarity index 95% rename from polkadot/node/core/approval-voting/src/time.rs rename to polkadot/node/primitives/src/approval/time.rs index 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 24c69582731b7cbc7857beda75f0dddac77708b3..685a9fd337dfd3ef0281d681d74a116f26943e3c 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -59,7 +59,7 @@ pub use disputes::{ /// relatively rare. /// /// The associated worker binaries should use the same version as the node that spawns them. -pub const NODE_VERSION: &'static str = "1.15.0"; +pub const NODE_VERSION: &'static str = "1.15.1"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: diff --git a/polkadot/node/service/chain-specs/kusama.json b/polkadot/node/service/chain-specs/kusama.json index 5d2413ac1e0263bc1cd69d4e9af696c95e909cdd..ff38192906da0e905fdddb878590a00a9ab41ad2 100644 --- a/polkadot/node/service/chain-specs/kusama.json +++ b/polkadot/node/service/chain-specs/kusama.json @@ -37,7 +37,8 @@ "/dns/ibp-boot-kusama.luckyfriday.io/tcp/30333/p2p/12D3KooW9vu1GWHBuxyhm7rZgD3fhGZpNajPXFexadvhujWMgwfT", "/dns/boot-kusama.luckyfriday.io/tcp/443/wss/p2p/12D3KooWS1Lu6DmK8YHSvkErpxpcXmk14vG6y4KVEFEkd9g62PP8", "/dns/ibp-boot-kusama.luckyfriday.io/tcp/30334/wss/p2p/12D3KooW9vu1GWHBuxyhm7rZgD3fhGZpNajPXFexadvhujWMgwfT", - "/dns4/kusama-0.boot.onfinality.io/tcp/27682/ws/p2p/12D3KooWFrwFo7ry3dEuFwhehGSSN96a5Xdzxot7SWfXeSbhELAe" + "/dns4/kusama-0.boot.onfinality.io/tcp/27682/ws/p2p/12D3KooWFrwFo7ry3dEuFwhehGSSN96a5Xdzxot7SWfXeSbhELAe", + "/dns/kusama.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWJHhnF64TXSmyxNkhPkXAHtYNRy86LuvGQu1LTi5vrJCL" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/chain-specs/paseo.json b/polkadot/node/service/chain-specs/paseo.json index 35ac9aa6aa93678192b199f07906d943568ac022..aacfdb02578684bf11c25bf3fc08fcf2570d8683 100644 --- a/polkadot/node/service/chain-specs/paseo.json +++ b/polkadot/node/service/chain-specs/paseo.json @@ -20,7 +20,8 @@ "/dns/pso16.rotko.net/tcp/33246/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", "/dns/pso16.rotko.net/tcp/35246/wss/p2p/12D3KooWRH8eBMhw8c7bucy6pJfy94q4dKpLkF3pmeGohHmemdRu", "/dns/paseo-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr", - "/dns/paseo-boot-ng.dwellir.com/tcp/30354/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr" + "/dns/paseo-boot-ng.dwellir.com/tcp/30354/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr", + "/dns/paseo.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWMdND5nwfCs5M2rfp5kyRo41BGDgD8V67rVRaB3acgZ53" ], "telemetryEndpoints": null, "protocolId": "pas", diff --git a/polkadot/node/service/chain-specs/polkadot.json b/polkadot/node/service/chain-specs/polkadot.json index 553ff1bb3e41e87a9b01c3e1d36019604a83e9b1..7e1e90f6a8c15e4491cd13176484903763044318 100644 --- a/polkadot/node/service/chain-specs/polkadot.json +++ b/polkadot/node/service/chain-specs/polkadot.json @@ -38,7 +38,8 @@ "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30333/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ", "/dns/ibp-boot-polkadot.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWEjk6QXrZJ26fLpaajisJGHiz6WiQsR8k7mkM9GmWKnRZ", "/dns/boot-polkadot.luckyfriday.io/tcp/443/wss/p2p/12D3KooWAdyiVAaeGdtBt6vn5zVetwA4z4qfm9Fi2QCSykN1wTBJ", - "/dns4/polkadot-0.boot.onfinality.io/tcp/24446/ws/p2p/12D3KooWT1PWaNdAwYrSr89dvStnoGdH3t4LNRbcVNN4JCtsotkR" + "/dns4/polkadot-0.boot.onfinality.io/tcp/24446/ws/p2p/12D3KooWT1PWaNdAwYrSr89dvStnoGdH3t4LNRbcVNN4JCtsotkR", + "/dns/polkadot.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWEymrFRHz6c17YP3FAyd8kXS5gMRLgkW4U77ZJD2ZNCLZ" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/chain-specs/westend.json b/polkadot/node/service/chain-specs/westend.json index 9059d525689d9ee2e9103fdda81f039d99b8f470..08c5bd33b4bd7e9f6c9cc75dfeef4de6f55ab491 100644 --- a/polkadot/node/service/chain-specs/westend.json +++ b/polkadot/node/service/chain-specs/westend.json @@ -33,7 +33,8 @@ "/dns/wnd14.rotko.net/tcp/35234/wss/p2p/12D3KooWLK8Zj1uZ46phU3vQwiDVda8tB76S8J26rXZQLHpwWkDJ", "/dns/wnd14.rotko.net/tcp/33234/p2p/12D3KooWLK8Zj1uZ46phU3vQwiDVda8tB76S8J26rXZQLHpwWkDJ", "/dns/ibp-boot-westend.luckyfriday.io/tcp/30333/p2p/12D3KooWDg1YEytdwFFNWroFj6gio4YFsMB3miSbHKgdpJteUMB9", - "/dns/ibp-boot-westend.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWDg1YEytdwFFNWroFj6gio4YFsMB3miSbHKgdpJteUMB9" + "/dns/ibp-boot-westend.luckyfriday.io/tcp/30334/wss/p2p/12D3KooWDg1YEytdwFFNWroFj6gio4YFsMB3miSbHKgdpJteUMB9", + "/dns/westend.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWHaQKkJiTPqeNgqDcW7dfYgJxYwT8YqJMtTkueSu6378V" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index f930476a5541af602b3f9a6e14cab9b9ba580182..d377a75f1069b52edbcd02a3c73f67d15be3d3a4 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -25,7 +25,7 @@ use sp_consensus_babe::AuthorityId as BabeId; use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId; #[cfg(feature = "westend-native")] -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; #[cfg(feature = "rococo-native")] use rococo_runtime as rococo; use sc_chain_spec::ChainSpecExtension; diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index cdef39d5bdf1805d1066fdeba9cb09f0037261b1..1f2efdbbb5b38937611299907a1da712a480c30c 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -21,12 +21,16 @@ use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - runtime_api, slashing, AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + runtime_api, slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, CandidateCommitments, + CandidateHash, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, + InboundDownwardMessage, InboundHrmpMessage, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; use sp_consensus_grandpa::AuthorityId as GrandpaId; diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index 5ca566d2c96286a1e4bdb1ee5010e7d2dde5ebed..fe96d29c1cebcdd1773fe3cc6fede1ca919f1949 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -63,6 +63,7 @@ use { }; use polkadot_node_subsystem_util::database::Database; +use polkadot_overseer::SpawnGlue; #[cfg(feature = "full-node")] pub use { @@ -83,7 +84,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use prometheus_endpoint::Registry; #[cfg(feature = "full-node")] use sc_service::KeystoreContainer; -use sc_service::RpcHandlers; +use sc_service::{RpcHandlers, SpawnTaskHandle}; use sc_telemetry::TelemetryWorker; #[cfg(feature = "full-node")] use sc_telemetry::{Telemetry, TelemetryWorkerHandle}; @@ -437,15 +438,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) = @@ -486,7 +488,6 @@ fn new_partial( sc_transaction_pool::FullPool, ( impl Fn( - polkadot_rpc::DenyUnsafe, polkadot_rpc::SubscriptionTaskExecutor, ) -> Result, ( @@ -593,15 +594,13 @@ where let chain_spec = config.chain_spec.cloned_box(); let backend = backend.clone(); - move |deny_unsafe, - subscription_executor: polkadot_rpc::SubscriptionTaskExecutor| + move |subscription_executor: polkadot_rpc::SubscriptionTaskExecutor| -> Result { let deps = polkadot_rpc::FullDeps { client: client.clone(), pool: transaction_pool.clone(), select_chain: select_chain.clone(), chain_spec: chain_spec.cloned_box(), - deny_unsafe, babe: polkadot_rpc::BabeDeps { babe_worker_handle: babe_worker_handle.clone(), keystore: keystore.clone(), @@ -764,10 +763,11 @@ pub fn new_full< ) -> Result { use polkadot_availability_recovery::FETCH_CHUNKS_THRESHOLD; use polkadot_node_network_protocol::request_response::IncomingRequest; - use sc_network_sync::WarpSyncParams; + use sc_network_sync::WarpSyncConfig; + 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()) @@ -1037,7 +1037,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; @@ -1082,13 +1082,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 + ); + } }, _ => {}, } @@ -1483,6 +1501,7 @@ pub fn revert_backend( backend: Arc, blocks: BlockNumber, config: Configuration, + task_handle: SpawnTaskHandle, ) -> Result<(), Error> { let best_number = client.info().best_number; let finalized = client.info().finalized_number; @@ -1503,7 +1522,7 @@ pub fn revert_backend( let parachains_db = open_database(&config.database) .map_err(|err| sp_blockchain::Error::Backend(err.to_string()))?; - revert_approval_voting(parachains_db.clone(), hash)?; + revert_approval_voting(parachains_db.clone(), hash, task_handle)?; revert_chain_selection(parachains_db, hash)?; // Revert Substrate consensus related components sc_consensus_babe::revert(client.clone(), backend, blocks)?; @@ -1526,7 +1545,11 @@ fn revert_chain_selection(db: Arc, hash: Hash) -> sp_blockchain::R .map_err(|err| sp_blockchain::Error::Backend(err.to_string())) } -fn revert_approval_voting(db: Arc, hash: Hash) -> sp_blockchain::Result<()> { +fn revert_approval_voting( + db: Arc, + hash: Hash, + task_handle: SpawnTaskHandle, +) -> sp_blockchain::Result<()> { let config = approval_voting_subsystem::Config { col_approval_data: parachains_db::REAL_COLUMNS.col_approval_data, slot_duration_millis: Default::default(), @@ -1538,6 +1561,7 @@ fn revert_approval_voting(db: Arc, hash: Hash) -> sp_blockchain::R Arc::new(sc_keystore::LocalKeystore::in_memory()), Box::new(sp_consensus::NoNetwork), approval_voting_subsystem::Metrics::default(), + Arc::new(SpawnGlue(task_handle)), ); approval_voting diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index add5d239364dfa8b66aaa33cc825512a59b244f4..3c071e34fe11d03e654bbc46c6c47517ccf03b1a 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -20,7 +20,7 @@ use polkadot_overseer::{DummySubsystem, InitializedOverseerBuilder, SubsystemErr use sp_core::traits::SpawnNamed; use polkadot_availability_distribution::IncomingRequestReceivers; -use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig; +use polkadot_node_core_approval_voting::{Config as ApprovalVotingConfig, RealAssignmentCriteria}; use polkadot_node_core_av_store::Config as AvailabilityConfig; use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig; use polkadot_node_core_chain_selection::Config as ChainSelectionConfig; @@ -309,13 +309,18 @@ where Metrics::register(registry)?, rand::rngs::StdRng::from_entropy(), )) - .approval_distribution(ApprovalDistributionSubsystem::new(Metrics::register(registry)?)) + .approval_distribution(ApprovalDistributionSubsystem::new( + Metrics::register(registry)?, + approval_voting_config.slot_duration_millis, + Arc::new(RealAssignmentCriteria {}), + )) .approval_voting(ApprovalVotingSubsystem::with_config( approval_voting_config, parachains_db.clone(), keystore.clone(), Box::new(sync_service.clone()), Metrics::register(registry)?, + Arc::new(spawner.clone()), )) .gossip_support(GossipSupportSubsystem::new( keystore.clone(), diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 9384f8142a97020ff17d300ba29fd523d64ecd6d..ae798cf2640a727d36c79a56019337792a1485b6 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -19,7 +19,11 @@ path = "src/cli/subsystem-bench.rs" # Prevent rustdoc error. Already documented from top-level Cargo.toml. doc = false + [dependencies] +tikv-jemallocator = { features = ["profiling", "unprefixed_malloc_on_supported_platforms"], workspace = true, optional = true } +jemalloc_pprof = { workspace = true, optional = true } +polkadot-service = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-node-subsystem-types = { workspace = true, default-features = true } @@ -93,3 +97,7 @@ strum = { features = ["derive"], workspace = true, default-features = true } [features] default = [] +memprofile = [ + "dep:jemalloc_pprof", + "dep:tikv-jemallocator", +] diff --git a/polkadot/node/subsystem-bench/README.md b/polkadot/node/subsystem-bench/README.md index 228fba41c46cce48a2a4bda96e869f6abcba1c44..8d20f1f49c0e705098d0e45a962623be58488f68 100644 --- a/polkadot/node/subsystem-bench/README.md +++ b/polkadot/node/subsystem-bench/README.md @@ -260,6 +260,41 @@ This file is best interpreted with `cg_annotate --auto=yes cachegrind.out.` For finer profiling of cache misses, better use `perf` on a bare-metal machine. +### Profile memory usage using jemalloc + +Bellow you can find instructions how to setup and run profiling with jemalloc, this is complementary +with using other memory profiling tools like: . + +#### Prerequisites + +Install tooling with: + +``` +sudo apt install libjemalloc-dev graphviz +``` + +#### Generate memory usage snapshots + +Memory usage can be profiled by running any subsystem benchmark with `--features memprofile`, e.g: + +``` +RUSTFLAGS=-g cargo run -p polkadot-subsystem-bench --release --features memprofile -- polkadot/node/subsystem-bench/examples/approvals_throughput.yaml +``` + +#### Interpret the results + +After the benchmark ran the memory usage snapshots can be found in `/tmp/subsystem-bench*`, to extract the information +from a snapshot you can use `jeprof` like this: + +``` +jeprof --text PATH_TO_EXECUTABLE_WITH_DEBUG_SYMBOLS /tmp/subsystem-bench.1222895.199.i199.heap > statistics.txt +``` + +Useful links: + +- Tutorial: +- Jemalloc configuration options: + ## Create new test objectives This tool is intended to make it easy to write new test objectives that focus individual subsystems, diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 153efdda4056f5ffd96a9cf7692cf51b2a07f440..0f68b905b4ca880aada73c3996bef9ed8d804b60 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -182,6 +182,17 @@ impl BenchCli { } } +#[cfg(feature = "memprofile")] +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + +#[cfg(feature = "memprofile")] +#[allow(non_upper_case_globals)] +#[export_name = "malloc_conf"] +// See https://jemalloc.net/jemalloc.3.html for more information on the configuration options. +pub static malloc_conf: &[u8] = + b"prof:true,prof_active:true,lg_prof_interval:30,lg_prof_sample:21,prof_prefix:/tmp/subsystem-bench\0"; + fn main() -> eyre::Result<()> { color_eyre::install()?; sp_tracing::try_init_simple(); diff --git a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs index ca58875c81393eebfea236e4fee0c25ca9d3a023..4b2b91696824c57f0965c166cbbb55f82263d61a 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -16,11 +16,11 @@ use crate::configuration::TestAuthorities; use itertools::Itertools; -use polkadot_node_core_approval_voting::time::{Clock, SystemClock, Tick}; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, View, }; +use polkadot_node_primitives::approval::time::{Clock, SystemClock, Tick}; use polkadot_node_subsystem_types::messages::{ network_bridge_event::NewGossipTopology, ApprovalDistributionMessage, NetworkBridgeEvent, }; diff --git a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs index 6d3e7dd92db151d067c9385dfdb4d39419152b76..da25a3bf3b7997561d7234b753c754e5d0e9e52e 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -28,16 +28,15 @@ use crate::{ use codec::Encode; use futures::SinkExt; use itertools::Itertools; -use polkadot_node_core_approval_voting::{ - criteria::{compute_assignments, Config}, - time::tranche_to_tick, -}; +use polkadot_node_core_approval_voting::criteria::{compute_assignments, Config}; + use polkadot_node_network_protocol::{ grid_topology::{GridNeighbors, RandomRouting, RequiredRouting, SessionGridTopology}, v3 as protocol_v3, }; use polkadot_node_primitives::approval::{ self, + time::tranche_to_tick, v2::{CoreBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }; use polkadot_primitives::{ diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs index 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..9d85039b8880c17aea8190d0b29a67eaeca95cc6 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -49,9 +49,13 @@ use itertools::Itertools; use orchestra::TimeoutExt; use overseer::{metrics::Metrics as OverseerMetrics, MetricsTrait}; use polkadot_approval_distribution::ApprovalDistribution; +use polkadot_node_primitives::approval::time::{ + slot_number_to_tick, tick_to_slot_number, Clock, ClockExt, SystemClock, +}; + use polkadot_node_core_approval_voting::{ - time::{slot_number_to_tick, tick_to_slot_number, Clock, ClockExt, SystemClock}, ApprovalVotingSubsystem, Config as ApprovalVotingConfig, Metrics as ApprovalVotingMetrics, + RealAssignmentCriteria, }; use polkadot_node_network_protocol::v3 as protocol_v3; use polkadot_node_primitives::approval::{self, v1::RelayVRFStory}; @@ -810,11 +814,16 @@ fn build_overseer( Arc::new(keystore), Box::new(TestSyncOracle {}), state.approval_voting_metrics.clone(), - Box::new(system_clock.clone()), + Arc::new(system_clock.clone()), + Arc::new(SpawnGlue(spawn_task_handle.clone())), ); - let approval_distribution = - ApprovalDistribution::new(Metrics::register(Some(&dependencies.registry)).unwrap()); + let approval_distribution = ApprovalDistribution::new_with_clock( + Metrics::register(Some(&dependencies.registry)).unwrap(), + SLOT_DURATION_MILLIS, + Box::new(system_clock.clone()), + Arc::new(RealAssignmentCriteria {}), + ); let mock_chain_api = MockChainApi::new(state.build_chain_api_state()); let mock_chain_selection = MockChainSelection { state: state.clone(), clock: system_clock }; let mock_runtime_api = MockRuntimeApi::new( diff --git a/polkadot/node/subsystem-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-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 4d27ac9b70e33b09810eaab3a30f5c3c82c8456f..854a9da158be65412809e34f7d75eb07b3ad9712 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -33,7 +33,7 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::{ approval::{ - v1::BlockApprovalMeta, + v1::{BlockApprovalMeta, DelayTranche}, v2::{CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }, AvailableData, BabeEpoch, BlockWeight, CandidateVotes, CollationGenerationConfig, @@ -43,13 +43,13 @@ use polkadot_node_primitives::{ }; use polkadot_primitives::{ async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BackedCandidate, - BlockNumber, CandidateEvent, CandidateHash, CandidateIndex, CandidateReceipt, CollatorId, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, HeadData, Header as BlockHeader, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, MultiDisputeStatementSet, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, PvfExecKind, SessionIndex, SessionInfo, - SignedAvailabilityBitfield, SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, CandidateIndex, + CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, + ExecutorParams, GroupIndex, GroupRotationInfo, Hash, HeadData, Header as BlockHeader, + Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, + NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, PvfExecKind, + SessionIndex, SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -879,7 +879,7 @@ pub enum CollationGenerationMessage { SubmitCollation(SubmitCollationParams), } -/// The result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request. +/// The result type of [`ApprovalVotingMessage::ImportAssignment`] request. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AssignmentCheckResult { /// The vote was accepted and should be propagated onwards. @@ -892,7 +892,7 @@ pub enum AssignmentCheckResult { Bad(AssignmentCheckError), } -/// The error result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request. +/// The error result type of [`ApprovalVotingMessage::ImportAssignment`] request. #[derive(Error, Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum AssignmentCheckError { @@ -912,7 +912,7 @@ pub enum AssignmentCheckError { InvalidBitfield(usize), } -/// The result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. +/// The result type of [`ApprovalVotingMessage::ImportApproval`] request. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ApprovalCheckResult { /// The vote was accepted and should be propagated onwards. @@ -921,7 +921,7 @@ pub enum ApprovalCheckResult { Bad(ApprovalCheckError), } -/// The error result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. +/// The error result type of [`ApprovalVotingMessage::ImportApproval`] request. #[derive(Error, Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum ApprovalCheckError { @@ -969,21 +969,68 @@ pub struct HighestApprovedAncestorBlock { pub descriptions: Vec, } +/// A checked indirect assignment, the crypto for the cert has been validated +/// and the `candidate_bitfield` is correctly claimed at `delay_tranche`. +#[derive(Debug)] +pub struct CheckedIndirectAssignment { + assignment: IndirectAssignmentCertV2, + candidate_indices: CandidateBitfield, + tranche: DelayTranche, +} + +impl CheckedIndirectAssignment { + /// Builds a checked assignment from an assignment that was checked to be valid for the + /// `claimed_candidate_indices` at the give tranche + pub fn from_checked( + assignment: IndirectAssignmentCertV2, + claimed_candidate_indices: CandidateBitfield, + tranche: DelayTranche, + ) -> Self { + Self { assignment, candidate_indices: claimed_candidate_indices, tranche } + } + + /// Returns the indirect assignment. + pub fn assignment(&self) -> &IndirectAssignmentCertV2 { + &self.assignment + } + + /// Returns the candidate bitfield claimed by the assignment. + pub fn candidate_indices(&self) -> &CandidateBitfield { + &self.candidate_indices + } + + /// Returns the tranche this assignment is claimed at. + pub fn tranche(&self) -> DelayTranche { + self.tranche + } +} + +/// A checked indirect signed approval vote. +/// +/// The crypto for the vote has been validated and the signature can be trusted as being valid and +/// to correspond to the `validator_index` inside the structure. +#[derive(Debug, derive_more::Deref, derive_more::Into)] +pub struct CheckedIndirectSignedApprovalVote(IndirectSignedApprovalVoteV2); + +impl CheckedIndirectSignedApprovalVote { + /// Builds a checked vote from a vote that was checked to be valid and correctly signed. + pub fn from_checked(vote: IndirectSignedApprovalVoteV2) -> Self { + Self(vote) + } +} + /// Message to the Approval Voting subsystem. #[derive(Debug)] pub enum ApprovalVotingMessage { - /// Check if the assignment is valid and can be accepted by our view of the protocol. - /// Should not be sent unless the block hash is known. - CheckAndImportAssignment( - IndirectAssignmentCertV2, - CandidateBitfield, - oneshot::Sender, - ), - /// Check if the approval vote is valid and can be accepted by our view of the - /// protocol. + /// Import an assignment into the approval-voting database. + /// + /// Should not be sent unless the block hash is known and the VRF assignment checks out. + ImportAssignment(CheckedIndirectAssignment, Option>), + /// Import an approval vote into approval-voting database /// - /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval(IndirectSignedApprovalVoteV2, oneshot::Sender), + /// Should not be sent unless the block hash within the indirect vote is known, vote is + /// correctly signed and we had a previous assignment for the candidate. + ImportApproval(CheckedIndirectSignedApprovalVote, Option>), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. /// The `BlockNumber` provided is the number of the block's ancestor which is the @@ -1126,6 +1173,32 @@ impl HypotheticalCandidate { HypotheticalCandidate::Incomplete { .. } => None, } } + + /// Get the candidate commitments, if the candidate is complete. + pub fn commitments(&self) -> Option<&CandidateCommitments> { + match *self { + HypotheticalCandidate::Complete { ref receipt, .. } => Some(&receipt.commitments), + HypotheticalCandidate::Incomplete { .. } => None, + } + } + + /// Get the persisted validation data, if the candidate is complete. + pub fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + match *self { + HypotheticalCandidate::Complete { ref persisted_validation_data, .. } => + Some(persisted_validation_data), + HypotheticalCandidate::Incomplete { .. } => None, + } + } + + /// Get the validation code hash, if the candidate is complete. + pub fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + match *self { + HypotheticalCandidate::Complete { ref receipt, .. } => + Some(&receipt.descriptor.validation_code_hash), + HypotheticalCandidate::Incomplete { .. } => None, + } + } } /// Request specifying which candidates are either already included diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index e5e1e4d24ef96e8fa7689a41cd7897832b5aa271..7938223df23b38091832c9716c121bbbc0893006 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -380,7 +380,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().availability_cores(at) + self.client + .runtime_api() + .availability_cores(at) + .map(|cores| cores.into_iter().map(|core| core.into()).collect::>()) } async fn persisted_validation_data( @@ -433,7 +436,10 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidate_pending_availability(at, para_id) + self.client + .runtime_api() + .candidate_pending_availability(at, para_id) + .map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into())) } async fn candidates_pending_availability( @@ -441,11 +447,19 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidates_pending_availability(at, para_id) + self.client + .runtime_api() + .candidates_pending_availability(at, para_id) + .map(|candidates| { + candidates.into_iter().map(|candidate| candidate.into()).collect::>() + }) } async fn candidate_events(&self, at: Hash) -> Result>, ApiError> { - self.client.runtime_api().candidate_events(at) + self.client + .runtime_api() + .candidate_events(at) + .map(|events| events.into_iter().map(|event| event.into()).collect::>()) } async fn dmq_contents( @@ -476,7 +490,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().on_chain_votes(at) + self.client + .runtime_api() + .on_chain_votes(at) + .map(|maybe_votes| maybe_votes.map(|votes| votes.into())) } async fn session_executor_params( @@ -588,7 +605,12 @@ where at: Hash, para_id: Id, ) -> Result, ApiError> { - self.client.runtime_api().para_backing_state(at, para_id) + self.client + .runtime_api() + .para_backing_state(at, para_id) + .map(|maybe_backing_state| { + maybe_backing_state.map(|backing_state| backing_state.into()) + }) } async fn async_backing_params( diff --git a/polkadot/node/subsystem-util/src/backing_implicit_view.rs b/polkadot/node/subsystem-util/src/backing_implicit_view.rs index 23a758d25715bbd33251b5edf8835ca3625f3ffa..a805ef8165e5fe108cf292c4eff04bf8d0f7e486 100644 --- a/polkadot/node/subsystem-util/src/backing_implicit_view.rs +++ b/polkadot/node/subsystem-util/src/backing_implicit_view.rs @@ -25,6 +25,7 @@ use polkadot_primitives::{BlockNumber, Hash, Id as ParaId}; use std::collections::HashMap; use crate::{ + inclusion_emulator::RelayChainBlockInfo, request_session_index_for_child, runtime::{self, prospective_parachains_mode, recv_runtime, ProspectiveParachainsMode}, }; @@ -121,6 +122,26 @@ struct BlockInfo { parent_hash: Hash, } +/// Information about a relay-chain block, to be used when calling this module from prospective +/// parachains. +#[derive(Debug, Clone, PartialEq)] +pub struct BlockInfoProspectiveParachains { + /// The hash of the relay-chain block. + pub hash: Hash, + /// The hash of the parent relay-chain block. + pub parent_hash: Hash, + /// The number of the relay-chain block. + pub number: BlockNumber, + /// The storage-root of the relay-chain block. + pub storage_root: Hash, +} + +impl From for RelayChainBlockInfo { + fn from(value: BlockInfoProspectiveParachains) -> Self { + Self { hash: value.hash, number: value.number, storage_root: value.storage_root } + } +} + impl View { /// Get an iterator over active leaves in the view. pub fn leaves(&self) -> impl Iterator { @@ -178,6 +199,61 @@ impl View { } } + /// Activate a leaf in the view. To be used by the prospective parachains subsystem. + /// + /// This will not request any additional data, as prospective parachains already provides all + /// the required info. + /// NOTE: using `activate_leaf` instead of this function will result in a + /// deadlock, as it calls prospective-parachains under the hood. + /// + /// No-op for known leaves. + pub fn activate_leaf_from_prospective_parachains( + &mut self, + leaf: BlockInfoProspectiveParachains, + ancestors: &[BlockInfoProspectiveParachains], + ) { + if self.leaves.contains_key(&leaf.hash) { + return + } + + // Retain at least `MINIMUM_RETAIN_LENGTH` blocks in storage. + // This helps to avoid Chain API calls when activating leaves in the + // same chain. + let retain_minimum = std::cmp::min( + ancestors.last().map(|a| a.number).unwrap_or(0), + leaf.number.saturating_sub(MINIMUM_RETAIN_LENGTH), + ); + + self.leaves.insert(leaf.hash, ActiveLeafPruningInfo { retain_minimum }); + let mut allowed_relay_parents = AllowedRelayParents { + allowed_relay_parents_contiguous: Vec::with_capacity(ancestors.len()), + // In this case, initialise this to an empty map, as prospective parachains already has + // this data and it won't query the implicit view for it. + minimum_relay_parents: HashMap::new(), + }; + + for ancestor in ancestors { + self.block_info_storage.insert( + ancestor.hash, + BlockInfo { + block_number: ancestor.number, + maybe_allowed_relay_parents: None, + parent_hash: ancestor.parent_hash, + }, + ); + allowed_relay_parents.allowed_relay_parents_contiguous.push(ancestor.hash); + } + + self.block_info_storage.insert( + leaf.hash, + BlockInfo { + block_number: leaf.number, + maybe_allowed_relay_parents: Some(allowed_relay_parents), + parent_hash: leaf.parent_hash, + }, + ); + } + /// Deactivate a leaf in the view. This prunes any outdated implicit ancestors as well. /// /// Returns hashes of blocks pruned from storage. diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index 2272f048089c7e7f5bda0ce9a2f970789e5c9b34..0c3b40743495c0d219b79a1a3e19f915a1c59fed 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -39,8 +39,8 @@ /// /// # Usage /// -/// It's expected that the users of this module will be building up chains of -/// [`Fragment`]s and consistently pruning and adding to the chains. +/// It's expected that the users of this module will be building up chains or trees of +/// [`Fragment`]s and consistently pruning and adding to them. /// /// ## Operating Constraints /// @@ -56,55 +56,19 @@ /// /// ## Fragment Chains /// -/// For simplicity and practicality, we expect that collators of the same parachain are -/// cooperating and don't create parachain forks or cycles on the same relay chain active leaf. -/// Therefore, higher-level code should maintain one fragment chain for each active leaf (not a -/// fragment tree). If parachains do create forks, their performance in regards to async -/// backing and elastic scaling will suffer, because different validators will have different -/// predictions of the future. +/// For the sake of this module, we don't care how higher-level code is managing parachain +/// fragments, whether or not they're kept as a chain or tree. In reality, +/// prospective-parachains is maintaining for every active leaf, a chain of the "best" backable +/// candidates and a storage of potential candidates which may be added to this chain in the +/// future. /// /// As the relay-chain grows, some predictions come true and others come false. -/// And new predictions get made. These three changes correspond distinctly to the -/// 3 primary operations on fragment chains. +/// And new predictions get made. Higher-level code is responsible for adding and pruning the +/// fragments chains. /// /// Avoiding fragment-chain blowup is beyond the scope of this module. Higher-level must ensure /// proper spam protection. /// -/// ### Pruning Fragment Chains -/// -/// When the relay-chain advances, we want to compare the new constraints of that relay-parent -/// to the root of the fragment chain we have. There are 3 cases: -/// -/// 1. The root fragment is still valid under the new constraints. In this case, we do nothing. -/// This is the "prediction still uncertain" case. (Corresponds to some candidates still -/// being pending availability). -/// -/// 2. The root fragment (potentially along with a number of descendants) is invalid under the -/// new constraints because it has been included by the relay-chain. In this case, we can -/// discard the included chain and split & re-root the chain under its descendants and -/// compare to the new constraints again. This is the "prediction came true" case. -/// -/// 3. The root fragment becomes invalid under the new constraints for any reason (if for -/// example the parachain produced a fork and the block producer picked a different -/// candidate to back). In this case we can discard the entire fragment chain. This is the -/// "prediction came false" case. -/// -/// This is all a bit of a simplification because it assumes that the relay-chain advances -/// without forks and is finalized instantly. In practice, the set of fragment-chains needs to -/// be observable from the perspective of a few different possible forks of the relay-chain and -/// not pruned too eagerly. -/// -/// Note that the fragments themselves don't need to change and the only thing we care about -/// is whether the predictions they represent are still valid. -/// -/// ### Extending Fragment Chains -/// -/// As predictions fade into the past, new ones should be stacked on top. -/// -/// Every new relay-chain block is an opportunity to make a new prediction about the future. -/// Higher-level logic should decide whether to build upon an existing chain or whether -/// to create a new fragment-chain. -/// /// ### Code Upgrades /// /// Code upgrades are the main place where this emulation fails. The on-chain PVF upgrade @@ -116,9 +80,11 @@ /// /// That means a few blocks of execution time lost, which is not a big deal for code upgrades /// in practice at most once every few weeks. +use polkadot_node_subsystem::messages::HypotheticalCandidate; use polkadot_primitives::{ - async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, Hash, - HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, ValidationCodeHash, + async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, + CandidateHash, Hash, HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, + ValidationCodeHash, }; use std::{collections::HashMap, sync::Arc}; @@ -702,6 +668,11 @@ impl Fragment { &self.candidate } + /// Get a cheap ref-counted copy of the underlying prospective candidate. + pub fn candidate_clone(&self) -> Arc { + self.candidate.clone() + } + /// Modifications to constraints based on the outputs of the candidate. pub fn constraint_modifications(&self) -> &ConstraintModifications { &self.modifications @@ -791,6 +762,55 @@ fn validate_against_constraints( .map_err(FragmentValidityError::OutputsInvalid) } +/// Trait for a hypothetical or concrete candidate, as needed when assessing the validity of a +/// potential candidate. +pub trait HypotheticalOrConcreteCandidate { + /// Return a reference to the candidate commitments, if present. + fn commitments(&self) -> Option<&CandidateCommitments>; + /// Return a reference to the persisted validation data, if present. + fn persisted_validation_data(&self) -> Option<&PersistedValidationData>; + /// Return a reference to the validation code hash, if present. + fn validation_code_hash(&self) -> Option<&ValidationCodeHash>; + /// Return the parent head hash. + fn parent_head_data_hash(&self) -> Hash; + /// Return the output head hash, if present. + fn output_head_data_hash(&self) -> Option; + /// Return the relay parent hash. + fn relay_parent(&self) -> Hash; + /// Return the candidate hash. + fn candidate_hash(&self) -> CandidateHash; +} + +impl HypotheticalOrConcreteCandidate for HypotheticalCandidate { + fn commitments(&self) -> Option<&CandidateCommitments> { + self.commitments() + } + + fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + self.persisted_validation_data() + } + + fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + self.validation_code_hash() + } + + fn parent_head_data_hash(&self) -> Hash { + self.parent_head_data_hash() + } + + fn output_head_data_hash(&self) -> Option { + self.output_head_data_hash() + } + + fn relay_parent(&self) -> Hash { + self.relay_parent() + } + + fn candidate_hash(&self) -> CandidateHash { + self.candidate_hash() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index 92f2cd189054dc2c26430e85448bbf51f88f4ee7..4bab4e80fe509dd9b0d15189773265ac374ccca8 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -49,6 +49,7 @@ use polkadot_primitives::{ ValidatorSignature, }; pub use rand; +use runtime::get_disabled_validators_with_fallback; use sp_application_crypto::AppCrypto; use sp_core::ByteArray; use sp_keystore::{Error as KeystoreError, KeystorePtr}; @@ -57,7 +58,6 @@ use std::{ time::Duration, }; use thiserror::Error; -use vstaging::get_disabled_validators_with_fallback; pub use determine_new_blocks::determine_new_blocks; pub use metered; diff --git a/polkadot/node/subsystem-util/src/runtime/mod.rs b/polkadot/node/subsystem-util/src/runtime/mod.rs index 2c9ec8db3778bbe31981d38d3ba4b4b1e426ccc5..2f9d3ed7b4f40cb27e9061bb6fadd4749a4f3173 100644 --- a/polkadot/node/subsystem-util/src/runtime/mod.rs +++ b/polkadot/node/subsystem-util/src/runtime/mod.rs @@ -32,17 +32,20 @@ use polkadot_node_subsystem_types::UnpinHandle; use polkadot_primitives::{ node_features::FeatureIndex, slashing, AsyncBackingParams, CandidateEvent, CandidateHash, CoreIndex, CoreState, EncodeAs, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, - IndexedVec, NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, - SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorId, - ValidatorIndex, LEGACY_MIN_BACKING_VOTES, + Id as ParaId, IndexedVec, NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, + SessionInfo, Signed, SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, LEGACY_MIN_BACKING_VOTES, }; +use std::collections::{BTreeMap, VecDeque}; + use crate::{ - request_async_backing_params, request_availability_cores, request_candidate_events, + has_required_runtime, request_async_backing_params, request_availability_cores, + request_candidate_events, request_claim_queue, request_disabled_validators, request_from_runtime, request_key_ownership_proof, request_on_chain_votes, request_session_executor_params, request_session_index_for_child, request_session_info, request_submit_report_dispute_lost, request_unapplied_slashes, request_validation_code_by_hash, - request_validator_groups, vstaging::get_disabled_validators_with_fallback, + request_validator_groups, }; /// Errors that can happen on runtime fetches. @@ -579,3 +582,98 @@ pub async fn request_node_features( res.map(Some) } } + +/// A snapshot of the runtime claim queue at an arbitrary relay chain block. +#[derive(Default)] +pub struct ClaimQueueSnapshot(pub BTreeMap>); + +impl From>> for ClaimQueueSnapshot { + fn from(claim_queue_snapshot: BTreeMap>) -> Self { + ClaimQueueSnapshot(claim_queue_snapshot) + } +} + +impl ClaimQueueSnapshot { + /// Returns the `ParaId` that has a claim for `core_index` at the specified `depth` in the + /// claim queue. A depth of `0` means the very next block. + pub fn get_claim_for(&self, core_index: CoreIndex, depth: usize) -> Option { + self.0.get(&core_index)?.get(depth).copied() + } + + /// Returns an iterator over all claimed cores and the claiming `ParaId` at the specified + /// `depth` in the claim queue. + pub fn iter_claims_at_depth( + &self, + depth: usize, + ) -> impl Iterator + '_ { + self.0 + .iter() + .filter_map(move |(core_index, paras)| Some((*core_index, *paras.get(depth)?))) + } + + /// Returns an iterator over all claims on the given core. + pub fn iter_claims_for_core( + &self, + core_index: &CoreIndex, + ) -> impl Iterator + '_ { + self.0.get(core_index).map(|c| c.iter()).into_iter().flatten() + } + + /// Returns an iterator over the whole claim queue. + pub fn iter_all_claims(&self) -> impl Iterator)> + '_ { + self.0.iter() + } +} + +// TODO: https://github.com/paritytech/polkadot-sdk/issues/1940 +/// Returns disabled validators list if the runtime supports it. Otherwise logs a debug messages and +/// returns an empty vec. +/// Once runtime ver `DISABLED_VALIDATORS_RUNTIME_REQUIREMENT` is released remove this function and +/// replace all usages with `request_disabled_validators` +pub async fn get_disabled_validators_with_fallback>( + sender: &mut Sender, + relay_parent: Hash, +) -> Result> { + let disabled_validators = if has_required_runtime( + sender, + relay_parent, + RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT, + ) + .await + { + request_disabled_validators(relay_parent, sender) + .await + .await + .map_err(Error::RuntimeRequestCanceled)?? + } else { + gum::debug!(target: LOG_TARGET, "Runtime doesn't support `DisabledValidators` - continuing with an empty disabled validators set"); + vec![] + }; + + Ok(disabled_validators) +} + +/// Checks if the runtime supports `request_claim_queue` and attempts to fetch the claim queue. +/// Returns `ClaimQueueSnapshot` or `None` if claim queue API is not supported by runtime. +/// Any specific `RuntimeApiError` is bubbled up to the caller. +pub async fn fetch_claim_queue( + sender: &mut impl SubsystemSender, + relay_parent: Hash, +) -> Result> { + if has_required_runtime( + sender, + relay_parent, + RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, + ) + .await + { + let res = request_claim_queue(relay_parent, sender) + .await + .await + .map_err(Error::RuntimeRequestCanceled)??; + Ok(Some(res.into())) + } else { + gum::trace!(target: LOG_TARGET, "Runtime doesn't support `request_claim_queue`"); + Ok(None) + } +} diff --git a/polkadot/node/subsystem-util/src/vstaging.rs b/polkadot/node/subsystem-util/src/vstaging.rs index b6cd73f412b33a10c814be9813e0cd14bd843934..b608bb416ff649a56a2aa6ef7131ebf77dcc0b8e 100644 --- a/polkadot/node/subsystem-util/src/vstaging.rs +++ b/polkadot/node/subsystem-util/src/vstaging.rs @@ -18,108 +18,3 @@ //! //! This module is intended to contain common boiler plate code handling unreleased runtime API //! calls. - -use std::collections::{BTreeMap, VecDeque}; - -use polkadot_node_subsystem_types::messages::{RuntimeApiMessage, RuntimeApiRequest}; -use polkadot_overseer::SubsystemSender; -use polkadot_primitives::{CoreIndex, Hash, Id as ParaId, ValidatorIndex}; - -use crate::{has_required_runtime, request_claim_queue, request_disabled_validators, runtime}; - -const LOG_TARGET: &'static str = "parachain::subsystem-util-vstaging"; - -/// A snapshot of the runtime claim queue at an arbitrary relay chain block. -#[derive(Default)] -pub struct ClaimQueueSnapshot(pub BTreeMap>); - -impl From>> for ClaimQueueSnapshot { - fn from(claim_queue_snapshot: BTreeMap>) -> Self { - ClaimQueueSnapshot(claim_queue_snapshot) - } -} - -impl ClaimQueueSnapshot { - /// Returns the `ParaId` that has a claim for `core_index` at the specified `depth` in the - /// claim queue. A depth of `0` means the very next block. - pub fn get_claim_for(&self, core_index: CoreIndex, depth: usize) -> Option { - self.0.get(&core_index)?.get(depth).copied() - } - - /// Returns an iterator over all claimed cores and the claiming `ParaId` at the specified - /// `depth` in the claim queue. - pub fn iter_claims_at_depth( - &self, - depth: usize, - ) -> impl Iterator + '_ { - self.0 - .iter() - .filter_map(move |(core_index, paras)| Some((*core_index, *paras.get(depth)?))) - } - - /// Returns an iterator over all claims on the given core. - pub fn iter_claims_for_core( - &self, - core_index: &CoreIndex, - ) -> impl Iterator + '_ { - self.0.get(core_index).map(|c| c.iter()).into_iter().flatten() - } - - /// Returns an iterator over the whole claim queue. - pub fn iter_all_claims(&self) -> impl Iterator)> + '_ { - self.0.iter() - } -} - -// TODO: https://github.com/paritytech/polkadot-sdk/issues/1940 -/// Returns disabled validators list if the runtime supports it. Otherwise logs a debug messages and -/// returns an empty vec. -/// Once runtime ver `DISABLED_VALIDATORS_RUNTIME_REQUIREMENT` is released remove this function and -/// replace all usages with `request_disabled_validators` -pub async fn get_disabled_validators_with_fallback>( - sender: &mut Sender, - relay_parent: Hash, -) -> Result, runtime::Error> { - let disabled_validators = if has_required_runtime( - sender, - relay_parent, - RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT, - ) - .await - { - request_disabled_validators(relay_parent, sender) - .await - .await - .map_err(runtime::Error::RuntimeRequestCanceled)?? - } else { - gum::debug!(target: LOG_TARGET, "Runtime doesn't support `DisabledValidators` - continuing with an empty disabled validators set"); - vec![] - }; - - Ok(disabled_validators) -} - -/// Checks if the runtime supports `request_claim_queue` and attempts to fetch the claim queue. -/// Returns `ClaimQueueSnapshot` or `None` if claim queue API is not supported by runtime. -/// Any specific `RuntimeApiError` is bubbled up to the caller. -pub async fn fetch_claim_queue( - sender: &mut impl SubsystemSender, - relay_parent: Hash, -) -> Result, runtime::Error> { - if has_required_runtime( - sender, - relay_parent, - RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT, - ) - .await - { - let res = request_claim_queue(relay_parent, sender) - .await - .await - .map_err(runtime::Error::RuntimeRequestCanceled)??; - Ok(Some(res.into())) - } else { - gum::trace!(target: LOG_TARGET, "Runtime doesn't support `request_claim_queue`"); - Ok(None) - } -} diff --git a/polkadot/node/test/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/client/src/lib.rs b/polkadot/node/test/client/src/lib.rs index 6b205c09f2f3297cb8b53bfb62cc80a1ed25c3a7..498994d9a0a88bdc91b197949f2bcec8c424bdbb 100644 --- a/polkadot/node/test/client/src/lib.rs +++ b/polkadot/node/test/client/src/lib.rs @@ -102,7 +102,7 @@ mod tests { #[test] fn ensure_test_client_can_build_and_import_block() { - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let block_builder = client.init_polkadot_block_builder(); let block = block_builder.build().expect("Finalizes the block").block; @@ -113,7 +113,7 @@ mod tests { #[test] fn ensure_test_client_can_push_extrinsic() { - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let transfer = construct_transfer_extrinsic( &client, diff --git a/polkadot/node/test/service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs index bd53fd843c693514b91f9ebda445a56675fbf27a..8add51b07521f4bbde67009d081b8fec0fd421ee 100644 --- a/polkadot/node/test/service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -18,7 +18,7 @@ use pallet_staking::Forcing; use polkadot_primitives::{ - vstaging::SchedulerParams, AccountId, AssignmentId, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE, + AccountId, AssignmentId, SchedulerParams, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE, }; use polkadot_service::chain_spec::{get_account_id_from_seed, get_from_seed, Extensions}; use polkadot_test_runtime::BABE_GENESIS_EPOCH_CONFIG; diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index a4e58253bb1706c9face2ded9dbbfb66a93ebb40..b12387884861fd5694bc82884a8543c379ead50a 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -68,6 +68,7 @@ use substrate_test_client::{ pub type Client = FullClient; pub use polkadot_service::{FullBackend, GetLastTimestamp}; +use sc_service::config::{ExecutorConfiguration, RpcConfiguration}; /// Create a new full node. #[sc_tracing::logging::prefix_logs_with(config.network.node_name.as_str())] @@ -200,35 +201,37 @@ pub fn node_config( state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, chain_spec: Box::new(spec), - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() }, wasm_runtime_overrides: Default::default(), - rpc_addr: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_id_provider: None, - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + max_connections: Default::default(), + cors: None, + methods: Default::default(), + id_provider: None, + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: Some(key_seed), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: root, base_path, diff --git a/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/lib.rs b/polkadot/primitives/src/lib.rs index 73736fd4a3d6b7fc6f164952fbbc6a49f139de88..493f9fb5ba92ee66320ad6ead60e5bb1111213b9 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -19,8 +19,8 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -// `v6` is currently the latest stable version of the runtime API. -pub mod v7; +// `v11` is currently the latest stable version of the runtime API. +pub mod v8; // The 'staging' version is special - it contains primitives which are // still in development. Once they are considered stable, they will be @@ -35,7 +35,7 @@ extern crate alloc; // Current primitives not requiring versioning are exported here. // Primitives requiring versioning must not be exported and must be referred by an exact version. -pub use v7::{ +pub use v8::{ async_backing, byzantine_threshold, check_candidate_backing, collator_signature_payload, effective_minimum_backing_votes, executor_params, metric_definitions, node_features, slashing, supermajority_threshold, well_known_keys, AbridgedHostConfiguration, AbridgedHrmpChannel, @@ -54,16 +54,17 @@ pub use v7::{ OutboundHrmpMessage, ParathreadClaim, ParathreadEntry, PersistedValidationData, PvfCheckStatement, PvfExecKind, PvfPrepKind, RuntimeMetricLabel, RuntimeMetricLabelValue, RuntimeMetricLabelValues, RuntimeMetricLabels, RuntimeMetricOp, RuntimeMetricUpdate, - ScheduledCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signature, Signed, - SignedAvailabilityBitfield, SignedAvailabilityBitfields, SignedStatement, SigningContext, Slot, - UncheckedSigned, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - UncheckedSignedStatement, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, - ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, ValidityAttestation, ValidityError, ASSIGNMENT_KEY_TYPE_ID, - LEGACY_MIN_BACKING_VOTES, LOWEST_PUBLIC_ID, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, - MIN_CODE_SIZE, ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, ON_DEMAND_MAX_QUEUE_MAX_SIZE, - PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, + ScheduledCore, SchedulerParams, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signature, + Signed, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SignedStatement, + SigningContext, Slot, UncheckedSigned, UncheckedSignedAvailabilityBitfield, + UncheckedSignedAvailabilityBitfields, UncheckedSignedStatement, UpgradeGoAhead, + UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, + ValidityError, ASSIGNMENT_KEY_TYPE_ID, LEGACY_MIN_BACKING_VOTES, LOWEST_PUBLIC_ID, + MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, MIN_CODE_SIZE, + ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, ON_DEMAND_MAX_QUEUE_MAX_SIZE, PARACHAINS_INHERENT_IDENTIFIER, + PARACHAIN_KEY_TYPE_ID, }; #[cfg(feature = "std")] -pub use v7::{AssignmentPair, CollatorPair, ValidatorPair}; +pub use v8::{AssignmentPair, CollatorPair, ValidatorPair}; diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index b4816ad15075dcc8d739259b779bfda1a9b5c84a..ddebe99e6214e4393e7ea589ecaa5203f917dcd9 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -114,11 +114,15 @@ //! separated from the stable primitives. use crate::{ - async_backing, slashing, ApprovalVotingParams, AsyncBackingParams, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + self, CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AsyncBackingParams, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use alloc::{ @@ -260,7 +264,7 @@ sp_api::decl_runtime_apis! { /// Returns the state of parachain backing for a given para. #[api_version(7)] - fn para_backing_state(_: ppp::Id) -> Option>; + fn para_backing_state(_: ppp::Id) -> Option>; /// Returns candidate's acceptance limitations for asynchronous backing for a relay parent. #[api_version(7)] diff --git a/polkadot/primitives/src/v7/async_backing.rs b/polkadot/primitives/src/v8/async_backing.rs similarity index 100% rename from polkadot/primitives/src/v7/async_backing.rs rename to polkadot/primitives/src/v8/async_backing.rs diff --git a/polkadot/primitives/src/v7/executor_params.rs b/polkadot/primitives/src/v8/executor_params.rs similarity index 100% rename from polkadot/primitives/src/v7/executor_params.rs rename to polkadot/primitives/src/v8/executor_params.rs diff --git a/polkadot/primitives/src/v7/metrics.rs b/polkadot/primitives/src/v8/metrics.rs similarity index 100% rename from polkadot/primitives/src/v7/metrics.rs rename to polkadot/primitives/src/v8/metrics.rs diff --git a/polkadot/primitives/src/v7/mod.rs b/polkadot/primitives/src/v8/mod.rs similarity index 96% rename from polkadot/primitives/src/v7/mod.rs rename to polkadot/primitives/src/v8/mod.rs index a729d8cee4c7eaa7b654c018f115b17457e17256..a51ee0bd99bfe9d2c8db17b1db5cdfadce489277 100644 --- a/polkadot/primitives/src/v7/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}, @@ -29,7 +28,10 @@ use core::{ use scale_info::TypeInfo; use sp_application_crypto::KeyTypeId; -use sp_arithmetic::traits::{BaseArithmetic, Saturating}; +use sp_arithmetic::{ + traits::{BaseArithmetic, Saturating}, + Perbill, +}; use sp_core::RuntimeDebug; use sp_inherents::InherentIdentifier; use sp_runtime::traits::{AppVerify, Header as HeaderT}; @@ -1154,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 { @@ -2051,8 +2053,83 @@ pub mod node_features { } } +/// Scheduler configuration parameters. All coretime/ondemand parameters are here. +#[derive( + RuntimeDebug, + Copy, + Clone, + PartialEq, + Encode, + Decode, + TypeInfo, + serde::Serialize, + serde::Deserialize, +)] +pub struct SchedulerParams { + /// How often parachain groups should be rotated across parachains. + /// + /// Must be non-zero. + pub group_rotation_frequency: BlockNumber, + /// Availability timeout for a block on a core, measured in blocks. + /// + /// This is the maximum amount of blocks after a core became occupied that validators have time + /// to make the block available. + /// + /// This value only has effect on group rotations. If backers backed something at the end of + /// their rotation, the occupied core affects the backing group that comes afterwards. We limit + /// the effect one backing group can have on the next to `paras_availability_period` blocks. + /// + /// Within a group rotation there is no timeout as backers are only affecting themselves. + /// + /// Must be at least 1. With a value of 1, the previous group will not be able to negatively + /// affect the following group at the expense of a tight availability timeline at group + /// rotation boundaries. + pub paras_availability_period: BlockNumber, + /// The maximum number of validators to have per core. + /// + /// `None` means no maximum. + pub max_validators_per_core: Option, + /// The amount of blocks ahead to schedule paras. + pub lookahead: u32, + /// How many cores are managed by the coretime chain. + pub num_cores: u32, + /// The max number of times a claim can time out in availability. + pub max_availability_timeouts: u32, + /// The maximum queue size of the pay as you go module. + pub on_demand_queue_max_size: u32, + /// The target utilization of the spot price queue in percentages. + pub on_demand_target_queue_utilization: Perbill, + /// How quickly the fee rises in reaction to increased utilization. + /// The lower the number the slower the increase. + pub on_demand_fee_variability: Perbill, + /// The minimum amount needed to claim a slot in the spot pricing queue. + pub on_demand_base_fee: Balance, + /// The number of blocks a claim stays in the scheduler's claim queue before getting cleared. + /// This number should go reasonably higher than the number of blocks in the async backing + /// lookahead. + pub ttl: BlockNumber, +} + +impl> Default for SchedulerParams { + fn default() -> Self { + Self { + group_rotation_frequency: 1u32.into(), + paras_availability_period: 1u32.into(), + max_validators_per_core: Default::default(), + lookahead: 1, + num_cores: Default::default(), + max_availability_timeouts: Default::default(), + on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, + on_demand_target_queue_utilization: Perbill::from_percent(25), + on_demand_fee_variability: Perbill::from_percent(3), + on_demand_base_fee: 10_000_000u128, + ttl: 5u32.into(), + } + } +} + #[cfg(test)] -mod tests { +pub mod tests { use super::*; use bitvec::bitvec; use sp_core::sr25519; diff --git a/polkadot/primitives/src/v7/signed.rs b/polkadot/primitives/src/v8/signed.rs similarity index 100% rename from polkadot/primitives/src/v7/signed.rs rename to polkadot/primitives/src/v8/signed.rs diff --git a/polkadot/primitives/src/v7/slashing.rs b/polkadot/primitives/src/v8/slashing.rs similarity index 100% rename from polkadot/primitives/src/v7/slashing.rs rename to polkadot/primitives/src/v8/slashing.rs diff --git a/polkadot/primitives/src/vstaging/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 27296213e6118c8216b15898d2fbb3bc29550b42..57cba85c10d945079618a4ed7344c8066c03a147 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,86 +15,913 @@ // along with Polkadot. If not, see . //! Staging Primitives. +use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here -use crate::v7::*; +use super::{ + async_backing::Constraints, BlakeTwo256, BlockNumber, CandidateCommitments, + CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash, + HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, + UncheckedSignedAvailabilityBitfields, ValidationCodeHash, +}; +use bitvec::prelude::*; +use sp_application_crypto::ByteArray; +use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_arithmetic::Perbill; use sp_core::RuntimeDebug; +use sp_runtime::traits::Header as HeaderT; +use sp_staking::SessionIndex; +/// Async backing primitives +pub mod async_backing; -/// Scheduler configuration parameters. All coretime/ondemand parameters are here. -#[derive( - RuntimeDebug, - Copy, - Clone, - PartialEq, - Encode, - Decode, - TypeInfo, - serde::Serialize, - serde::Deserialize, -)] -pub struct SchedulerParams { - /// How often parachain groups should be rotated across parachains. - /// - /// Must be non-zero. - pub group_rotation_frequency: BlockNumber, - /// Availability timeout for a block on a core, measured in blocks. - /// - /// This is the maximum amount of blocks after a core became occupied that validators have time - /// to make the block available. - /// - /// This value only has effect on group rotations. If backers backed something at the end of - /// their rotation, the occupied core affects the backing group that comes afterwards. We limit - /// the effect one backing group can have on the next to `paras_availability_period` blocks. - /// - /// Within a group rotation there is no timeout as backers are only affecting themselves. +/// 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. /// - /// Must be at least 1. With a value of 1, the previous group will not be able to negatively - /// affect the following group at the expense of a tight availability timeline at group - /// rotation boundaries. - pub paras_availability_period: BlockNumber, - /// The maximum number of validators to have per core. + /// This computes the canonical hash, not the hash of the directly encoded data. + /// Thus this is a shortcut for `candidate.to_plain().hash()`. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + self.to_plain().hash() + } + + /// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]? + pub fn corresponds_to(&self, receipt: &CandidateReceiptV2) -> bool + where + H: PartialEq, + { + receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash() + } +} + +impl PartialOrd for CommittedCandidateReceiptV2 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for CommittedCandidateReceiptV2 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.descriptor + .para_id + .cmp(&other.descriptor.para_id) + .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data)) + } +} + +impl From> for super::v8::CommittedCandidateReceipt { + fn from(value: CommittedCandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments: value.commitments } + } +} + +impl From> for super::v8::CandidateReceipt { + fn from(value: CandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash } + } +} + +/// A strictly increasing sequence number, typically this would be the least significant byte of the +/// block number. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct CoreSelector(pub u8); + +/// An offset in the relay chain claim queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct ClaimQueueOffset(pub u8); + +/// Signals that a parachain can send to the relay chain via the UMP queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum UMPSignal { + /// A message sent by a parachain to select the core the candidate is commited to. + /// Relay chain validators, in particular backers, use the `CoreSelector` and + /// `ClaimQueueOffset` to compute the index of the core the candidate has commited to. + SelectCore(CoreSelector, ClaimQueueOffset), +} +/// Separator between `XCM` and `UMPSignal`. +pub const UMP_SEPARATOR: Vec = vec![]; + +impl CandidateCommitments { + /// Returns the core selector and claim queue offset the candidate has committed to, if any. + pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { + // We need at least 2 messages for the separator and core selector + if self.upward_messages.len() < 2 { + return None + } + + let separator_pos = + self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?; + + // Use first commitment + let message = self.upward_messages.get(separator_pos + 1)?; + + match UMPSignal::decode(&mut message.as_slice()).ok()? { + UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)), + } + } +} + +/// CandidateReceipt construction errors. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum CandidateReceiptError { + /// The specified core index is invalid. + InvalidCoreIndex, + /// The core index in commitments doesn't match the one in descriptor + CoreIndexMismatch, + /// The core selector or claim queue offset is invalid. + InvalidSelectedCore, + /// The parachain is not assigned to any core at specified claim queue offset. + NoAssignment, + /// No core was selected. + NoCoreSelected, + /// Unknown version. + UnknownVersion(InternalVersion), +} + +macro_rules! impl_getter { + ($field:ident, $type:ident) => { + /// Returns the value of $field field. + pub fn $field(&self) -> $type { + self.$field + } + }; +} + +impl CandidateDescriptorV2 { + impl_getter!(erasure_root, Hash); + impl_getter!(para_head, Hash); + impl_getter!(relay_parent, H); + impl_getter!(para_id, ParaId); + impl_getter!(persisted_validation_data_hash, Hash); + impl_getter!(pov_hash, Hash); + impl_getter!(validation_code_hash, ValidationCodeHash); + + /// Returns the candidate descriptor version. + /// The candidate is at version 2 if the reserved fields are zeroed out + /// and the internal `version` field is 0. + pub fn version(&self) -> CandidateDescriptorVersion { + if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] { + return CandidateDescriptorVersion::V1 + } + + match self.version.0 { + 0 => CandidateDescriptorVersion::V2, + _ => CandidateDescriptorVersion::Unknown, + } + } + + fn rebuild_collator_field(&self) -> CollatorId { + let mut collator_id = Vec::with_capacity(32); + let core_index: [u8; 2] = self.core_index.to_ne_bytes(); + let session_index: [u8; 4] = self.session_index.to_ne_bytes(); + + collator_id.push(self.version.0); + collator_id.extend_from_slice(core_index.as_slice()); + collator_id.extend_from_slice(session_index.as_slice()); + collator_id.extend_from_slice(self.reserved1.as_slice()); + + CollatorId::from_slice(&collator_id.as_slice()) + .expect("Slice size is exactly 32 bytes; qed") + } + + /// Returns the collator id if this is a v1 `CandidateDescriptor` + pub fn collator(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + Some(self.rebuild_collator_field()) + } else { + None + } + } + + fn rebuild_signature_field(&self) -> CollatorSignature { + CollatorSignature::from_slice(self.reserved2.as_slice()) + .expect("Slice size is exactly 64 bytes; qed") + } + + /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. + pub fn signature(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return Some(self.rebuild_signature_field()) + } + + None + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn core_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(CoreIndex(self.core_index as u32)) + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn session_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(self.session_index) + } +} + +impl CommittedCandidateReceiptV2 { + /// Checks if descriptor core index is equal to the commited core index. + /// Input `assigned_cores` must contain the sorted cores assigned to the para at + /// the committed claim queue offset. + pub fn check(&self, assigned_cores: &[CoreIndex]) -> Result<(), CandidateReceiptError> { + // Don't check v1 descriptors. + if self.descriptor.version() == CandidateDescriptorVersion::V1 { + return Ok(()) + } + + if self.descriptor.version() == CandidateDescriptorVersion::Unknown { + return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)) + } + + if assigned_cores.is_empty() { + return Err(CandidateReceiptError::NoAssignment) + } + + let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + + let (core_selector, _cq_offset) = + self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?; + + let core_index = assigned_cores + .get(core_selector.0 as usize % assigned_cores.len()) + .ok_or(CandidateReceiptError::InvalidCoreIndex)?; + + if *core_index != descriptor_core_index { + return Err(CandidateReceiptError::CoreIndexMismatch) + } + + Ok(()) + } +} + +/// A backed (or backable, depending on context) candidate. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BackedCandidate { + /// The candidate referred to. + candidate: CommittedCandidateReceiptV2, + /// The validity votes themselves, expressed as signatures. + validity_votes: Vec, + /// The indices of the validators within the group, expressed as a bitfield. May be extended + /// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is + /// enabled. + validator_indices: BitVec, +} + +/// Parachains inherent-data passed into the runtime by a block author +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] +pub struct InherentData { + /// Signed bitfields by validators about availability. + pub bitfields: UncheckedSignedAvailabilityBitfields, + /// Backed candidates for inclusion in the block. + pub backed_candidates: Vec>, + /// Sets of dispute votes for inclusion, + pub disputes: MultiDisputeStatementSet, + /// The parent block header. Used for checking state proofs. + pub parent_header: HDR, +} + +impl BackedCandidate { + /// Constructor + pub fn new( + candidate: CommittedCandidateReceiptV2, + validity_votes: Vec, + validator_indices: BitVec, + core_index: Option, + ) -> Self { + let mut instance = Self { candidate, validity_votes, validator_indices }; + if let Some(core_index) = core_index { + instance.inject_core_index(core_index); + } + instance + } + + /// Get a reference to the committed candidate receipt of the candidate. + pub fn candidate(&self) -> &CommittedCandidateReceiptV2 { + &self.candidate + } + + /// Get a reference to the descriptor of the candidate. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.candidate.descriptor + } + + /// Get a mutable reference to the descriptor of the candidate. Only for testing. + #[cfg(feature = "test")] + pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2 { + &mut self.candidate.descriptor + } + + /// Get a reference to the validity votes of the candidate. + pub fn validity_votes(&self) -> &[ValidityAttestation] { + &self.validity_votes + } + + /// Get a mutable reference to validity votes of the para. + pub fn validity_votes_mut(&mut self) -> &mut Vec { + &mut self.validity_votes + } + + /// Compute this candidate's hash. + pub fn hash(&self) -> CandidateHash + where + H: Clone + Encode, + { + self.candidate.to_plain().hash() + } + + /// Get this candidate's receipt. + pub fn receipt(&self) -> CandidateReceiptV2 + where + H: Clone, + { + self.candidate.to_plain() + } + + /// Get a copy of the validator indices and the assumed core index, if any. + pub fn validator_indices_and_core_index( + &self, + core_index_enabled: bool, + ) -> (&BitSlice, Option) { + // This flag tells us if the block producers must enable Elastic Scaling MVP hack. + // It extends `BackedCandidate::validity_indices` to store a 8 bit core index. + if core_index_enabled { + let core_idx_offset = self.validator_indices.len().saturating_sub(8); + if core_idx_offset > 0 { + let (validator_indices_slice, core_idx_slice) = + self.validator_indices.split_at(core_idx_offset); + return ( + validator_indices_slice, + Some(CoreIndex(core_idx_slice.load::() as u32)), + ); + } + } + + (&self.validator_indices, None) + } + + /// Inject a core index in the validator_indices bitvec. + fn inject_core_index(&mut self, core_index: CoreIndex) { + let core_index_to_inject: BitVec = + BitVec::from_vec(vec![core_index.0 as u8]); + self.validator_indices.extend(core_index_to_inject); + } + + /// Update the validator indices and core index in the candidate. + pub fn set_validator_indices_and_core_index( + &mut self, + new_indices: BitVec, + maybe_core_index: Option, + ) { + self.validator_indices = new_indices; + + if let Some(core_index) = maybe_core_index { + self.inject_core_index(core_index); + } + } +} + +/// Scraped runtime backing votes and resolved disputes. +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct ScrapedOnChainVotes { + /// The session in which the block was included. + pub session: SessionIndex, + /// Set of backing validators for each candidate, represented by its candidate + /// receipt. + pub backing_validators_per_candidate: + Vec<(CandidateReceiptV2, Vec<(ValidatorIndex, ValidityAttestation)>)>, + /// On-chain-recorded set of disputes. + /// Note that the above `backing_validators` are + /// unrelated to the backers of the disputes candidates. + pub disputes: MultiDisputeStatementSet, +} + +impl From> for super::v8::ScrapedOnChainVotes { + fn from(value: ScrapedOnChainVotes) -> Self { + Self { + session: value.session, + backing_validators_per_candidate: value + .backing_validators_per_candidate + .into_iter() + .map(|(receipt, validators)| (receipt.into(), validators)) + .collect::>(), + disputes: value.disputes, + } + } +} + +/// Information about a core which is currently occupied. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct OccupiedCore { + // NOTE: this has no ParaId as it can be deduced from the candidate descriptor. + /// If this core is freed by availability, this is the assignment that is next up on this + /// core, if any. None if there is nothing queued for this core. + pub next_up_on_available: Option, + /// The relay-chain block number this began occupying the core at. + pub occupied_since: N, + /// The relay-chain block this will time-out at, if any. + pub time_out_at: N, + /// If this core is freed by being timed-out, this is the assignment that is next up on this + /// core. None if there is nothing queued for this core or there is no possibility of timing + /// out. + pub next_up_on_time_out: Option, + /// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding + /// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that + /// this will be available. + pub availability: BitVec, + /// The group assigned to distribute availability pieces of this candidate. + pub group_responsible: GroupIndex, + /// The hash of the candidate occupying the core. + pub candidate_hash: CandidateHash, + /// The descriptor of the candidate occupying the core. + pub candidate_descriptor: CandidateDescriptorV2, +} + +/// The state of a particular availability core. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CoreState { + /// The core is currently occupied. + #[codec(index = 0)] + Occupied(OccupiedCore), + /// The core is currently free, with a para scheduled and given the opportunity + /// to occupy. /// - /// `None` means no maximum. - pub max_validators_per_core: Option, - /// The amount of blocks ahead to schedule paras. - pub lookahead: u32, - /// How many cores are managed by the coretime chain. - pub num_cores: u32, - /// The max number of times a claim can time out in availability. - pub max_availability_timeouts: u32, - /// The maximum queue size of the pay as you go module. - pub on_demand_queue_max_size: u32, - /// The target utilization of the spot price queue in percentages. - pub on_demand_target_queue_utilization: Perbill, - /// How quickly the fee rises in reaction to increased utilization. - /// The lower the number the slower the increase. - pub on_demand_fee_variability: Perbill, - /// The minimum amount needed to claim a slot in the spot pricing queue. - pub on_demand_base_fee: Balance, - /// The number of blocks a claim stays in the scheduler's claim queue before getting cleared. - /// This number should go reasonably higher than the number of blocks in the async backing - /// lookahead. - pub ttl: BlockNumber, -} - -impl> Default for SchedulerParams { - fn default() -> Self { + /// 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 { - group_rotation_frequency: 1u32.into(), - paras_availability_period: 1u32.into(), - max_validators_per_core: Default::default(), - lookahead: 1, - num_cores: Default::default(), - max_availability_timeouts: Default::default(), - on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE, - on_demand_target_queue_utilization: Perbill::from_percent(25), - on_demand_fee_variability: Perbill::from_percent(3), - on_demand_base_fee: 10_000_000u128, - ttl: 5u32.into(), + next_up_on_available: value.next_up_on_available, + occupied_since: value.occupied_since, + time_out_at: value.time_out_at, + next_up_on_time_out: value.next_up_on_time_out, + availability: value.availability, + group_responsible: value.group_responsible, + candidate_hash: value.candidate_hash, + candidate_descriptor: value.candidate_descriptor.into(), } } } + +impl From> for super::v8::CoreState { + fn from(value: CoreState) -> Self { + match value { + CoreState::Free => super::v8::CoreState::Free, + CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core), + CoreState::Occupied(occupied_core) => + super::v8::CoreState::Occupied(occupied_core.into()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + v8::{ + tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, + CommittedCandidateReceipt, Hash, HeadData, ValidationCode, + }, + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2}, + }; + + fn dummy_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") + } + + fn dummy_collator_id() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") + } + + pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { + let zeros = Hash::zero(); + let reserved2 = [0; 64]; + + CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2 { + para_id: 0.into(), + relay_parent: zeros, + version: InternalVersion(0), + core_index: 123, + session_index: 1, + reserved1: Default::default(), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + reserved2, + para_head: zeros, + validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(), + }, + commitments: CandidateCommitments { + head_data: HeadData(vec![]), + upward_messages: vec![].try_into().expect("empty vec fits within bounds"), + new_validation_code: None, + horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"), + processed_downward_messages: 0, + hrmp_watermark: 0_u32, + }, + } + } + + #[test] + fn is_binary_compatibile() { + let old_ccr = dummy_old_committed_candidate_receipt(); + let new_ccr = dummy_committed_candidate_receipt_v2(); + + assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size()); + + let encoded_old = old_ccr.encode(); + + // Deserialize from old candidate receipt. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_old.as_slice()).unwrap(); + + // We get same candidate hash. + assert_eq!(old_ccr.hash(), new_ccr.hash()); + } + + #[test] + fn invalid_version_descriptor() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2); + // Put some unknown version. + new_ccr.descriptor.version = InternalVersion(100); + + // Deserialize as V1. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut new_ccr.encode().as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); + assert_eq!( + new_ccr.check(&vec![].as_slice()), + Err(CandidateReceiptError::UnknownVersion(InternalVersion(100))) + ) + } + + #[test] + fn test_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + } + + #[test] + fn test_invalid_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 0; + new_ccr.descriptor.para_id = ParaId::new(1000); + + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // The check should fail because no `SelectCore` signal was sent. + assert_eq!( + new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + Err(CandidateReceiptError::NoCoreSelected) + ); + + // Garbage message. + new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode()); + + // No `SelectCore` can be decoded. + assert_eq!(new_ccr.commitments.selected_core(), None); + + // Failure is expected. + assert_eq!( + new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + Err(CandidateReceiptError::NoCoreSelected) + ); + + new_ccr.commitments.upward_messages.clear(); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + // Duplicate + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); + + // Duplicate doesn't override first signal. + assert_eq!(new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), Ok(())); + } + + #[test] + fn test_version2_receipts_decoded_as_v1() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + let encoded_ccr = new_ccr.encode(); + let decoded_ccr: CommittedCandidateReceipt = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent()); + assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id()); + + assert_eq!(new_ccr.hash(), decoded_ccr.hash()); + + // Encode v1 and decode as V2 + let encoded_ccr = new_ccr.encode(); + let v2_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123))); + assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + + assert_eq!(new_ccr.hash(), v2_ccr.hash()); + } + + #[test] + fn test_core_select_is_mandatory() { + // Testing edge case when collators provide zeroed signature and collator id. + let mut old_ccr = dummy_old_committed_candidate_receipt(); + old_ccr.descriptor.para_id = ParaId::new(1000); + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + // Since collator sig and id are zeroed, it means that the descriptor uses format + // version 2. + // We expect the check to fail in such case because there will be no `SelectCore` + // commitment. + assert_eq!(new_ccr.check(&vec![CoreIndex(0)]), Err(CandidateReceiptError::NoCoreSelected)); + + // Adding collator signature should make it decode as v1. + old_ccr.descriptor.signature = dummy_collator_signature(); + old_ccr.descriptor.collator = dummy_collator_id(); + + let old_ccr_hash = old_ccr.hash(); + + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature)); + assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator)); + + assert_eq!(new_ccr.descriptor.core_index(), None); + assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000)); + + assert_eq!(old_ccr_hash, new_ccr.hash()); + } +} diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index 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.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/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index c82d89d2d8799eff6a68b49bdb01ee15bbe43d84..317f339ddd4e972fcce89354efac3bf27712f313 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -111,21 +111,15 @@ pub enum ApprovalCheckError { } enum ApprovalVotingMessage { - /// Check if the assignment is valid and can be accepted by our view of the protocol. - /// Should not be sent unless the block hash is known. - CheckAndImportAssignment( - IndirectAssignmentCert, - CandidateIndex, // The index of the candidate included in the block. - ResponseChannel, - ), - /// Check if the approval vote is valid and can be accepted by our view of the - /// protocol. - /// - /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval( - IndirectSignedApprovalVote, - ResponseChannel, - ), + /// Import an assignment into the approval-voting database. + /// + /// Should not be sent unless the block hash is known and the VRF assignment checks out. + ImportAssignment(CheckedIndirectAssignment, Option>), + /// Import an approval vote into approval-voting database + /// + /// Should not be sent unless the block hash within the indirect vote is known, vote is + /// correctly signed and we had a previous assignment for the candidate. + ImportApproval(CheckedIndirectSignedApprovalVote, Option>), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. Along with that, return the lists of candidate hashes /// which appear in every block from the (non-inclusive) base number up to (inclusive) the specified diff --git a/polkadot/rpc/src/lib.rs b/polkadot/rpc/src/lib.rs index eb0133b6e8fd6bc5d07d40e1fb917066285bf1bc..0007df908e2bff5136bfb16fbff33a19b304ba98 100644 --- a/polkadot/rpc/src/lib.rs +++ b/polkadot/rpc/src/lib.rs @@ -27,7 +27,7 @@ use sc_consensus_beefy::communication::notification::{ BeefyBestBlockStream, BeefyVersionedFinalityProofStream, }; use sc_consensus_grandpa::FinalityProofProvider; -pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; +pub use sc_rpc::SubscriptionTaskExecutor; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_application_crypto::RuntimeAppPublic; @@ -83,8 +83,6 @@ pub struct FullDeps { pub select_chain: SC, /// A copy of the chain spec. pub chain_spec: Box, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, /// BABE specific dependencies. pub babe: BabeDeps, /// GRANDPA specific dependencies. @@ -97,7 +95,13 @@ pub struct FullDeps { /// Instantiate all RPC extensions. pub fn create_full( - FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa, beefy, backend } : FullDeps, + FullDeps { client, pool, select_chain, chain_spec, babe, grandpa, beefy, backend }: FullDeps< + C, + P, + SC, + B, + AuthorityId, + >, ) -> Result> where C: ProvideRuntimeApi @@ -138,8 +142,8 @@ where finality_provider, } = grandpa; - io.merge(StateMigration::new(client.clone(), backend.clone(), deny_unsafe).into_rpc())?; - io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + io.merge(StateMigration::new(client.clone(), backend.clone()).into_rpc())?; + io.merge(System::new(client.clone(), pool.clone()).into_rpc())?; io.merge(TransactionPayment::new(client.clone()).into_rpc())?; io.merge( Mmr::new( @@ -151,8 +155,7 @@ where .into_rpc(), )?; io.merge( - Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain, deny_unsafe) - .into_rpc(), + Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain).into_rpc(), )?; io.merge( Grandpa::new( diff --git a/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 b2a67ee8dd24105b1e3f5298dafbba66193681f0..665737afa6cb51328a2bd944ec2538d44731b793 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -30,32 +30,38 @@ use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use polkadot_primitives::{ - node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorV2, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + InherentData as ParachainsInherentData, + }, + AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, + CollatorSignature, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, + GroupIndex, HeadData, Id as ParaId, IndexedVec, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, }; -use sp_core::{sr25519, ByteArray, H256}; +use sp_core::{ByteArray, H256}; use sp_runtime::{ generic::Digest, traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; - -/// Create a null collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from_slice(&vec![0u8; 32]).expect("32 bytes; qed") +fn mock_validation_code() -> ValidationCode { + ValidationCode(vec![1, 2, 3]) } -/// Create a null collator signature. -pub fn dummy_collator_signature() -> CollatorSignature { - CollatorSignature::from_slice(&vec![0u8; 64]).expect("64 bytes; qed") +// Create a dummy collator id suitable to be used in a V1 candidate descriptor. +fn junk_collator() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") } -fn mock_validation_code() -> ValidationCode { - ValidationCode(vec![1, 2, 3]) +// Creates a dummy collator signature suitable to be used in a V1 candidate descriptor. +fn junk_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") } /// Grab an account, seeded by a name and index. @@ -68,6 +74,21 @@ fn account(name: &'static str, index: u32, seed: u32) -> Acco .expect("infinite input; no invalid input; qed") } +pub fn generate_validator_pairs( + validator_count: u32, +) -> Vec<(T::AccountId, ValidatorId)> { + (0..validator_count) + .map(|i| { + let public = ValidatorId::generate_pair(None); + + // The account Id is not actually used anywhere, just necessary to fulfill the + // expected type of the `validators` param of `test_trigger_on_new_session`. + let account: T::AccountId = account("validator", i, i); + (account, public) + }) + .collect() +} + /// Create a 32 byte slice based on the given number. fn byte32_slice_from(n: u32) -> [u8; 32] { let mut slice = [0u8; 32]; @@ -121,6 +142,8 @@ pub(crate) struct BenchBuilder { fill_claimqueue: bool, /// Cores which should not be available when being populated with pending candidates. unavailable_cores: Vec, + /// Use v2 candidate descriptor. + candidate_descriptor_v2: bool, _phantom: core::marker::PhantomData, } @@ -152,6 +175,7 @@ impl BenchBuilder { code_upgrade: None, fill_claimqueue: true, unavailable_cores: vec![], + candidate_descriptor_v2: false, _phantom: core::marker::PhantomData::, } } @@ -221,9 +245,10 @@ impl BenchBuilder { .expect("self.block_number is u32") } - /// Maximum number of validators that may be part of a validator group. + /// Fallback for the maximum number of validators participating in parachains consensus (a.k.a. + /// active validators). pub(crate) fn fallback_max_validators() -> u32 { - configuration::ActiveConfig::::get().max_validators.unwrap_or(200) + configuration::ActiveConfig::::get().max_validators.unwrap_or(1024) } /// Maximum number of validators participating in parachains consensus (a.k.a. active @@ -259,6 +284,12 @@ impl BenchBuilder { self } + /// Toggle usage of v2 candidate descriptors. + pub(crate) fn set_candidate_descriptor_v2(mut self, enable: bool) -> Self { + self.candidate_descriptor_v2 = enable; + self + } + /// Get the maximum number of validators per core. fn max_validators_per_core(&self) -> u32 { self.max_validators_per_core.unwrap_or(Self::fallback_max_validators_per_core()) @@ -285,8 +316,8 @@ impl BenchBuilder { /// Get the minimum number of validity votes in order for a backed candidate to be included. #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn fallback_min_validity_votes() -> u32 { - (Self::fallback_max_validators() / 2) + 1 + pub(crate) fn fallback_min_backing_votes() -> u32 { + 2 } fn mock_head_data() -> HeadData { @@ -294,18 +325,20 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock() -> CandidateDescriptor { + fn candidate_descriptor_mock() -> CandidateDescriptorV2 { + // Use a v1 descriptor. CandidateDescriptor:: { para_id: 0.into(), relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), + collator: junk_collator(), persisted_validation_data_hash: Default::default(), pov_hash: Default::default(), erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), + signature: junk_collator_signature(), para_head: Default::default(), validation_code_hash: mock_validation_code().hash(), } + .into() } /// Create a mock of `CandidatePendingAvailability`. @@ -356,11 +389,11 @@ impl BenchBuilder { availability_votes, commitments, ); - inclusion::PendingAvailability::::mutate(para_id, |maybe_andidates| { - if let Some(candidates) = maybe_andidates { + inclusion::PendingAvailability::::mutate(para_id, |maybe_candidates| { + if let Some(candidates) = maybe_candidates { candidates.push_back(candidate_availability); } else { - *maybe_andidates = + *maybe_candidates = Some([candidate_availability].into_iter().collect::>()); } }); @@ -422,20 +455,6 @@ impl BenchBuilder { } } - /// Generate validator key pairs and account ids. - fn generate_validator_pairs(validator_count: u32) -> Vec<(T::AccountId, ValidatorId)> { - (0..validator_count) - .map(|i| { - let public = ValidatorId::generate_pair(None); - - // The account Id is not actually used anywhere, just necessary to fulfill the - // expected type of the `validators` param of `test_trigger_on_new_session`. - let account: T::AccountId = account("validator", i, i); - (account, public) - }) - .collect() - } - fn signing_context(&self) -> SigningContext { SigningContext { parent_hash: Self::header(self.block_number).hash(), @@ -630,18 +649,35 @@ impl BenchBuilder { let group_validators = scheduler::Pallet::::group_validators(group_idx).unwrap(); - let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor:: { + let descriptor = if self.candidate_descriptor_v2 { + CandidateDescriptorV2::new( para_id, relay_parent, - collator: dummy_collator(), + core_idx, + 1, + persisted_validation_data_hash, + pov_hash, + Default::default(), + head_data.hash(), + validation_code_hash, + ) + } else { + CandidateDescriptor:: { + para_id, + relay_parent, + collator: junk_collator(), persisted_validation_data_hash, pov_hash, erasure_root: Default::default(), - signature: dummy_collator_signature(), + signature: junk_collator_signature(), para_head: head_data.hash(), validation_code_hash, - }, + } + .into() + }; + + let candidate = CommittedCandidateReceipt:: { + descriptor, commitments: CandidateCommitments:: { upward_messages: Default::default(), horizontal_messages: Default::default(), @@ -799,7 +835,7 @@ impl BenchBuilder { c.scheduler_params.num_cores = used_cores as u32; }); - let validator_ids = Self::generate_validator_pairs(self.max_validators()); + let validator_ids = generate_validator_pairs::(self.max_validators()); let target_session = SessionIndex::from(self.target_session); let builder = self.setup_session(target_session, validator_ids, used_cores, extra_cores); diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index d09962ef2b4412c3e583129c2ab14624062c5dff..30fe95883e77f2cf09ada2a14ced20b93a7d1988 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -42,7 +42,7 @@ mod benchmarking; pub mod migration; pub use pallet::*; -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; const LOG_TARGET: &str = "runtime::configuration"; @@ -557,7 +557,7 @@ pub mod pallet { /// The list is sorted ascending by session index. Also, this list can only contain at most /// 2 items: for the next session and for the `scheduled_session`. #[pallet::storage] - pub(crate) type PendingConfigs = + pub type PendingConfigs = StorageValue<_, Vec<(SessionIndex, HostConfiguration>)>, ValueQuery>; /// If this is set, then the configuration setters will bypass the consistency checks. This diff --git a/polkadot/runtime/parachains/src/configuration/migration/v12.rs b/polkadot/runtime/parachains/src/configuration/migration/v12.rs index 6b77655687f0dfd13bfdf3f425a4d744b9798cd3..111b1a1999661aab11ecbe9746a3930061c88e1b 100644 --- a/polkadot/runtime/parachains/src/configuration/migration/v12.rs +++ b/polkadot/runtime/parachains/src/configuration/migration/v12.rs @@ -24,7 +24,7 @@ use frame_support::{ traits::{Defensive, UncheckedOnRuntimeUpgrade}, }; use frame_system::pallet_prelude::BlockNumberFor; -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; use sp_core::Get; use sp_staking::SessionIndex; diff --git a/polkadot/runtime/parachains/src/coretime/migration.rs b/polkadot/runtime/parachains/src/coretime/migration.rs index 4e75088675590e54fa6d8dc040732cc032cc0732..d4be135aad65767fdccf8aea785019940e81c2bb 100644 --- a/polkadot/runtime/parachains/src/coretime/migration.rs +++ b/polkadot/runtime/parachains/src/coretime/migration.rs @@ -24,7 +24,6 @@ mod v_coretime { use crate::{ assigner_coretime, configuration, coretime::{mk_coretime_call, Config, PartsOf57600, WeightInfo}, - paras, }; use alloc::{vec, vec::Vec}; #[cfg(feature = "try-runtime")] @@ -51,18 +50,24 @@ mod v_coretime { pub trait GetLegacyLease { /// If parachain is a lease holding parachain, return the block at which the lease expires. fn get_parachain_lease_in_blocks(para: ParaId) -> Option; + // All parachains holding a lease, no matter if there are gaps in the slots or not. + fn get_all_parachains_with_leases() -> Vec; } /// Migrate a chain to use coretime. /// /// This assumes that the `Coretime` and the `AssignerCoretime` pallets are added at the same /// time to a runtime. - pub struct MigrateToCoretime( + pub struct MigrateToCoretime( core::marker::PhantomData<(T, SendXcm, LegacyLease)>, ); - impl>> - MigrateToCoretime + impl< + T: Config, + SendXcm: xcm::v4::SendXcm, + LegacyLease: GetLegacyLease>, + const TIMESLICE_PERIOD: u32, + > MigrateToCoretime { fn already_migrated() -> bool { // We are using the assigner coretime because the coretime pallet doesn't has any @@ -94,7 +99,8 @@ mod v_coretime { T: Config + crate::dmp::Config, SendXcm: xcm::v4::SendXcm, LegacyLease: GetLegacyLease>, - > OnRuntimeUpgrade for MigrateToCoretime + const TIMESLICE_PERIOD: u32, + > OnRuntimeUpgrade for MigrateToCoretime { fn on_runtime_upgrade() -> Weight { if Self::already_migrated() { @@ -102,7 +108,7 @@ mod v_coretime { } log::info!("Migrating existing parachains to coretime."); - migrate_to_coretime::() + migrate_to_coretime::() } #[cfg(feature = "try-runtime")] @@ -111,7 +117,7 @@ mod v_coretime { return Ok(Vec::new()) } - let legacy_paras = paras::Parachains::::get(); + let legacy_paras = LegacyLease::get_all_parachains_with_leases(); let config = configuration::ActiveConfig::::get(); let total_core_count = config.scheduler_params.num_cores + legacy_paras.len() as u32; @@ -154,8 +160,9 @@ mod v_coretime { T: Config, SendXcm: xcm::v4::SendXcm, LegacyLease: GetLegacyLease>, + const TIMESLICE_PERIOD: u32, >() -> Weight { - let legacy_paras = paras::Parachains::::get(); + let legacy_paras = LegacyLease::get_all_parachains_with_leases(); let legacy_count = legacy_paras.len() as u32; let now = frame_system::Pallet::::block_number(); for (core, para_id) in legacy_paras.into_iter().enumerate() { @@ -175,7 +182,6 @@ mod v_coretime { } let config = configuration::ActiveConfig::::get(); - // num_cores was on_demand_cores until now: for on_demand in 0..config.scheduler_params.num_cores { let core = CoreIndex(legacy_count.saturating_add(on_demand as _)); let r = assigner_coretime::Pallet::::assign_core( @@ -193,7 +199,9 @@ mod v_coretime { c.scheduler_params.num_cores = total_cores; }); - if let Err(err) = migrate_send_assignments_to_coretime_chain::() { + if let Err(err) = + migrate_send_assignments_to_coretime_chain::( + ) { log::error!("Sending legacy chain data to coretime chain failed: {:?}", err); } @@ -210,8 +218,9 @@ mod v_coretime { T: Config, SendXcm: xcm::v4::SendXcm, LegacyLease: GetLegacyLease>, + const TIMESLICE_PERIOD: u32, >() -> result::Result<(), SendError> { - let legacy_paras = paras::Parachains::::get(); + let legacy_paras = LegacyLease::get_all_parachains_with_leases(); let legacy_paras_count = legacy_paras.len(); let (system_chains, lease_holding): (Vec<_>, Vec<_>) = legacy_paras.into_iter().partition(IsSystem::is_system); @@ -224,7 +233,7 @@ mod v_coretime { mk_coretime_call::(crate::coretime::CoretimeCalls::Reserve(schedule)) }); - let leases = lease_holding.into_iter().filter_map(|p| { + let mut leases = lease_holding.into_iter().filter_map(|p| { log::trace!(target: "coretime-migration", "Preparing sending of lease holding para {:?}", p); let Some(valid_until) = LegacyLease::get_parachain_lease_in_blocks(p) else { log::error!("Lease holding chain with no lease information?!"); @@ -237,10 +246,7 @@ mod v_coretime { return None }, }; - // We assume the coretime chain set this parameter to the recommended value in RFC-1: - const TIME_SLICE_PERIOD: u32 = 80; - let round_up = if valid_until % TIME_SLICE_PERIOD > 0 { 1 } else { 0 }; - let time_slice = valid_until / TIME_SLICE_PERIOD + TIME_SLICE_PERIOD * round_up; + let time_slice = (valid_until + TIMESLICE_PERIOD - 1) / TIMESLICE_PERIOD; log::trace!(target: "coretime-migration", "Sending of lease holding para {:?}, valid_until: {:?}, time_slice: {:?}", p, valid_until, time_slice); Some(mk_coretime_call::(crate::coretime::CoretimeCalls::SetLease(p.into(), time_slice))) }); @@ -269,16 +275,30 @@ mod v_coretime { }); let reservation_content = message_content.clone().chain(reservations).collect(); - let pool_content = message_content.clone().chain(pool).collect(); - let leases_content = message_content.clone().chain(leases).collect(); + let leases_content_1 = message_content + .clone() + .chain(leases.by_ref().take(legacy_paras_count / 2)) // split in two messages to avoid overweighted XCM + .collect(); + let leases_content_2 = message_content.clone().chain(leases).collect(); let set_core_count_content = message_content.clone().chain(set_core_count).collect(); - - let messages = vec![ - Xcm(reservation_content), - Xcm(pool_content), - Xcm(leases_content), - Xcm(set_core_count_content), - ]; + // If `pool_content` is empty don't send a blank XCM message + let messages = if core_count as usize > legacy_paras_count { + let pool_content = message_content.clone().chain(pool).collect(); + vec![ + Xcm(reservation_content), + Xcm(pool_content), + Xcm(leases_content_1), + Xcm(leases_content_2), + Xcm(set_core_count_content), + ] + } else { + vec![ + Xcm(reservation_content), + Xcm(leases_content_1), + Xcm(leases_content_2), + Xcm(set_core_count_content), + ] + }; for message in messages { send_xcm::( diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index fbd8935f1990233ebfded2db9f31ac4b241bebde..9b9bdb86878f3bc30545ed58e4504ae5ef5b4582 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -358,7 +358,10 @@ fn mk_coretime_call(call: crate::coretime::CoretimeCalls) -> Instruct fn do_notify_revenue(when: BlockNumber, raw_revenue: Balance) -> Result<(), XcmError> { let dest = Junction::Parachain(T::BrokerId::get()).into_location(); - let mut message = Vec::new(); + let mut message = vec![Instruction::UnpaidExecution { + weight_limit: WeightLimit::Unlimited, + check_origin: None, + }]; let asset = Asset { id: AssetId(Location::here()), fun: Fungible(raw_revenue) }; let dummy_xcm_context = XcmContext { origin: None, message_id: [0; 32], topic: None }; @@ -384,10 +387,6 @@ fn do_notify_revenue(when: BlockNumber, raw_revenue: Balance) -> Resu message.extend( [ - Instruction::UnpaidExecution { - weight_limit: WeightLimit::Unlimited, - check_origin: None, - }, ReceiveTeleportedAsset(assets_reanchored), DepositAsset { assets: Wild(AllCounted(1)), diff --git a/polkadot/runtime/parachains/src/dmp.rs b/polkadot/runtime/parachains/src/dmp.rs index 54e112d1b8b44206b8a121dd082c60718662e14a..03580e11b8e9cbe7dce4c9e375741ec3677bd9fe 100644 --- a/polkadot/runtime/parachains/src/dmp.rs +++ b/polkadot/runtime/parachains/src/dmp.rs @@ -287,7 +287,7 @@ impl Pallet { } /// Prunes the specified number of messages from the downward message queue of the given para. - pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) -> Weight { + pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) { let q_len = DownwardMessageQueues::::mutate(para, |q| { let processed_downward_messages = processed_downward_messages as usize; if processed_downward_messages > q.len() { @@ -306,7 +306,6 @@ impl Pallet { if q_len <= (threshold as usize) { Self::decrease_fee_factor(para); } - T::DbWeight::get().reads_writes(1, 1) } /// Returns the Head of Message Queue Chain for the given para or `None` if there is none diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs index 8b01a755c3c7bd75d558989fd593a44334c906b6..b149404b41b8716173d57b0dfa37a777618946db 100644 --- a/polkadot/runtime/parachains/src/hrmp.rs +++ b/polkadot/runtime/parachains/src/hrmp.rs @@ -1305,9 +1305,7 @@ impl Pallet { remaining } - pub(crate) fn prune_hrmp(recipient: ParaId, new_hrmp_watermark: BlockNumberFor) -> Weight { - let mut weight = Weight::zero(); - + pub(crate) fn prune_hrmp(recipient: ParaId, new_hrmp_watermark: BlockNumberFor) { // sift through the incoming messages digest to collect the paras that sent at least one // message to this parachain between the old and new watermarks. let senders = HrmpChannelDigests::::mutate(&recipient, |digest| { @@ -1323,7 +1321,6 @@ impl Pallet { *digest = leftover; senders }); - weight += T::DbWeight::get().reads_writes(1, 1); // having all senders we can trivially find out the channels which we need to prune. let channels_to_prune = @@ -1356,21 +1353,13 @@ impl Pallet { channel.total_size -= pruned_size as u32; } }); - - weight += T::DbWeight::get().reads_writes(2, 2); } HrmpWatermarks::::insert(&recipient, new_hrmp_watermark); - weight += T::DbWeight::get().reads_writes(0, 1); - - weight } /// Process the outbound HRMP messages by putting them into the appropriate recipient queues. - /// - /// Returns the amount of weight consumed. - pub(crate) fn queue_outbound_hrmp(sender: ParaId, out_hrmp_msgs: HorizontalMessages) -> Weight { - let mut weight = Weight::zero(); + pub(crate) fn queue_outbound_hrmp(sender: ParaId, out_hrmp_msgs: HorizontalMessages) { let now = frame_system::Pallet::::block_number(); for out_msg in out_hrmp_msgs { @@ -1426,11 +1415,7 @@ impl Pallet { recipient_digest.push((now, vec![sender])); } HrmpChannelDigests::::insert(&channel_id.recipient, recipient_digest); - - weight += T::DbWeight::get().reads_writes(2, 2); } - - weight } /// Initiate opening a channel from a parachain to a given recipient with given channel diff --git a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs index 169e858deda8aea05751450a65065550b881142b..1dac3c92cf166cd30c2170d9d8fb142fad6af54f 100644 --- a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs +++ b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs @@ -15,23 +15,133 @@ // along with Polkadot. If not, see . use super::*; +use crate::{ + builder::generate_validator_pairs, + configuration, + hrmp::{HrmpChannel, HrmpChannels}, + initializer, HeadData, ValidationCode, +}; +use bitvec::{bitvec, prelude::Lsb0}; use frame_benchmarking::benchmarks; use pallet_message_queue as mq; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateCommitments, + HrmpChannelId, OutboundHrmpMessage, SessionIndex, +}; + +fn create_candidate_commitments( + para_id: ParaId, + head_data: HeadData, + max_msg_len: usize, + ump_msg_count: u32, + hrmp_msg_count: u32, + code_upgrade: bool, +) -> CandidateCommitments { + let upward_messages = { + let unbounded = create_messages(max_msg_len, ump_msg_count as _); + BoundedVec::truncate_from(unbounded) + }; + + let horizontal_messages = { + let unbounded = create_messages(max_msg_len, hrmp_msg_count as _); + + for n in 0..unbounded.len() { + let channel_id = HrmpChannelId { sender: para_id, recipient: para_id + n as u32 + 1 }; + HrmpChannels::::insert( + &channel_id, + HrmpChannel { + sender_deposit: 42, + recipient_deposit: 42, + max_capacity: 10_000_000, + max_total_size: 1_000_000_000, + max_message_size: 10_000_000, + msg_count: 0, + total_size: 0, + mqc_head: None, + }, + ); + } + + let unbounded = unbounded + .into_iter() + .enumerate() + .map(|(n, data)| OutboundHrmpMessage { recipient: para_id + n as u32 + 1, data }) + .collect(); + BoundedVec::truncate_from(unbounded) + }; + + let new_validation_code = code_upgrade.then_some(ValidationCode(vec![42u8; 1024])); + + CandidateCommitments:: { + upward_messages, + horizontal_messages, + new_validation_code, + head_data, + processed_downward_messages: 0, + hrmp_watermark: 10, + } +} + +fn create_messages(msg_len: usize, n_msgs: usize) -> Vec> { + let best_number = 73_u8; // Chuck Norris of numbers + vec![vec![best_number; msg_len]; n_msgs] +} benchmarks! { where_clause { where - T: mq::Config, + T: mq::Config + configuration::Config + initializer::Config, } - receive_upward_messages { - let i in 1 .. 1000; + enact_candidate { + let u in 0 .. 2; + let h in 0 .. 2; + let c in 0 .. 1; + + let para = 42_u32.into(); // not especially important. let max_len = mq::MaxMessageLenOf::::get() as usize; - let para = 42u32.into(); // not especially important. - let upward_messages = vec![vec![0; max_len]; i as usize]; + + let config = configuration::ActiveConfig::::get(); + let n_validators = config.max_validators.unwrap_or(500); + let validators = generate_validator_pairs::(n_validators); + + let session = SessionIndex::from(0u32); + initializer::Pallet::::test_trigger_on_new_session( + false, + session, + validators.iter().map(|(a, v)| (a, v.clone())), + None, + ); + let backing_group_size = config.scheduler_params.max_validators_per_core.unwrap_or(5); + let head_data = HeadData(vec![0xFF; 1024]); + + let relay_parent_number = BlockNumberFor::::from(10u32); + let commitments = create_candidate_commitments::(para, head_data, max_len, u, h, c != 0); + let backers = bitvec![u8, Lsb0; 1; backing_group_size as usize]; + let availability_votes = bitvec![u8, Lsb0; 1; n_validators as usize]; + let core_index = CoreIndex::from(0); + let backing_group = GroupIndex::from(0); + + let descriptor = CandidateDescriptor::::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, + commitments, + }; + Pallet::::receive_upward_messages(para, vec![vec![0; max_len]; 1].as_slice()); - }: { Pallet::::receive_upward_messages(para, upward_messages.as_slice()) } + } : { Pallet::::enact_candidate(relay_parent_number, receipt, backers, availability_votes, core_index, backing_group) } impl_benchmark_test_suite!( Pallet, diff --git a/polkadot/runtime/parachains/src/inclusion/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 115eee97553039a01c12797e22066c996e1b3050..e014529ea11a00cec30d94d64bbb72be45d6e3ec 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -34,7 +34,6 @@ use alloc::{ }; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use codec::{Decode, Encode}; -#[cfg(feature = "std")] use core::fmt; use frame_support::{ defensive, @@ -45,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}; @@ -65,18 +68,23 @@ mod benchmarking; pub mod migration; pub trait WeightInfo { - fn receive_upward_messages(i: u32) -> Weight; + /// Weight for `enact_candidate` extrinsic given the number of sent messages + /// (ump, hrmp) and whether there is a new code for a runtime upgrade. + /// + /// NOTE: due to a shortcoming of the current benchmarking framework, + /// we use `u32` for the code upgrade, even though it is a `bool`. + fn enact_candidate(u: u32, h: u32, c: u32) -> Weight; } pub struct TestWeightInfo; impl WeightInfo for TestWeightInfo { - fn receive_upward_messages(_: u32) -> Weight { - Weight::MAX + fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight { + Weight::zero() } } impl WeightInfo for () { - fn receive_upward_messages(_: u32) -> Weight { + fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight { Weight::zero() } } @@ -376,7 +384,7 @@ pub mod pallet { const LOG_TARGET: &str = "runtime::inclusion"; /// The reason that a candidate's outputs were rejected for. -#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Debug)] enum AcceptanceCheckErr { HeadDataTooLarge, /// Code upgrades are not permitted at the current time. @@ -434,7 +442,6 @@ pub(crate) enum UmpAcceptanceCheckErr { IsOffboarding, } -#[cfg(feature = "std")] impl fmt::Debug for UmpAcceptanceCheckErr { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -507,7 +514,7 @@ impl Pallet { pub(crate) fn update_pending_availability_and_get_freed_cores( validators: &[ValidatorId], signed_bitfields: SignedAvailabilityBitfields, - ) -> Vec<(CoreIndex, CandidateHash)> { + ) -> (Weight, Vec<(CoreIndex, CandidateHash)>) { let threshold = availability_threshold(validators.len()); let mut votes_per_core: BTreeMap> = BTreeMap::new(); @@ -528,6 +535,7 @@ impl Pallet { } let mut freed_cores = vec![]; + let mut weight = Weight::zero(); let pending_paraids: Vec<_> = PendingAvailability::::iter_keys().collect(); for paraid in pending_paraids { @@ -581,7 +589,17 @@ impl Pallet { descriptor: candidate.descriptor, commitments: candidate.commitments, }; - let _weight = Self::enact_candidate( + + let has_runtime_upgrade = + receipt.commitments.new_validation_code.as_ref().map_or(0, |_| 1); + let u = receipt.commitments.upward_messages.len() as u32; + let h = receipt.commitments.horizontal_messages.len() as u32; + let enact_weight = ::WeightInfo::enact_candidate( + u, + h, + has_runtime_upgrade, + ); + Self::enact_candidate( candidate.relay_parent_number, receipt, candidate.backers, @@ -589,13 +607,14 @@ impl Pallet { candidate.core, candidate.backing_group, ); + weight.saturating_accrue(enact_weight); } } } }); } - freed_cores + (weight, freed_cores) } /// Process candidates that have been backed. Provide a set of @@ -749,7 +768,7 @@ impl Pallet { let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; let signing_context = SigningContext { - parent_hash: backed_candidate.descriptor().relay_parent, + parent_hash: backed_candidate.descriptor().relay_parent(), session_index: shared::CurrentSessionIndex::::get(), }; @@ -811,23 +830,20 @@ impl Pallet { let prev_context = Self::para_most_recent_context(¶_id); let check_ctx = CandidateCheckContext::::new(prev_context); - if check_ctx - .check_validation_outputs( - para_id, - relay_parent_number, - &validation_outputs.head_data, - &validation_outputs.new_validation_code, - validation_outputs.processed_downward_messages, - &validation_outputs.upward_messages, - BlockNumberFor::::from(validation_outputs.hrmp_watermark), - &validation_outputs.horizontal_messages, - ) - .is_err() - { + if let Err(err) = check_ctx.check_validation_outputs( + para_id, + relay_parent_number, + &validation_outputs.head_data, + &validation_outputs.new_validation_code, + validation_outputs.processed_downward_messages, + &validation_outputs.upward_messages, + BlockNumberFor::::from(validation_outputs.hrmp_watermark), + &validation_outputs.horizontal_messages, + ) { log::debug!( target: LOG_TARGET, - "Validation outputs checking for parachain `{}` failed", - u32::from(para_id), + "Validation outputs checking for parachain `{}` failed, error: {:?}", + u32::from(para_id), err ); false } else { @@ -842,7 +858,7 @@ impl Pallet { availability_votes: BitVec, core_index: CoreIndex, backing_group: GroupIndex, - ) -> Weight { + ) { let plain = receipt.to_plain(); let commitments = receipt.commitments; let config = configuration::ActiveConfig::::get(); @@ -863,38 +879,36 @@ impl Pallet { .map(|(i, _)| ValidatorIndex(i as _)), ); - // initial weight is config read. - let mut weight = T::DbWeight::get().reads_writes(1, 0); if let Some(new_code) = commitments.new_validation_code { // Block number of candidate's inclusion. let now = frame_system::Pallet::::block_number(); - weight.saturating_add(paras::Pallet::::schedule_code_upgrade( - receipt.descriptor.para_id, + paras::Pallet::::schedule_code_upgrade( + receipt.descriptor.para_id(), new_code, now, &config, UpgradeStrategy::SetGoAheadSignal, - )); + ); } // enact the messaging facet of the candidate. - weight.saturating_accrue(dmp::Pallet::::prune_dmq( - receipt.descriptor.para_id, + dmp::Pallet::::prune_dmq( + receipt.descriptor.para_id(), commitments.processed_downward_messages, - )); - weight.saturating_accrue(Self::receive_upward_messages( - receipt.descriptor.para_id, + ); + Self::receive_upward_messages( + receipt.descriptor.para_id(), commitments.upward_messages.as_slice(), - )); - weight.saturating_accrue(hrmp::Pallet::::prune_hrmp( - receipt.descriptor.para_id, + ); + hrmp::Pallet::::prune_hrmp( + receipt.descriptor.para_id(), BlockNumberFor::::from(commitments.hrmp_watermark), - )); - weight.saturating_accrue(hrmp::Pallet::::queue_outbound_hrmp( - receipt.descriptor.para_id, + ); + hrmp::Pallet::::queue_outbound_hrmp( + receipt.descriptor.para_id(), commitments.horizontal_messages, - )); + ); Self::deposit_event(Event::::CandidateIncluded( plain, @@ -903,11 +917,11 @@ impl Pallet { backing_group, )); - weight.saturating_add(paras::Pallet::::note_new_head( - receipt.descriptor.para_id, + paras::Pallet::::note_new_head( + receipt.descriptor.para_id(), commitments.head_data, relay_parent_number, - )) + ); } pub(crate) fn relay_dispatch_queue_size(para_id: ParaId) -> (u32, u32) { @@ -972,7 +986,7 @@ impl Pallet { /// This function is infallible since the candidate was already accepted and we therefore need /// to deal with the messages as given. Messages that are too long will be ignored since such /// candidates should have already been rejected in [`Self::check_upward_messages`]. - pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) -> Weight { + pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) { let bounded = upward_messages .iter() .filter_map(|d| { @@ -991,19 +1005,17 @@ impl Pallet { pub(crate) fn receive_bounded_upward_messages( para: ParaId, messages: Vec>>, - ) -> Weight { + ) { let count = messages.len() as u32; if count == 0 { - return Weight::zero() + return } T::MessageQueue::enqueue_messages( messages.into_iter(), AggregateMessageOrigin::Ump(UmpQueueId::Para(para)), ); - let weight = ::WeightInfo::receive_upward_messages(count); Self::deposit_event(Event::UpwardMessagesReceived { from: para, count }); - weight } /// Cleans up all timed out candidates as well as their descendant candidates. @@ -1242,8 +1254,8 @@ impl CandidateCheckContext { backed_candidate_receipt: &CommittedCandidateReceipt<::Hash>, parent_head_data: HeadData, ) -> Result, Error> { - let para_id = backed_candidate_receipt.descriptor().para_id; - let relay_parent = backed_candidate_receipt.descriptor().relay_parent; + let para_id = backed_candidate_receipt.descriptor.para_id(); + let relay_parent = backed_candidate_receipt.descriptor.relay_parent(); // Check that the relay-parent is one of the allowed relay-parents. let (relay_parent_storage_root, relay_parent_number) = { @@ -1263,7 +1275,7 @@ impl CandidateCheckContext { let expected = persisted_validation_data.hash(); ensure!( - expected == backed_candidate_receipt.descriptor().persisted_validation_data_hash, + expected == backed_candidate_receipt.descriptor.persisted_validation_data_hash(), Error::::ValidationDataHashMismatch, ); } @@ -1272,12 +1284,12 @@ impl CandidateCheckContext { // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; ensure!( - backed_candidate_receipt.descriptor().validation_code_hash == validation_code_hash, + backed_candidate_receipt.descriptor.validation_code_hash() == validation_code_hash, Error::::InvalidValidationCodeHash, ); ensure!( - backed_candidate_receipt.descriptor().para_head == + backed_candidate_receipt.descriptor.para_head() == backed_candidate_receipt.commitments.head_data.hash(), Error::::ParaHeadMismatch, ); @@ -1294,9 +1306,10 @@ impl CandidateCheckContext { ) { log::debug!( target: LOG_TARGET, - "Validation outputs checking during inclusion of a candidate {:?} for parachain `{}` failed", + "Validation outputs checking during inclusion of a candidate {:?} for parachain `{}` failed, error: {:?}", backed_candidate_receipt.hash(), u32::from(para_id), + err ); Err(err.strip_into_dispatch_err::())?; }; @@ -1352,10 +1365,49 @@ impl CandidateCheckContext { para_id, relay_parent_number, processed_downward_messages, + ) + .map_err(|e| { + log::debug!( + target: LOG_TARGET, + "Check processed downward messages for parachain `{}` on relay parent number `{:?}` failed, error: {:?}", + u32::from(para_id), + relay_parent_number, + e + ); + e + })?; + Pallet::::check_upward_messages(&self.config, para_id, upward_messages).map_err( + |e| { + log::debug!( + target: LOG_TARGET, + "Check upward messages for parachain `{}` failed, error: {:?}", + u32::from(para_id), + e + ); + e + }, )?; - Pallet::::check_upward_messages(&self.config, para_id, upward_messages)?; - hrmp::Pallet::::check_hrmp_watermark(para_id, relay_parent_number, hrmp_watermark)?; - hrmp::Pallet::::check_outbound_hrmp(&self.config, para_id, horizontal_messages)?; + hrmp::Pallet::::check_hrmp_watermark(para_id, relay_parent_number, hrmp_watermark) + .map_err(|e| { + log::debug!( + target: LOG_TARGET, + "Check hrmp watermark for parachain `{}` on relay parent number `{:?}` failed, error: {:?}", + u32::from(para_id), + relay_parent_number, + e + ); + e + })?; + hrmp::Pallet::::check_outbound_hrmp(&self.config, para_id, horizontal_messages) + .map_err(|e| { + log::debug!( + target: LOG_TARGET, + "Check outbound hrmp for parachain `{}` failed, error: {:?}", + u32::from(para_id), + e + ); + e + })?; Ok(()) } diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 3ead456cde5a98eed8950551901ad3bae2107b97..59114e28be16486a527503cb74a3b043b8840402 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,22 +26,21 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, AvailabilityBitfield, SignedAvailabilityBitfields, - UncheckedSignedAvailabilityBitfields, + effective_minimum_backing_votes, AvailabilityBitfield, CandidateDescriptor, + SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorId, + BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; -use polkadot_primitives_test_helpers::{ - dummy_collator, dummy_collator_signature, dummy_validation_code, -}; +use polkadot_primitives_test_helpers::dummy_validation_code; use sc_keystore::LocalKeystore; +use sp_core::ByteArray; use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -96,24 +95,6 @@ pub(crate) enum BackingKind { Lacking, } -pub(crate) fn collator_sign_candidate( - collator: Sr25519Keyring, - candidate: &mut CommittedCandidateReceipt, -) { - candidate.descriptor.collator = collator.public().into(); - - let payload = polkadot_primitives::collator_signature_payload( - &candidate.descriptor.relay_parent, - &candidate.descriptor.para_id, - &candidate.descriptor.persisted_validation_data_hash, - &candidate.descriptor.pov_hash, - &candidate.descriptor.validation_code_hash, - ); - - candidate.descriptor.signature = collator.sign(&payload[..]).into(); - assert!(candidate.descriptor().check_collator_signature().is_ok()); -} - pub(crate) fn back_candidate( candidate: CommittedCandidateReceipt, validators: &[Sr25519Keyring], @@ -311,9 +292,16 @@ impl TestCandidateBuilder { validation_code_hash: self.validation_code.hash(), para_head: self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), erasure_root: Default::default(), - signature: dummy_collator_signature(), - collator: dummy_collator(), - }, + signature: CollatorSignature::from_slice( + &mut (0..64).into_iter().collect::>().as_slice(), + ) + .expect("64 bytes; qed"), + collator: CollatorId::from_slice( + &mut (0..32).into_iter().collect::>().as_slice(), + ) + .expect("32 bytes; qed"), + } + .into(), commitments: CandidateCommitments { head_data: self.head_data, new_validation_code: self.new_validation_code, @@ -366,10 +354,11 @@ pub(crate) fn process_bitfields( ) -> Vec<(CoreIndex, CandidateHash)> { let validators = shared::ActiveValidatorKeys::::get(); - ParaInclusion::update_pending_availability_and_get_freed_cores( + let (_weight, bitfields) = ParaInclusion::update_pending_availability_and_get_freed_cores( &validators[..], signed_bitfields, - ) + ); + bitfields } #[test] @@ -1243,7 +1232,7 @@ fn candidate_checks() { // Check candidate ordering { - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1252,7 +1241,7 @@ fn candidate_checks() { ..Default::default() } .build(); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1264,7 +1253,7 @@ fn candidate_checks() { .build(); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1280,10 +1269,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_1); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_2); - let backed_a = back_candidate( candidate_a, &validators, @@ -1355,7 +1340,7 @@ fn candidate_checks() { // candidate does not build on top of the latest unincluded head - let mut candidate_b_3 = TestCandidateBuilder { + let candidate_b_3 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(4), @@ -1370,7 +1355,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_3); let backed_b_3 = back_candidate( candidate_b_3, @@ -1395,7 +1379,7 @@ fn candidate_checks() { // candidate not backed. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1404,7 +1388,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); // Insufficient backing. let backed = back_candidate( @@ -1458,7 +1441,7 @@ fn candidate_checks() { let wrong_parent_hash = Hash::repeat_byte(222); assert!(System::parent_hash() != wrong_parent_hash); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: wrong_parent_hash, pov_hash: Hash::repeat_byte(1), @@ -1467,7 +1450,7 @@ fn candidate_checks() { } .build(); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1477,10 +1460,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b); - let backed_a = back_candidate( candidate_a, &validators, @@ -1530,10 +1509,9 @@ fn candidate_checks() { .build(); assert_eq!(CollatorId::from(Sr25519Keyring::Two.public()), thread_collator); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate); // change the candidate after signing. - candidate.descriptor.pov_hash = Hash::repeat_byte(2); + candidate.descriptor.set_pov_hash(Hash::repeat_byte(2)); let backed = back_candidate( candidate, @@ -1596,7 +1574,7 @@ fn candidate_checks() { // interfering code upgrade - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1607,8 +1585,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1647,7 +1623,7 @@ fn candidate_checks() { // Bad validation data hash - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1657,8 +1633,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1684,7 +1658,7 @@ fn candidate_checks() { // bad validation code hash { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1695,8 +1669,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1722,7 +1694,7 @@ fn candidate_checks() { // Para head hash in descriptor doesn't match head data { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1733,8 +1705,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1825,7 +1795,7 @@ fn backing_works() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1834,9 +1804,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1845,9 +1814,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1856,7 +1824,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let backed_a = back_candidate( candidate_a.clone(), @@ -1977,7 +1944,7 @@ fn backing_works() { Vec<(ValidatorIndex, ValidityAttestation)>, )>| { candidate_receipts_with_backers.sort_by(|(cr1, _), (cr2, _)| { - cr1.descriptor().para_id.cmp(&cr2.descriptor().para_id) + cr1.descriptor().para_id().cmp(&cr2.descriptor().para_id()) }); candidate_receipts_with_backers }; @@ -2120,7 +2087,7 @@ fn backing_works_with_elastic_scaling_mvp() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2129,9 +2096,8 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -2140,10 +2106,9 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_1); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -2157,7 +2122,6 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_2); let backed_a = back_candidate( candidate_a.clone(), @@ -2395,7 +2359,7 @@ fn can_include_candidate_with_ok_code_upgrade() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2405,7 +2369,6 @@ fn can_include_candidate_with_ok_code_upgrade() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), @@ -2555,7 +2518,7 @@ fn check_allowed_relay_parents() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: relay_parent_a.1, pov_hash: Hash::repeat_byte(1), @@ -2568,10 +2531,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let signing_context_a = SigningContext { parent_hash: relay_parent_a.1, session_index: 5 }; - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: relay_parent_b.1, pov_hash: Hash::repeat_byte(2), @@ -2584,10 +2546,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); let signing_context_b = SigningContext { parent_hash: relay_parent_b.1, session_index: 5 }; - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: relay_parent_c.1, pov_hash: Hash::repeat_byte(3), @@ -2600,7 +2561,6 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let signing_context_c = SigningContext { parent_hash: relay_parent_c.1, session_index: 5 }; let backed_a = back_candidate( @@ -2822,7 +2782,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2832,7 +2792,6 @@ fn para_upgrade_delay_scheduled_from_inclusion() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index fbe9ebf809b35f7f57589ad313f415b997fb8fe2..75c9e3a5c9b947832a4ca8c15b8813e21d641b2b 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -449,8 +449,16 @@ impl SendXcm for DummyXcmSender { } } +pub struct InclusionWeightInfo; + +impl crate::inclusion::WeightInfo for InclusionWeightInfo { + fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight { + Weight::from_parts(1024 * 1024, 0) + } +} + impl crate::inclusion::Config for Test { - type WeightInfo = (); + type WeightInfo = InclusionWeightInfo; type RuntimeEvent = RuntimeEvent; type DisputesHandler = Disputes; type RewardValidators = TestRewardValidators; diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index a4c404de2a651d0a4359a59fa9efb66b275defe8..5048656e63633d78f42a0ee70f8fab9faf5ad5ee 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -1956,14 +1956,12 @@ impl Pallet { inclusion_block_number: BlockNumberFor, cfg: &configuration::HostConfiguration>, upgrade_strategy: UpgradeStrategy, - ) -> Weight { - let mut weight = T::DbWeight::get().reads(1); - + ) { // Should be prevented by checks in `schedule_code_upgrade_external` let new_code_len = new_code.0.len(); if new_code_len < MIN_CODE_SIZE as usize || new_code_len > cfg.max_code_size as usize { log::warn!(target: LOG_TARGET, "attempted to schedule an upgrade with invalid new validation code",); - return weight + return } // Enacting this should be prevented by the `can_upgrade_validation_code` @@ -1977,7 +1975,7 @@ impl Pallet { // NOTE: we cannot set `UpgradeGoAheadSignal` signal here since this will be reset by // the following call `note_new_head` log::warn!(target: LOG_TARGET, "ended up scheduling an upgrade while one is pending",); - return weight + return } let code_hash = new_code.hash(); @@ -1986,7 +1984,6 @@ impl Pallet { // process right away. // // We do not want to allow this since it will mess with the code reference counting. - weight += T::DbWeight::get().reads(1); if CurrentCodeHash::::get(&id) == Some(code_hash) { // NOTE: we cannot set `UpgradeGoAheadSignal` signal here since this will be reset by // the following call `note_new_head` @@ -1994,15 +1991,13 @@ impl Pallet { target: LOG_TARGET, "para tried to upgrade to the same code. Abort the upgrade", ); - return weight + return } // This is the start of the upgrade process. Prevent any further attempts at upgrading. - weight += T::DbWeight::get().writes(2); FutureCodeHash::::insert(&id, &code_hash); UpgradeRestrictionSignal::::insert(&id, UpgradeRestriction::Present); - weight += T::DbWeight::get().reads_writes(1, 1); let next_possible_upgrade_at = inclusion_block_number + cfg.validation_upgrade_cooldown; UpgradeCooldowns::::mutate(|upgrade_cooldowns| { let insert_idx = upgrade_cooldowns @@ -2011,14 +2006,12 @@ impl Pallet { upgrade_cooldowns.insert(insert_idx, (id, next_possible_upgrade_at)); }); - weight += Self::kick_off_pvf_check( + Self::kick_off_pvf_check( PvfCheckCause::Upgrade { id, included_at: inclusion_block_number, upgrade_strategy }, code_hash, new_code, cfg, ); - - weight } /// Makes sure that the given code hash has passed pre-checking. @@ -2108,11 +2101,11 @@ impl Pallet { id: ParaId, new_head: HeadData, execution_context: BlockNumberFor, - ) -> Weight { + ) { Heads::::insert(&id, &new_head); MostRecentContext::::insert(&id, execution_context); - let weight = if let Some(expected_at) = FutureCodeUpgrades::::get(&id) { + if let Some(expected_at) = FutureCodeUpgrades::::get(&id) { if expected_at <= execution_context { FutureCodeUpgrades::::remove(&id); UpgradeGoAheadSignal::::remove(&id); @@ -2122,14 +2115,10 @@ impl Pallet { new_code_hash } else { log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id); - return T::DbWeight::get().reads_writes(3, 1 + 3) + return }; - let weight = Self::set_current_code(id, new_code_hash, expected_at); - - weight + T::DbWeight::get().reads_writes(3, 3) - } else { - T::DbWeight::get().reads_writes(1, 1 + 0) + Self::set_current_code(id, new_code_hash, expected_at); } } else { // This means there is no upgrade scheduled. @@ -2137,10 +2126,9 @@ impl Pallet { // In case the upgrade was aborted by the relay-chain we should reset // the `Abort` signal. UpgradeGoAheadSignal::::remove(&id); - T::DbWeight::get().reads_writes(1, 2) }; - weight.saturating_add(T::OnNewHead::on_new_head(id, &new_head)) + T::OnNewHead::on_new_head(id, &new_head); } /// Set the current code for the given parachain. diff --git a/polkadot/runtime/parachains/src/paras/tests.rs b/polkadot/runtime/parachains/src/paras/tests.rs index 732b75417387ad549f87e9e091566b1734e35058..6e4f99aa3d84412f4644402bedf425fef9d6a13c 100644 --- a/polkadot/runtime/parachains/src/paras/tests.rs +++ b/polkadot/runtime/parachains/src/paras/tests.rs @@ -16,7 +16,7 @@ use super::*; use frame_support::{assert_err, assert_ok, assert_storage_noop}; -use polkadot_primitives::{vstaging::SchedulerParams, BlockNumber, PARACHAIN_KEY_TYPE_ID}; +use polkadot_primitives::{BlockNumber, SchedulerParams, PARACHAIN_KEY_TYPE_ID}; use polkadot_primitives_test_helpers::{dummy_head_data, dummy_validation_code, validator_pubkeys}; use sc_keystore::LocalKeystore; use sp_keyring::Sr25519Keyring; diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index c5284ba1dd1f850d5fdabc7bfd782b488ea12cba..266860061bed8830311237721deda12b2b0e9356 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -17,15 +17,29 @@ 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; -use polkadot_primitives::v7::GroupIndex; +use polkadot_primitives::v8::GroupIndex; use crate::builder::BenchBuilder; benchmarks! { + enter_empty { + let scenario = BenchBuilder::::new() + .build(); + + let mut benchmark = scenario.data.clone(); + + benchmark.bitfields.clear(); + benchmark.backed_candidates.clear(); + benchmark.disputes.clear(); + }: enter(RawOrigin::None, benchmark) + verify { + // Assert that the block was not discarded + assert!(Included::::get().is_some()); + } // Variant over `v`, the number of dispute statements in a dispute statement set. This gives the // weight of a single dispute statement set. enter_variable_disputes { @@ -92,18 +106,8 @@ benchmarks! { // Variant over `v`, the amount of validity votes for a backed candidate. This gives the weight // of a single backed candidate. enter_backed_candidates_variable { - // NOTE: the starting value must be over half of the max validators per group so the backed - // candidate is not rejected. Also, we cannot have more validity votes than validators in - // the group. - - // Do not use this range for Rococo because it only has 1 validator per backing group, - // which causes issues when trying to create slopes with the benchmarking analysis. Instead - // use v = 1 for running Rococo benchmarks - let v in (BenchBuilder::::fallback_min_validity_votes()) - ..(BenchBuilder::::fallback_max_validators()); - - // Comment in for running rococo benchmarks - // let v = 1; + let v in (BenchBuilder::::fallback_min_backing_votes()) + .. 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. @@ -119,7 +123,6 @@ benchmarks! { // There is 1 backed, assert_eq!(benchmark.backed_candidates.len(), 1); // with `v` validity votes. - // let votes = v as usize; let votes = min(scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), v as usize); assert_eq!(benchmark.backed_candidates.get(0).unwrap().validity_votes().len(), votes); @@ -141,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); } @@ -157,7 +160,7 @@ benchmarks! { let v = crate::configuration::ActiveConfig::::get().max_code_size; let cores_with_backed: BTreeMap<_, _> - = vec![(0, BenchBuilder::::fallback_min_validity_votes())] + = vec![(0, BenchBuilder::::fallback_min_backing_votes())] .into_iter() .collect(); @@ -168,8 +171,10 @@ benchmarks! { let mut benchmark = scenario.data.clone(); - // let votes = BenchBuilder::::fallback_min_validity_votes() as usize; - let votes = min(scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), BenchBuilder::::fallback_min_validity_votes() as usize); + let votes = min( + scheduler::Pallet::::group_validators(GroupIndex::from(0)).unwrap().len(), + BenchBuilder::::fallback_min_backing_votes() as usize + ); // There is 1 backed assert_eq!(benchmark.backed_candidates.len(), 1); @@ -198,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 9d27e86ef901f3afc60379d406379464a18a8590..84d8299cd29cbfc51a58cfa4644cdc5883694c2c 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -48,12 +48,17 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use polkadot_primitives::{ - effective_minimum_backing_votes, node_features::FeatureIndex, BackedCandidate, CandidateHash, - CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, - DisputeStatementSet, HeadData, InherentData as ParachainsInherentData, - MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, - SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorVersion, CandidateReceiptV2 as CandidateReceipt, + InherentData as ParachainsInherentData, ScrapedOnChainVotes, + }, + CandidateHash, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, + DisputeStatementSet, HeadData, MultiDisputeStatementSet, SessionIndex, + SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, + UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, + PARACHAINS_INHERENT_IDENTIFIER, }; use rand::{seq::SliceRandom, SeedableRng}; use scale_info::TypeInfo; @@ -347,12 +352,12 @@ impl Pallet { let bitfields_weight = signed_bitfields_weight::(&bitfields); let disputes_weight = multi_dispute_statement_sets_weight::(&disputes); - // Weight before filtering/sanitization - let all_weight_before = candidates_weight + bitfields_weight + disputes_weight; + // Weight before filtering/sanitization except for enacting the candidates + let weight_before_filtering = candidates_weight + bitfields_weight + disputes_weight; - METRICS.on_before_filter(all_weight_before.ref_time()); - log::debug!(target: LOG_TARGET, "Size before filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_before.proof_size(), candidates_weight.proof_size() + bitfields_weight.proof_size(), disputes_weight.proof_size()); - log::debug!(target: LOG_TARGET, "Time weight before filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_before.ref_time(), candidates_weight.ref_time() + bitfields_weight.ref_time(), disputes_weight.ref_time()); + METRICS.on_before_filter(weight_before_filtering.ref_time()); + log::debug!(target: LOG_TARGET, "Size before filter: {}, candidates + bitfields: {}, disputes: {}", weight_before_filtering.proof_size(), candidates_weight.proof_size() + bitfields_weight.proof_size(), disputes_weight.proof_size()); + log::debug!(target: LOG_TARGET, "Time weight before filter: {}, candidates + bitfields: {}, disputes: {}", weight_before_filtering.ref_time(), candidates_weight.ref_time() + bitfields_weight.ref_time(), disputes_weight.ref_time()); let current_session = shared::CurrentSessionIndex::::get(); let expected_bits = scheduler::AvailabilityCores::::get().len(); @@ -409,7 +414,7 @@ impl Pallet { max_block_weight, ); - let all_weight_after = if context == ProcessInherentDataContext::ProvideInherent { + let mut all_weight_after = if context == ProcessInherentDataContext::ProvideInherent { // Assure the maximum block weight is adhered, by limiting bitfields and backed // candidates. Dispute statement sets were already limited before. let non_disputes_weight = apply_weight_limit::( @@ -424,11 +429,11 @@ impl Pallet { METRICS.on_after_filter(all_weight_after.ref_time()); log::debug!( - target: LOG_TARGET, - "[process_inherent_data] after filter: bitfields.len(): {}, backed_candidates.len(): {}, checked_disputes_sets.len() {}", - bitfields.len(), - backed_candidates.len(), - checked_disputes_sets.len() + target: LOG_TARGET, + "[process_inherent_data] after filter: bitfields.len(): {}, backed_candidates.len(): {}, checked_disputes_sets.len() {}", + bitfields.len(), + backed_candidates.len(), + checked_disputes_sets.len() ); log::debug!(target: LOG_TARGET, "Size after filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_after.proof_size(), non_disputes_weight.proof_size(), checked_disputes_sets_consumed_weight.proof_size()); log::debug!(target: LOG_TARGET, "Time weight after filter: {}, candidates + bitfields: {}, disputes: {}", all_weight_after.ref_time(), non_disputes_weight.ref_time(), checked_disputes_sets_consumed_weight.ref_time()); @@ -440,17 +445,20 @@ impl Pallet { } else { // This check is performed in the context of block execution. Ensures inherent weight // invariants guaranteed by `create_inherent_data` for block authorship. - if all_weight_before.any_gt(max_block_weight) { + if weight_before_filtering.any_gt(max_block_weight) { log::error!( "Overweight para inherent data reached the runtime {:?}: {} > {}", parent_hash, - all_weight_before, + weight_before_filtering, max_block_weight ); } - ensure!(all_weight_before.all_lte(max_block_weight), Error::::InherentOverweight); - all_weight_before + ensure!( + weight_before_filtering.all_lte(max_block_weight), + Error::::InherentOverweight + ); + weight_before_filtering }; // Note that `process_checked_multi_dispute_data` will iterate and import each @@ -529,11 +537,32 @@ impl Pallet { // Process new availability bitfields, yielding any availability cores whose // work has now concluded. - let freed_concluded = + let (enact_weight, freed_concluded) = inclusion::Pallet::::update_pending_availability_and_get_freed_cores( &validator_public[..], bitfields.clone(), ); + all_weight_after.saturating_accrue(enact_weight); + log::debug!( + target: LOG_TARGET, + "Enacting weight: {}, all weight: {}", + enact_weight.ref_time(), + all_weight_after.ref_time(), + ); + + // It's possible that that after the enacting the candidates, the total weight + // goes over the limit, however, we can't do anything about it at this point. + // By using the `Mandatory` weight, we ensure the block is still accepted, + // but no other (user) transactions can be included. + if all_weight_after.any_gt(max_block_weight) { + log::warn!( + target: LOG_TARGET, + "Overweight para inherent data after enacting the candidates {:?}: {} > {}", + parent_hash, + all_weight_after, + max_block_weight, + ); + } // Inform the disputes module of all included candidates. for (_, candidate_hash) in &freed_concluded { @@ -570,6 +599,12 @@ impl Pallet { .map(|b| *b) .unwrap_or(false); + let allow_v2_receipts = configuration::ActiveConfig::::get() + .node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false); + let mut eligible: BTreeMap> = BTreeMap::new(); let mut total_eligible_cores = 0; @@ -586,6 +621,7 @@ impl Pallet { concluded_invalid_hashes, eligible, core_index_enabled, + allow_v2_receipts, ); let count = count_backed_candidates(&backed_candidates_with_core); @@ -763,7 +799,7 @@ pub(crate) fn apply_weight_limit( let mut current_para_id = None; for candidate in core::mem::take(candidates).into_iter() { - let candidate_para_id = candidate.descriptor().para_id; + let candidate_para_id = candidate.descriptor().para_id(); if Some(candidate_para_id) == current_para_id { let chain = chained_candidates .last_mut() @@ -942,14 +978,15 @@ pub(crate) fn sanitize_bitfields( /// subsequent candidates after the filtered one. /// /// Filter out: -/// 1. any candidates which don't form a chain with the other candidates of the paraid (even if they +/// 1. Candidates that have v2 descriptors if the node `CandidateReceiptV2` feature is not enabled. +/// 2. any candidates which don't form a chain with the other candidates of the paraid (even if they /// do form a chain but are not in the right order). -/// 2. any candidates that have a concluded invalid dispute or who are descendants of a concluded +/// 3. any candidates that have a concluded invalid dispute or who are descendants of a concluded /// invalid candidate. -/// 3. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned +/// 4. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned /// but have no injected core index. -/// 4. all backing votes from disabled validators -/// 5. any candidates that end up with less than `effective_minimum_backing_votes` backing votes +/// 5. all backing votes from disabled validators +/// 6. any candidates that end up with less than `effective_minimum_backing_votes` backing votes /// /// Returns the scheduled /// backed candidates which passed filtering, mapped by para id and in the right dependency order. @@ -959,13 +996,28 @@ fn sanitize_backed_candidates( concluded_invalid_with_descendants: BTreeSet, scheduled: BTreeMap>, core_index_enabled: bool, + allow_v2_receipts: bool, ) -> BTreeMap, CoreIndex)>> { // Map the candidates to the right paraids, while making sure that the order between candidates // of the same para is preserved. let mut candidates_per_para: BTreeMap> = BTreeMap::new(); for candidate in backed_candidates { + // Drop any v2 candidate receipts if nodes are not allowed to use them. + // It is mandatory to filter these before calling `filter_unchained_candidates` to ensure + // any v1 descendants of v2 candidates are dropped. + if !allow_v2_receipts && candidate.descriptor().version() == CandidateDescriptorVersion::V2 + { + log::debug!( + target: LOG_TARGET, + "V2 candidate descriptors not allowed. Dropping candidate {:?} for paraid {:?}.", + candidate.candidate().hash(), + candidate.descriptor().para_id() + ); + continue + } + candidates_per_para - .entry(candidate.descriptor().para_id) + .entry(candidate.descriptor().para_id()) .or_default() .push(candidate); } @@ -984,7 +1036,7 @@ fn sanitize_backed_candidates( target: LOG_TARGET, "Found backed candidate {:?} which was concluded invalid or is a descendant of a concluded invalid candidate, for paraid {:?}.", candidate.candidate().hash(), - candidate.descriptor().para_id + candidate.descriptor().para_id() ); } keep @@ -1165,13 +1217,13 @@ fn filter_backed_statements_from_disabled_validators< // Get relay parent block number of the candidate. We need this to get the group index // assigned to this core at this block number let relay_parent_block_number = - match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent, None) { + match allowed_relay_parents.acquire_info(bc.descriptor().relay_parent(), None) { Some((_, block_num)) => block_num, None => { log::debug!( target: LOG_TARGET, "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", - bc.descriptor().relay_parent + bc.descriptor().relay_parent() ); return false }, @@ -1372,7 +1424,7 @@ fn map_candidates_to_cores block_num, None => { log::debug!( target: LOG_TARGET, "Relay parent {:?} for candidate {:?} is not in the allowed relay parents.", - candidate.descriptor().relay_parent, + candidate.descriptor().relay_parent(), candidate.candidate().hash(), ); return None diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 59fbb094837370b71ae9d9c5c8fff5e77233a010..ac42ac1611df00ebf35b32ad3a134f9536429a4e 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -20,7 +20,7 @@ use crate::{ configuration::{self, HostConfiguration}, mock::MockGenesisConfig, }; -use polkadot_primitives::vstaging::SchedulerParams; +use polkadot_primitives::SchedulerParams; fn default_config() -> MockGenesisConfig { MockGenesisConfig { @@ -58,7 +58,9 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{vstaging::SchedulerParams, AvailabilityBitfield, UncheckedSigned}; + use polkadot_primitives::{ + vstaging::InternalVersion, AvailabilityBitfield, SchedulerParams, UncheckedSigned, + }; use sp_runtime::Perbill; struct TestConfig { @@ -70,6 +72,7 @@ mod enter { fill_claimqueue: bool, elastic_paras: BTreeMap, unavailable_cores: Vec, + v2_descriptor: bool, } fn make_inherent_data( @@ -82,6 +85,7 @@ mod enter { fill_claimqueue, elastic_paras, unavailable_cores, + v2_descriptor, }: TestConfig, ) -> Bench { let extra_cores = elastic_paras @@ -99,7 +103,8 @@ mod enter { .set_backed_and_concluding_paras(backed_and_concluding.clone()) .set_dispute_sessions(&dispute_sessions[..]) .set_fill_claimqueue(fill_claimqueue) - .set_unavailable_cores(unavailable_cores); + .set_unavailable_cores(unavailable_cores) + .set_candidate_descriptor_v2(v2_descriptor); // Setup some assignments as needed: mock_assigner::Pallet::::set_core_count(builder.max_cores()); @@ -145,6 +150,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); // We expect the scenario to have cores 0 & 1 with pending availability. The backed @@ -240,6 +246,7 @@ mod enter { fill_claimqueue: false, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -344,6 +351,7 @@ mod enter { fill_claimqueue: true, elastic_paras: [(2, 4)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), + v2_descriptor: false, }); let mut expected_para_inherent_data = scenario.data.clone(); @@ -600,6 +608,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -673,6 +682,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -744,6 +754,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -831,6 +842,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -918,6 +930,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -943,6 +956,66 @@ mod enter { }); } + // Ensure that even if the block is over weight due to candidates enactment, + // we still can import it. + #[test] + fn overweight_candidates_enactment_is_fine() { + sp_tracing::try_init_simple(); + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + use crate::inclusion::WeightInfo as _; + + let mut backed_and_concluding = BTreeMap::new(); + // The number of candidates is chosen to go over the weight limit + // of the mock runtime together with the `enact_candidate`s weight. + let num_candidates = 5u32; + let max_weight = ::BlockWeights::get().max_block; + assert!(::WeightInfo::enact_candidate(0, 0, 0) + .saturating_mul(u64::from(num_candidates)) + .any_gt(max_weight)); + + for i in 0..num_candidates { + backed_and_concluding.insert(i, 2); + } + + let num_validators_per_core: u32 = 5; + let num_backed = backed_and_concluding.len(); + let bitfields_len = num_validators_per_core as usize * num_backed; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], + backed_and_concluding, + num_validators_per_core, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], + v2_descriptor: false, + }); + + let expected_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + assert_eq!(expected_para_inherent_data.bitfields.len(), bitfields_len); + assert_eq!(expected_para_inherent_data.backed_candidates.len(), num_backed); + assert_eq!(expected_para_inherent_data.disputes.len(), 0); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) + .unwrap(); + + let limit_inherent_data = + Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(); + assert!(limit_inherent_data == expected_para_inherent_data); + + assert_ok!(Pallet::::enter( + frame_system::RawOrigin::None.into(), + limit_inherent_data, + )); + }); + } + fn max_block_weight_proof_size_adjusted() -> Weight { let raw_weight = ::BlockWeights::get().max_block; let block_length = ::BlockLength::get(); @@ -1004,6 +1077,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1111,6 +1185,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1179,6 +1254,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1245,6 +1321,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1348,6 +1425,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let mut para_inherent_data = scenario.data.clone(); @@ -1381,7 +1459,7 @@ mod enter { // The chained candidates are not picked, instead a single other candidate is picked assert_eq!(backed_candidates.len(), 1); - assert_ne!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); + assert_ne!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1402,9 +1480,9 @@ mod enter { // Only the chained candidates should pass filter. assert_eq!(backed_candidates.len(), 3); // Check the actual candidates - assert_eq!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[1].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[2].descriptor().para_id, ParaId::from(1000)); + assert_eq!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[1].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[2].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1437,6 +1515,7 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1465,6 +1544,76 @@ mod enter { assert_eq!(dispatch_error, Error::::InherentOverweight.into()); }); } + + #[test] + fn v2_descriptors_are_filtered() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + // 8 cores ! + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + }); + + let mut unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + // Make the last candidate look like v1, by using an unknown version. + unfiltered_para_inherent_data.backed_candidates[9] + .descriptor_mut() + .set_version(InternalVersion(123)); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + // We expect all backed candidates to be filtered out. + let filtered_para_inherend_data = + Pallet::::create_inherent_inner(&inherent_data).unwrap(); + + assert_eq!(filtered_para_inherend_data.backed_candidates.len(), 0); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } } fn default_header() -> polkadot_primitives::Header { @@ -1481,9 +1630,7 @@ mod sanitizers { use super::*; use crate::{ - inclusion::tests::{ - back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder, - }, + inclusion::tests::{back_candidate, BackingKind, TestCandidateBuilder}, mock::new_test_ext, }; use bitvec::order::Lsb0; @@ -1497,7 +1644,6 @@ mod sanitizers { use crate::mock::Test; use polkadot_primitives::PARACHAIN_KEY_TYPE_ID; use sc_keystore::LocalKeystore; - use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -1881,7 +2027,7 @@ mod sanitizers { .into_iter() .map(|idx0| { let idx1 = idx0 + 1; - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(idx1), relay_parent, pov_hash: Hash::repeat_byte(idx1 as u8), @@ -1898,8 +2044,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1932,7 +2076,7 @@ mod sanitizers { let mut expected_backed_candidates_with_core = BTreeMap::new(); for candidate in backed_candidates.iter() { - let para_id = candidate.descriptor().para_id; + let para_id = candidate.descriptor().para_id(); expected_backed_candidates_with_core.entry(para_id).or_insert(vec![]).push(( candidate.clone(), @@ -2118,7 +2262,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2136,8 +2280,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2156,7 +2298,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2174,8 +2316,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2196,7 +2336,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2213,8 +2353,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2235,7 +2373,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2252,8 +2390,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2272,7 +2408,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2289,8 +2425,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -2307,7 +2441,7 @@ mod sanitizers { .or_insert(vec![]) .push((backed, CoreIndex(5))); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2325,8 +2459,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2343,7 +2475,7 @@ mod sanitizers { // Para 6. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(6), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2360,8 +2492,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2376,7 +2506,7 @@ mod sanitizers { // Para 7. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(7), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2393,8 +2523,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2409,7 +2537,7 @@ mod sanitizers { // Para 8. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(8), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2426,8 +2554,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2648,7 +2774,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2666,8 +2792,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let prev_backed: BackedCandidate = back_candidate( candidate, @@ -2679,7 +2803,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(0 as u32)), ); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2697,8 +2821,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2714,7 +2836,7 @@ mod sanitizers { // Para 2. { - let mut candidate_1 = TestCandidateBuilder { + let candidate_1 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2732,8 +2854,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_1); - let backed_1: BackedCandidate = back_candidate( candidate_1, &validators, @@ -2752,7 +2872,7 @@ mod sanitizers { .push((backed_1, CoreIndex(2))); } - let mut candidate_2 = TestCandidateBuilder { + let candidate_2 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2770,8 +2890,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_2); - let backed_2 = back_candidate( candidate_2.clone(), &validators, @@ -2783,7 +2901,7 @@ mod sanitizers { ); backed_candidates.push(backed_2.clone()); - let mut candidate_3 = TestCandidateBuilder { + let candidate_3 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2801,8 +2919,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_3); - let backed_3 = back_candidate( candidate_3, &validators, @@ -2817,7 +2933,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2835,8 +2951,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2855,7 +2969,7 @@ mod sanitizers { .push((backed, CoreIndex(5))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2873,8 +2987,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2895,7 +3007,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(8 as u8), @@ -2913,8 +3025,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed: BackedCandidate = back_candidate( candidate.clone(), &validators, @@ -3151,7 +3261,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3169,8 +3279,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3189,7 +3297,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3208,8 +3316,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3222,7 +3328,7 @@ mod sanitizers { ); backed_candidates.push(backed.clone()); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3241,8 +3347,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3257,7 +3361,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3275,8 +3379,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3295,7 +3397,7 @@ mod sanitizers { .push((backed, CoreIndex(3))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3314,8 +3416,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3334,7 +3434,7 @@ mod sanitizers { .push((backed, CoreIndex(4))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3353,8 +3453,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3427,7 +3525,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3451,7 +3550,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3476,6 +3576,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3508,6 +3609,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3548,6 +3650,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3618,6 +3721,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3656,6 +3760,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); assert!(sanitized_backed_candidates.is_empty()); @@ -3692,6 +3797,7 @@ mod sanitizers { set, scheduled, core_index_enabled, + false, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); @@ -3714,9 +3820,9 @@ mod sanitizers { let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 0 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 0 { invalid_set.insert(backed_candidate.hash()); - } else if backed_candidate.descriptor().para_id == ParaId::from(3) { + } else if backed_candidate.descriptor().para_id() == ParaId::from(3) { invalid_set.insert(backed_candidate.hash()); } } @@ -3729,6 +3835,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // We'll be left with candidates from paraid 2 and 4. @@ -3752,7 +3859,7 @@ mod sanitizers { let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 1 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 1 { invalid_set.insert(backed_candidate.hash()); } } @@ -3765,6 +3872,7 @@ mod sanitizers { invalid_set, scheduled, true, + false, ); // Only the second candidate of paraid 1 should be removed. diff --git a/polkadot/runtime/parachains/src/paras_inherent/weights.rs b/polkadot/runtime/parachains/src/paras_inherent/weights.rs index 37809396a82317eb2370c13d39acd60b1275154c..81c926a90e0bf81e427fe6ecfbb71c4c4c62f3b2 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/weights.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/weights.rs @@ -19,6 +19,7 @@ //! the relay chain, but we do care about the size of the block, by putting the tx in the //! proof_size we can use the already existing weight limiting code to limit the used size as well. +use crate::{configuration, inclusion}; use codec::{Encode, WrapperTypeEncode}; use polkadot_primitives::{ CheckedMultiDisputeStatementSet, MultiDisputeStatementSet, UncheckedSignedAvailabilityBitfield, @@ -28,6 +29,8 @@ use polkadot_primitives::{ use super::{BackedCandidate, Config, DisputeStatementSet, Weight}; pub trait WeightInfo { + /// The weight of processing an empty parachain inherent. + fn enter_empty() -> Weight; /// Variant over `v`, the count of dispute statements in a dispute statement set. This gives the /// weight of a single dispute statement set. fn enter_variable_disputes(v: u32) -> Weight; @@ -45,6 +48,9 @@ pub struct TestWeightInfo; // mock. #[cfg(not(feature = "runtime-benchmarks"))] impl WeightInfo for TestWeightInfo { + fn enter_empty() -> Weight { + Weight::zero() + } fn enter_variable_disputes(v: u32) -> Weight { // MAX Block Weight should fit 4 disputes Weight::from_parts(80_000 * v as u64 + 80_000, 0) @@ -66,6 +72,9 @@ impl WeightInfo for TestWeightInfo { // running as a test. #[cfg(feature = "runtime-benchmarks")] impl WeightInfo for TestWeightInfo { + fn enter_empty() -> Weight { + Weight::zero() + } fn enter_variable_disputes(_v: u32) -> Weight { Weight::zero() } @@ -88,6 +97,7 @@ pub fn paras_inherent_total_weight( backed_candidates_weight::(backed_candidates) .saturating_add(signed_bitfields_weight::(bitfields)) .saturating_add(multi_dispute_statement_sets_weight::(disputes)) + .saturating_add(enact_candidates_max_weight::(bitfields)) } pub fn multi_dispute_statement_sets_weight( @@ -123,7 +133,8 @@ where set_proof_size_to_tx_size( <::WeightInfo as WeightInfo>::enter_variable_disputes( statement_set.as_ref().statements.len() as u32, - ), + ) + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()), statement_set, ) } @@ -133,6 +144,7 @@ pub fn signed_bitfields_weight( ) -> Weight { set_proof_size_to_tx_size( <::WeightInfo as WeightInfo>::enter_bitfields() + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()) .saturating_mul(bitfields.len() as u64), bitfields, ) @@ -140,11 +152,33 @@ pub fn signed_bitfields_weight( pub fn signed_bitfield_weight(bitfield: &UncheckedSignedAvailabilityBitfield) -> Weight { set_proof_size_to_tx_size( - <::WeightInfo as WeightInfo>::enter_bitfields(), + <::WeightInfo as WeightInfo>::enter_bitfields() + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()), bitfield, ) } +/// Worst case scenario is all candidates have been enacted +/// and process a maximum number of messages. +pub fn enact_candidates_max_weight( + bitfields: &UncheckedSignedAvailabilityBitfields, +) -> Weight { + let config = configuration::ActiveConfig::::get(); + let max_ump_msgs = config.max_upward_message_num_per_candidate; + let max_hrmp_msgs = config.hrmp_max_message_num_per_candidate; + // No bitfields - no enacted candidates + let bitfield_size = bitfields.first().map(|b| b.unchecked_payload().0.len()).unwrap_or(0); + set_proof_size_to_tx_size( + <::WeightInfo as inclusion::WeightInfo>::enact_candidate( + max_ump_msgs, + max_hrmp_msgs, + 1, // runtime upgrade + ) + .saturating_mul(bitfield_size as u64), + bitfields, + ) +} + pub fn backed_candidate_weight( candidate: &BackedCandidate, ) -> Weight { @@ -155,7 +189,8 @@ pub fn backed_candidate_weight( <::WeightInfo as WeightInfo>::enter_backed_candidates_variable( candidate.validity_votes().len() as u32, ) - }, + } + .saturating_sub(<::WeightInfo as WeightInfo>::enter_empty()), candidate, ) } diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index 697890232211315af749a365c95fa8cc23425450..ead825b38f079e9789c65eebb1ff7171c4aa2847 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -27,15 +27,19 @@ use frame_support::traits::{GetStorageVersion, StorageVersion}; use frame_system::pallet_prelude::*; use polkadot_primitives::{ async_backing::{ - AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints, - InboundHrmpLimitations, OutboundHrmpChannelLimitations, + AsyncBackingParams, Constraints, InboundHrmpLimitations, OutboundHrmpChannelLimitations, }, - slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - NodeFeatures, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + async_backing::{BackingState, CandidatePendingAvailability}, + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + OccupiedCore, ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sp_runtime::traits::One; diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs index 4aa381e33b1bc2246abd3c16044cc56290597b12..a3440f686e94306399659f844ef31c720e77cad8 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -21,7 +21,9 @@ use alloc::{ collections::{btree_map::BTreeMap, vec_deque::VecDeque}, vec::Vec, }; -use polkadot_primitives::{CommittedCandidateReceipt, CoreIndex, Id as ParaId}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, Id as ParaId, +}; use sp_runtime::traits::One; /// Returns the claimqueue from the scheduler diff --git a/polkadot/runtime/parachains/src/scheduler/tests.rs b/polkadot/runtime/parachains/src/scheduler/tests.rs index f3866146e81121e7fe1ad85e09719df00a89a212..5f80114b596368135e91ca59ca9c7e6f0b59510f 100644 --- a/polkadot/runtime/parachains/src/scheduler/tests.rs +++ b/polkadot/runtime/parachains/src/scheduler/tests.rs @@ -19,7 +19,7 @@ use super::*; use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use frame_support::assert_ok; use polkadot_primitives::{ - vstaging::SchedulerParams, BlockNumber, SessionIndex, ValidationCode, ValidatorId, + BlockNumber, SchedulerParams, SessionIndex, ValidationCode, ValidatorId, }; use sp_keyring::Sr25519Keyring; diff --git a/polkadot/runtime/parachains/src/session_info/tests.rs b/polkadot/runtime/parachains/src/session_info/tests.rs index 3e81ca4987132ebbaacead0510688fa754b1d94f..aec0ff56e670be84c088974de2c36c8bb02f5a39 100644 --- a/polkadot/runtime/parachains/src/session_info/tests.rs +++ b/polkadot/runtime/parachains/src/session_info/tests.rs @@ -24,7 +24,7 @@ use crate::{ }, util::take_active_subset, }; -use polkadot_primitives::{vstaging::SchedulerParams, BlockNumber, ValidatorId, ValidatorIndex}; +use polkadot_primitives::{BlockNumber, SchedulerParams, ValidatorId, ValidatorIndex}; use sp_keyring::Sr25519Keyring; fn run_to_block( diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 50970965e11e6ebbd4f984ed16c21be44cd978c6..4aaaf94da5865e8c97406c11e443267733a2280b 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -339,4 +339,4 @@ runtime-metrics = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/polkadot/runtime/rococo/src/genesis_config_presets.rs b/polkadot/runtime/rococo/src/genesis_config_presets.rs index 67dcd6cd7a510d117eab9299793c7842db1b036e..17792cff18678623f373621e7206b609340f2bb6 100644 --- a/polkadot/runtime/rococo/src/genesis_config_presets.rs +++ b/polkadot/runtime/rococo/src/genesis_config_presets.rs @@ -20,9 +20,7 @@ use crate::{SessionKeys, BABE_GENESIS_EPOCH_CONFIG}; #[cfg(not(feature = "std"))] use alloc::format; use alloc::vec::Vec; -use polkadot_primitives::{ - vstaging::SchedulerParams, AccountId, AccountPublic, AssignmentId, ValidatorId, -}; +use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, SchedulerParams, ValidatorId}; use rococo_runtime_constants::currency::UNITS as ROC; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; diff --git a/polkadot/runtime/rococo/src/impls.rs b/polkadot/runtime/rococo/src/impls.rs index a4440a1c6e0b986780454d6aaa1bce19ff5a85ad..f01440ea02bc7c15377c732d22041e60d3425d66 100644 --- a/polkadot/runtime/rococo/src/impls.rs +++ b/polkadot/runtime/rococo/src/impls.rs @@ -90,7 +90,7 @@ where fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult { use crate::{ impls::IdentityMigratorCalls::PokeDeposit, - weights::runtime_common_identity_migrator::WeightInfo as MigratorWeights, + weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights, }; let total_to_send = Self::calculate_remote_deposit(fields, subs); diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 2f23b889916b8087e8e06988c5f532d6e211ca12..6ec49c5830f7d5a871d2aeb37a11cb5370653e24 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -35,12 +35,16 @@ use frame_support::{ }; use pallet_nis::WithMaximumOf; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, - SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, SessionInfo, Signature, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, claims, crowdloan, identity_migrator, impl_runtime_weights, @@ -170,7 +174,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -674,7 +678,7 @@ impl claims::Config for Runtime { type VestingSchedule = Vesting; type Prefix = Prefix; type MoveClaimOrigin = EnsureRoot; - type WeightInfo = weights::runtime_common_claims::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_claims::WeightInfo; } parameter_types! { @@ -940,7 +944,7 @@ impl pallet_proxy::Config for Runtime { impl parachains_origin::Config for Runtime {} impl parachains_configuration::Config for Runtime { - type WeightInfo = weights::runtime_parachains_configuration::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_configuration::WeightInfo; } impl parachains_shared::Config for Runtime { @@ -963,7 +967,7 @@ impl parachains_inclusion::Config for Runtime { type DisputesHandler = ParasDisputes; type RewardValidators = RewardValidators; type MessageQueue = MessageQueue; - type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -972,7 +976,7 @@ parameter_types! { impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::runtime_parachains_paras::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; @@ -1046,11 +1050,11 @@ impl parachains_hrmp::Config for Runtime { HrmpChannelSizeAndCapacityWithSystemRatio, >; type VersionWrapper = crate::XcmPallet; - type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_hrmp::WeightInfo; } impl parachains_paras_inherent::Config for Runtime { - type WeightInfo = weights::runtime_parachains_paras_inherent::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras_inherent::WeightInfo; } impl parachains_scheduler::Config for Runtime { @@ -1079,7 +1083,7 @@ impl coretime::Config for Runtime { type Currency = Balances; type BrokerId = BrokerId; type BrokerPotLocation = BrokerPot; - type WeightInfo = weights::runtime_parachains_coretime::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_coretime::WeightInfo; type SendXcm = crate::xcm_config::XcmRouter; type AssetTransactor = crate::xcm_config::LocalAssetTransactor; type AccountToLocation = xcm_builder::AliasesIntoAccountId32< @@ -1100,7 +1104,7 @@ impl parachains_on_demand::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type TrafficDefaultValue = OnDemandTrafficDefaultValue; - type WeightInfo = weights::runtime_parachains_on_demand::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_on_demand::WeightInfo; type MaxHistoricalRevenue = MaxHistoricalRevenue; type PalletId = OnDemandPalletId; } @@ -1110,7 +1114,7 @@ impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type ForceOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_initializer::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_initializer::WeightInfo; type CoretimeOnNewSession = Coretime; } @@ -1118,7 +1122,7 @@ impl parachains_disputes::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RewardValidators = (); type SlashingHandler = parachains_slashing::SlashValidatorsForDisputes; - type WeightInfo = weights::runtime_parachains_disputes::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_disputes::WeightInfo; } impl parachains_slashing::Config for Runtime { @@ -1149,7 +1153,7 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; - type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_paras_registrar::WeightInfo; } parameter_types! { @@ -1163,7 +1167,7 @@ impl slots::Config for Runtime { type LeasePeriod = LeasePeriod; type LeaseOffset = (); type ForceOrigin = EitherOf, LeaseAdmin>; - type WeightInfo = weights::runtime_common_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_slots::WeightInfo; } parameter_types! { @@ -1184,7 +1188,7 @@ impl crowdloan::Config for Runtime { type Registrar = Registrar; type Auctioneer = Auctions; type MaxMemoLength = MaxMemoLength; - type WeightInfo = weights::runtime_common_crowdloan::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_crowdloan::WeightInfo; } parameter_types! { @@ -1203,14 +1207,14 @@ impl auctions::Config for Runtime { type SampleLength = SampleLength; type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type InitiateOrigin = EitherOf, AuctionAdmin>; - type WeightInfo = weights::runtime_common_auctions::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_auctions::WeightInfo; } impl identity_migrator::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reaper = EnsureSigned; type ReapIdentityHandler = ToParachainIdentityReaper; - type WeightInfo = weights::runtime_common_identity_migrator::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_identity_migrator::WeightInfo; } type NisCounterpartInstance = pallet_balances::Instance2; @@ -1335,6 +1339,7 @@ impl pallet_beefy_mmr::Config for Runtime { type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = H256; type BeefyDataProvider = ParaHeadsRootProvider; + type WeightInfo = weights::pallet_beefy_mmr::WeightInfo; } impl paras_sudo_wrapper::Config for Runtime {} @@ -1352,7 +1357,7 @@ impl assigned_slots::Config for Runtime { type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength; type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength; type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod; - type WeightInfo = weights::runtime_common_assigned_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_assigned_slots::WeightInfo; } impl validator_manager::Config for Runtime { @@ -1579,6 +1584,13 @@ pub mod migrations { as Leaser>::lease_period_index(now)?; Some(index.saturating_add(lease.len() as u32).saturating_mul(LeasePeriod::get())) } + + fn get_all_parachains_with_leases() -> Vec { + slots::Leases::::iter() + .filter(|(_, lease)| !lease.is_empty()) + .map(|(para, _)| para) + .collect::>() + } } parameter_types! { @@ -1629,47 +1641,45 @@ pub mod migrations { /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - pallet_society::migrations::MigrateToV2, - parachains_configuration::migration::v7::MigrateToV7, - assigned_slots::migration::v1::MigrateToV1, - parachains_scheduler::migration::MigrateV1ToV2, - parachains_configuration::migration::v8::MigrateToV8, - parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, - pallet_referenda::migration::v1::MigrateV0ToV1, - pallet_referenda::migration::v1::MigrateV0ToV1, - - // Unlock & unreserve Gov1 funds - - pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, - pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, - - // Delete all Gov v1 pallet storage key/values. - - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - frame_support::migrations::RemovePallet::DbWeight>, - - pallet_grandpa::migrations::MigrateV4ToV5, - parachains_configuration::migration::v10::MigrateToV10, - - // Migrate Identity pallet for Usernames - pallet_identity::migration::versioned::V0ToV1, - parachains_configuration::migration::v11::MigrateToV11, - // This needs to come after the `parachains_configuration` above as we are reading the configuration. - coretime::migration::MigrateToCoretime, - parachains_configuration::migration::v12::MigrateToV12, - parachains_on_demand::migration::MigrateV0ToV1, - - // permanent - pallet_xcm::migration::MigrateToLatestXcmVersion, - - parachains_inclusion::migration::MigrateToV1, - ); + pallet_society::migrations::MigrateToV2, + parachains_configuration::migration::v7::MigrateToV7, + assigned_slots::migration::v1::MigrateToV1, + parachains_scheduler::migration::MigrateV1ToV2, + parachains_configuration::migration::v8::MigrateToV8, + parachains_configuration::migration::v9::MigrateToV9, + paras_registrar::migration::MigrateToV1, + pallet_referenda::migration::v1::MigrateV0ToV1, + pallet_referenda::migration::v1::MigrateV0ToV1, + + // Unlock & unreserve Gov1 funds + + pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, + pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, + pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, + + // Delete all Gov v1 pallet storage key/values. + + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + frame_support::migrations::RemovePallet::DbWeight>, + pallet_grandpa::migrations::MigrateV4ToV5, + parachains_configuration::migration::v10::MigrateToV10, + + // Migrate Identity pallet for Usernames + pallet_identity::migration::versioned::V0ToV1, + parachains_configuration::migration::v11::MigrateToV11, + // This needs to come after the `parachains_configuration` above as we are reading the configuration. + coretime::migration::MigrateToCoretime, + parachains_configuration::migration::v12::MigrateToV12, + parachains_on_demand::migration::MigrateV0ToV1, + + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, + parachains_inclusion::migration::MigrateToV1, + ); } /// Executive: handles dispatch to the various modules. @@ -1735,6 +1745,7 @@ mod benches { // Substrate [pallet_balances, Balances] [pallet_balances, NisCounterpartBalances] + [pallet_beefy_mmr, MmrLeaf] [frame_benchmarking::baseline, Baseline::] [pallet_bounties, Bounties] [pallet_child_bounties, ChildBounties] @@ -2025,7 +2036,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index 0512a393a6c4a49732b135cc61f96ae370c18309..020f8e22594a602f4bf12336eba92470ec754c4e 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -19,6 +19,7 @@ pub mod frame_system; pub mod pallet_asset_rate; pub mod pallet_balances_balances; pub mod pallet_balances_nis_counterpart_balances; +pub mod pallet_beefy_mmr; pub mod pallet_bounties; pub mod pallet_child_bounties; pub mod pallet_conviction_voting; @@ -43,20 +44,20 @@ pub mod pallet_utility; pub mod pallet_vesting; pub mod pallet_whitelist; pub mod pallet_xcm; -pub mod runtime_common_assigned_slots; -pub mod runtime_common_auctions; -pub mod runtime_common_claims; -pub mod runtime_common_crowdloan; -pub mod runtime_common_identity_migrator; -pub mod runtime_common_paras_registrar; -pub mod runtime_common_slots; -pub mod runtime_parachains_configuration; -pub mod runtime_parachains_coretime; -pub mod runtime_parachains_disputes; -pub mod runtime_parachains_hrmp; -pub mod runtime_parachains_inclusion; -pub mod runtime_parachains_initializer; -pub mod runtime_parachains_on_demand; -pub mod runtime_parachains_paras; -pub mod runtime_parachains_paras_inherent; +pub mod polkadot_runtime_common_assigned_slots; +pub mod polkadot_runtime_common_auctions; +pub mod polkadot_runtime_common_claims; +pub mod polkadot_runtime_common_crowdloan; +pub mod polkadot_runtime_common_identity_migrator; +pub mod polkadot_runtime_common_paras_registrar; +pub mod polkadot_runtime_common_slots; +pub mod polkadot_runtime_parachains_configuration; +pub mod polkadot_runtime_parachains_coretime; +pub mod polkadot_runtime_parachains_disputes; +pub mod polkadot_runtime_parachains_hrmp; +pub mod polkadot_runtime_parachains_inclusion; +pub mod polkadot_runtime_parachains_initializer; +pub mod polkadot_runtime_parachains_on_demand; +pub mod polkadot_runtime_parachains_paras; +pub mod polkadot_runtime_parachains_paras_inherent; pub mod xcm; diff --git a/polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs b/polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs new file mode 100644 index 0000000000000000000000000000000000000000..317c9149ec6c57c8e1c9d5bc5deb5ee648175ada --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_beefy_mmr.rs @@ -0,0 +1,89 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Autogenerated weights for `pallet_beefy_mmr` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_beefy_mmr +// --chain=rococo-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_beefy_mmr`. +pub struct WeightInfo(PhantomData); +impl pallet_beefy_mmr::WeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_116_000 picoseconds. + Weight::from_parts(7_343_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `234` + // Estimated: `3505` + // Minimum execution time: 5_652_000 picoseconds. + Weight::from_parts(5_963_000, 0) + .saturating_add(Weight::from_parts(0, 3505)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `226` + // Estimated: `1517` + // Minimum execution time: 11_953_000 picoseconds. + Weight::from_parts(15_978_891, 0) + .saturating_add(Weight::from_parts(0, 1517)) + // Standard Error: 1_780 + .saturating_add(Weight::from_parts(1_480_582, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_claims.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_claims.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_slots.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_common_slots.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_coretime.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_coretime.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_coretime.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_coretime.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_hrmp.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_hrmp.rs diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs new file mode 100644 index 0000000000000000000000000000000000000000..5824658383becd3dd9502883e4e048a80f29102b --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -0,0 +1,126 @@ +// 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 `polkadot_runtime_parachains::inclusion` +//! +//! 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-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: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=polkadot_runtime_parachains::inclusion +// --chain=rococo-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `polkadot_runtime_parachains::inclusion`. +pub struct WeightInfo(PhantomData); +impl polkadot_runtime_parachains::inclusion::WeightInfo for WeightInfo { + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeCooldowns` (r:1 w:1) + /// Proof: `Paras::UpgradeCooldowns` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:10) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:11 w:11) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:10 w:10) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// 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`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// The range of component `u` is `[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: `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(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, 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/rococo/src/weights/runtime_parachains_initializer.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_on_demand.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_on_demand.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs similarity index 100% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs similarity index 82% rename from polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs rename to polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs index c00966fb8048d0e09f5547eb1eaa354876f27f8c..b7b3d12d4d92c301c4b4a8754890676d3ca291d6 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Autogenerated weights for `runtime_parachains::paras_inherent` +//! Autogenerated weights for `polkadot_runtime_parachains::paras_inherent` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -32,7 +32,7 @@ // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_parachains::paras_inherent +// --pallet=polkadot_runtime_parachains::paras_inherent // --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -45,9 +45,49 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `runtime_parachains::paras_inherent`. +/// Weight functions for `polkadot_runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl polkadot_runtime_parachains::paras_inherent::WeightInfo for WeightInfo { + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:1 w:0) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn enter_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `8967` + // Estimated: `12432` + // Minimum execution time: 144_751_000 picoseconds. + Weight::from_parts(153_966_000, 0) + .saturating_add(Weight::from_parts(0, 12432)) + .saturating_add(T::DbWeight::get().reads(15)) + .saturating_add(T::DbWeight::get().writes(5)) + } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `System::ParentHash` (r:1 w:0) @@ -109,13 +149,13 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// The range of component `v` is `[10, 200]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `67785` - // Estimated: `73725 + v * (23 ±0)` - // Minimum execution time: 949_716_000 picoseconds. - Weight::from_parts(482_361_515, 0) - .saturating_add(Weight::from_parts(0, 73725)) - // Standard Error: 17_471 - .saturating_add(Weight::from_parts(50_100_764, 0).saturating_mul(v.into())) + // Measured: `67786` + // Estimated: `73726 + v * (23 ±0)` + // Minimum execution time: 972_311_000 picoseconds. + Weight::from_parts(645_559_304, 0) + .saturating_add(Weight::from_parts(0, 73726)) + // Standard Error: 53_320 + .saturating_add(Weight::from_parts(41_795_493, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(25)) .saturating_add(T::DbWeight::get().writes(15)) .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) @@ -140,18 +180,6 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaInclusion::V1` (r:2 w:1) /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) - /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) - /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) - /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) - /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Registrar::Paras` (r:1 w:0) - /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Disputes` (r:1 w:0) - /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) @@ -164,25 +192,15 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Included` (r:0 w:1) - /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) - /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::Heads` (r:0 w:1) - /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) - /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::MostRecentContext` (r:0 w:1) - /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `42757` - // Estimated: `48697` - // Minimum execution time: 437_627_000 picoseconds. - Weight::from_parts(460_975_000, 0) - .saturating_add(Weight::from_parts(0, 48697)) - .saturating_add(T::DbWeight::get().reads(23)) - .saturating_add(T::DbWeight::get().writes(15)) + // Measured: `42374` + // Estimated: `48314` + // Minimum execution time: 361_262_000 picoseconds. + Weight::from_parts(370_617_000, 0) + .saturating_add(Weight::from_parts(0, 48314)) + .saturating_add(T::DbWeight::get().reads(17)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -247,13 +265,13 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// The range of component `v` is `[101, 200]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `42829` - // Estimated: `48769` - // Minimum execution time: 1_305_254_000 picoseconds. - Weight::from_parts(1_347_160_667, 0) - .saturating_add(Weight::from_parts(0, 48769)) - // Standard Error: 22_128 - .saturating_add(Weight::from_parts(57_229, 0).saturating_mul(v.into())) + // Measured: `42830` + // Estimated: `48770` + // Minimum execution time: 1_322_051_000 picoseconds. + Weight::from_parts(1_379_846_608, 0) + .saturating_add(Weight::from_parts(0, 48770)) + // Standard Error: 19_959 + .saturating_add(Weight::from_parts(24_630, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(26)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -323,11 +341,11 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `42842` - // Estimated: `48782` - // Minimum execution time: 38_637_547_000 picoseconds. - Weight::from_parts(41_447_412_000, 0) - .saturating_add(Weight::from_parts(0, 48782)) + // Measured: `42843` + // Estimated: `48783` + // Minimum execution time: 37_550_515_000 picoseconds. + Weight::from_parts(37_886_489_000, 0) + .saturating_add(Weight::from_parts(0, 48783)) .saturating_add(T::DbWeight::get().reads(28)) .saturating_add(T::DbWeight::get().writes(15)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs deleted file mode 100644 index da1b7a0dad9ad9ef93a6d78e564ff6a096add497..0000000000000000000000000000000000000000 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_inclusion.rs +++ /dev/null @@ -1,74 +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 `runtime_parachains::inclusion` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=rococo-dev -// --steps=50 -// --repeat=20 -// --pallet=runtime_parachains::inclusion -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_inclusion.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `runtime_parachains::inclusion`. -pub struct WeightInfo(PhantomData); -impl polkadot_runtime_parachains::inclusion::WeightInfo for WeightInfo { - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:999) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `i` is `[1, 1000]`. - fn receive_upward_messages(i: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `33280` - // Estimated: `36283` - // Minimum execution time: 71_094_000 picoseconds. - Weight::from_parts(71_436_000, 0) - .saturating_add(Weight::from_parts(0, 36283)) - // Standard Error: 22_149 - .saturating_add(Weight::from_parts(51_495_472, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) - } -} diff --git a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 60c40429b1ac323e69bca1345c41c3c04c221f79..7d743b209124a51c3a09edfd84fbff4d18eb665f 100644 --- a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_189_000 picoseconds. - Weight::from_parts(23_896_000, 3593) + // Minimum execution time: 30_672_000 picoseconds. + Weight::from_parts(31_677_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 50_299_000 picoseconds. - Weight::from_parts(50_962_000, 6196) + // Minimum execution time: 41_132_000 picoseconds. + Weight::from_parts(41_654_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -83,10 +83,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `243` + // Measured: `281` // Estimated: `6196` - // Minimum execution time: 71_748_000 picoseconds. - Weight::from_parts(74_072_000, 6196) + // Minimum execution time: 97_174_000 picoseconds. + Weight::from_parts(99_537_000, 6196) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -105,16 +105,18 @@ impl WeightInfo { /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 27_806_000 picoseconds. - Weight::from_parts(28_594_000, 3607) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 67_105_000 picoseconds. + Weight::from_parts(68_659_000, 3746) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -122,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_199_000 picoseconds. - Weight::from_parts(21_857_000, 3593) + // Minimum execution time: 30_780_000 picoseconds. + Weight::from_parts(31_496_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,27 +135,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 23_578_000 picoseconds. - Weight::from_parts(24_060_000, 3593) + // Minimum execution time: 23_411_000 picoseconds. + Weight::from_parts(23_891_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 48_522_000 picoseconds. - Weight::from_parts(49_640_000, 3607) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 61_541_000 picoseconds. + Weight::from_parts(63_677_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -169,10 +171,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 50_429_000 picoseconds. - Weight::from_parts(51_295_000, 3607) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 48_574_000 picoseconds. + Weight::from_parts(49_469_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8e34320d38f2f26edc800343cf37217977e8bad1..b032315691135a074b406f16decc0d83ff235d38 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -59,10 +59,14 @@ use pallet_session::historical as session_historical; use pallet_timestamp::Now; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, Balance, BlockNumber, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, SessionInfo as SessionInfoData, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; @@ -121,7 +125,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -978,7 +982,7 @@ sp_api::impl_runtime_apis! { runtime_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { runtime_impl::backing_state::(para_id) } diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 4226595cd2ffb25cc753f85583724bc1222b2f7e..83c0eb037f4a15de4634ccce14e1c7573e2671c1 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -358,4 +358,4 @@ runtime-metrics = [ # A feature that should be enabled when the runtime should be built for on-chain # deployment. This will disable stuff that shouldn't be part of the on-chain wasm # to make it smaller, like logging for example. -on-chain-release-build = ["metadata-hash", "sp-api/disable-logging"] +on-chain-release-build = ["metadata-hash"] diff --git a/polkadot/runtime/westend/src/impls.rs b/polkadot/runtime/westend/src/impls.rs index 11665953bd8e17b2f0368602b176a19046d44be9..ac3f9e679f8d3d5d7b1409a858598cc866f7c415 100644 --- a/polkadot/runtime/westend/src/impls.rs +++ b/polkadot/runtime/westend/src/impls.rs @@ -90,7 +90,7 @@ where fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult { use crate::{ impls::IdentityMigratorCalls::PokeDeposit, - weights::runtime_common_identity_migrator::WeightInfo as MigratorWeights, + weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights, }; let total_to_send = Self::calculate_remote_deposit(fields, subs); diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 838ba17e5613d22df1248052623777857285f44f..d0c1cd89de32292e7505076bcf006e060464c56c 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, @@ -65,7 +69,7 @@ use polkadot_runtime_common::{ VersionedLocatableAsset, VersionedLocationConverter, }, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, - traits::{Leaser, OnSwap}, + traits::OnSwap, BalanceToU256, BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance, }; @@ -99,7 +103,7 @@ use sp_runtime::{ IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, + ApplyExtrinsicResult, FixedU128, KeyTypeId, Percent, Permill, }; use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] @@ -171,7 +175,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -461,6 +465,7 @@ impl pallet_beefy_mmr::Config for Runtime { type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = H256; type BeefyDataProvider = ParaHeadsRootProvider; + type WeightInfo = weights::pallet_beefy_mmr::WeightInfo; } parameter_types! { @@ -601,20 +606,20 @@ impl pallet_election_provider_multi_phase::MinerConfig for Runtime { type MaxWeight = OffchainSolutionWeightLimit; type Solution = NposCompactSolution16; type MaxVotesPerVoter = < - ::DataProvider - as - frame_election_provider_support::ElectionDataProvider - >::MaxVotesPerVoter; + ::DataProvider + as + frame_election_provider_support::ElectionDataProvider + >::MaxVotesPerVoter; type MaxWinners = MaxActiveValidators; // The unsigned submissions have to respect the weight of the submit_unsigned call, thus their // weight estimate function is wired to this call's weight. fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight { < - ::WeightInfo - as - pallet_election_provider_multi_phase::WeightInfo - >::submit_unsigned(v, t, a, d) + ::WeightInfo + as + pallet_election_provider_multi_phase::WeightInfo + >::submit_unsigned(v, t, a, d) } } @@ -1177,7 +1182,7 @@ impl pallet_proxy::Config for Runtime { impl parachains_origin::Config for Runtime {} impl parachains_configuration::Config for Runtime { - type WeightInfo = weights::runtime_parachains_configuration::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_configuration::WeightInfo; } impl parachains_shared::Config for Runtime { @@ -1193,7 +1198,7 @@ impl parachains_inclusion::Config for Runtime { type DisputesHandler = ParasDisputes; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; type MessageQueue = MessageQueue; - type WeightInfo = weights::runtime_parachains_inclusion::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_inclusion::WeightInfo; } parameter_types! { @@ -1202,7 +1207,7 @@ parameter_types! { impl parachains_paras::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::runtime_parachains_paras::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras::WeightInfo; type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; @@ -1276,11 +1281,11 @@ impl parachains_hrmp::Config for Runtime { HrmpChannelSizeAndCapacityWithSystemRatio, >; type VersionWrapper = crate::XcmPallet; - type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_hrmp::WeightInfo; } impl parachains_paras_inherent::Config for Runtime { - type WeightInfo = weights::runtime_parachains_paras_inherent::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_paras_inherent::WeightInfo; } impl parachains_scheduler::Config for Runtime { @@ -1309,7 +1314,7 @@ impl coretime::Config for Runtime { type Currency = Balances; type BrokerId = BrokerId; type BrokerPotLocation = BrokerPot; - type WeightInfo = weights::runtime_parachains_coretime::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_coretime::WeightInfo; type SendXcm = crate::xcm_config::XcmRouter; type AssetTransactor = crate::xcm_config::LocalAssetTransactor; type AccountToLocation = xcm_builder::AliasesIntoAccountId32< @@ -1330,7 +1335,7 @@ impl parachains_on_demand::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type TrafficDefaultValue = OnDemandTrafficDefaultValue; - type WeightInfo = weights::runtime_parachains_on_demand::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_on_demand::WeightInfo; type MaxHistoricalRevenue = MaxHistoricalRevenue; type PalletId = OnDemandPalletId; } @@ -1340,7 +1345,7 @@ impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type ForceOrigin = EnsureRoot; - type WeightInfo = weights::runtime_parachains_initializer::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_initializer::WeightInfo; type CoretimeOnNewSession = Coretime; } @@ -1359,14 +1364,14 @@ impl assigned_slots::Config for Runtime { type PermanentSlotLeasePeriodLength = PermanentSlotLeasePeriodLength; type TemporarySlotLeasePeriodLength = TemporarySlotLeasePeriodLength; type MaxTemporarySlotPerLeasePeriod = MaxTemporarySlotPerLeasePeriod; - type WeightInfo = weights::runtime_common_assigned_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_assigned_slots::WeightInfo; } impl parachains_disputes::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RewardValidators = parachains_reward_points::RewardValidatorsWithEraPoints; type SlashingHandler = parachains_slashing::SlashValidatorsForDisputes; - type WeightInfo = weights::runtime_parachains_disputes::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_disputes::WeightInfo; } impl parachains_slashing::Config for Runtime { @@ -1382,7 +1387,7 @@ impl parachains_slashing::Config for Runtime { Offences, ReportLongevity, >; - type WeightInfo = weights::runtime_parachains_disputes_slashing::WeightInfo; + type WeightInfo = weights::polkadot_runtime_parachains_disputes_slashing::WeightInfo; type BenchmarkingConfig = parachains_slashing::BenchConfig<300>; } @@ -1398,7 +1403,7 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; - type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_paras_registrar::WeightInfo; } parameter_types! { @@ -1412,7 +1417,7 @@ impl slots::Config for Runtime { type LeasePeriod = LeasePeriod; type LeaseOffset = (); type ForceOrigin = EitherOf, LeaseAdmin>; - type WeightInfo = weights::runtime_common_slots::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_slots::WeightInfo; } parameter_types! { @@ -1433,7 +1438,7 @@ impl crowdloan::Config for Runtime { type Registrar = Registrar; type Auctioneer = Auctions; type MaxMemoLength = MaxMemoLength; - type WeightInfo = weights::runtime_common_crowdloan::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_crowdloan::WeightInfo; } parameter_types! { @@ -1452,14 +1457,14 @@ impl auctions::Config for Runtime { type SampleLength = SampleLength; type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type InitiateOrigin = EitherOf, AuctionAdmin>; - type WeightInfo = weights::runtime_common_auctions::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_auctions::WeightInfo; } impl identity_migrator::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reaper = EnsureSigned; type ReapIdentityHandler = ToParachainIdentityReaper; - type WeightInfo = weights::runtime_common_identity_migrator::WeightInfo; + type WeightInfo = weights::polkadot_runtime_common_identity_migrator::WeightInfo; } parameter_types! { @@ -1759,11 +1764,8 @@ pub type SignedExtra = ( ); parameter_types! { - // This is the max pools that will be migrated in the runtime upgrade. Westend has more pools - // than this, but we want to emulate some non migrated pools. In prod runtimes, if weight is not - // a concern, it is recommended to set to (existing pools + 10) to also account for any new - // pools getting created before the migration is actually executed. - pub const MaxPoolsToMigrate: u32 = 250; + /// Bounding number of agent pot accounts to be migrated in a single block. + pub const MaxAgentsToMigrate: u32 = 300; } /// All migrations that will run on the next runtime upgrade. @@ -1777,33 +1779,13 @@ pub type Migrations = migrations::Unreleased; pub mod migrations { use super::*; - pub struct GetLegacyLeaseImpl; - impl coretime::migration::GetLegacyLease for GetLegacyLeaseImpl { - fn get_parachain_lease_in_blocks(para: ParaId) -> Option { - let now = frame_system::Pallet::::block_number(); - let lease = slots::Leases::::get(para); - if lease.is_empty() { - return None; - } - // Lease not yet started, ignore: - if lease.iter().any(Option::is_none) { - return None; - } - let (index, _) = - as Leaser>::lease_period_index(now)?; - Some(index.saturating_add(lease.len() as u32).saturating_mul(LeasePeriod::get())) - } - } - /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - // Migrate NominationPools to `DelegateStake` adapter. This is unversioned upgrade and - // should not be applied yet in Kusama/Polkadot. - pallet_nomination_pools::migration::unversioned::DelegationStakeMigration< + // This is only needed for Westend. + pallet_delegated_staking::migration::unversioned::ProxyDelegatorMigration< Runtime, - MaxPoolsToMigrate, + MaxAgentsToMigrate, >, - pallet_staking::migrations::v15::MigrateV14ToV15, ); } @@ -1847,6 +1829,7 @@ mod benches { // Substrate [pallet_bags_list, VoterList] [pallet_balances, Balances] + [pallet_beefy_mmr, BeefyMmrLeaf] [pallet_conviction_voting, ConvictionVoting] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] [frame_election_provider_support, ElectionProviderBench::] @@ -2085,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) } @@ -2474,6 +2457,14 @@ sp_api::impl_runtime_apis! { fn member_needs_delegate_migration(member: AccountId) -> bool { NominationPools::api_member_needs_delegate_migration(member) } + + fn member_total_balance(member: AccountId) -> Balance { + NominationPools::api_member_total_balance(member) + } + + fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance { + NominationPools::api_pool_balance(pool_id) + } } impl pallet_staking_runtime_api::StakingApi for Runtime { @@ -2774,45 +2765,6 @@ sp_api::impl_runtime_apis! { } } -#[cfg(all(test, feature = "try-runtime"))] -mod remote_tests { - use super::*; - use frame_try_runtime::{runtime_decl_for_try_runtime::TryRuntime, UpgradeCheckSelect}; - use remote_externalities::{ - Builder, Mode, OfflineConfig, OnlineConfig, SnapshotConfig, Transport, - }; - use std::env::var; - - #[tokio::test] - async fn run_migrations() { - if var("RUN_MIGRATION_TESTS").is_err() { - return; - } - - sp_tracing::try_init_simple(); - let transport: Transport = - var("WS").unwrap_or("wss://westend-rpc.polkadot.io:443".to_string()).into(); - let maybe_state_snapshot: Option = var("SNAP").map(|s| s.into()).ok(); - let mut ext = Builder::::default() - .mode(if let Some(state_snapshot) = maybe_state_snapshot { - Mode::OfflineOrElseOnline( - OfflineConfig { state_snapshot: state_snapshot.clone() }, - OnlineConfig { - transport, - state_snapshot: Some(state_snapshot), - ..Default::default() - }, - ) - } else { - Mode::Online(OnlineConfig { transport, ..Default::default() }) - }) - .build() - .await - .unwrap(); - ext.execute_with(|| Runtime::on_runtime_upgrade(UpgradeCheckSelect::PreAndPost)); - } -} - mod clean_state_migration { use super::Runtime; #[cfg(feature = "try-runtime")] diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index 4d5e2e946bce6e1cf26f061309b1a1000a3d1853..dc8103ab52c474a8f24b288add5b0cd9880265fc 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -99,3 +99,140 @@ fn check_treasury_pallet_id() { westend_runtime_constants::TREASURY_PALLET_ID ); } + +#[cfg(all(test, feature = "try-runtime"))] +mod remote_tests { + use super::*; + use frame_try_runtime::{runtime_decl_for_try_runtime::TryRuntime, UpgradeCheckSelect}; + use remote_externalities::{ + Builder, Mode, OfflineConfig, OnlineConfig, SnapshotConfig, Transport, + }; + use std::env::var; + + #[tokio::test] + async fn run_migrations() { + if var("RUN_MIGRATION_TESTS").is_err() { + return; + } + + sp_tracing::try_init_simple(); + let transport: Transport = + var("WS").unwrap_or("wss://westend-rpc.polkadot.io:443".to_string()).into(); + let maybe_state_snapshot: Option = var("SNAP").map(|s| s.into()).ok(); + let mut ext = Builder::::default() + .mode(if let Some(state_snapshot) = maybe_state_snapshot { + Mode::OfflineOrElseOnline( + OfflineConfig { state_snapshot: state_snapshot.clone() }, + OnlineConfig { + transport, + state_snapshot: Some(state_snapshot), + ..Default::default() + }, + ) + } else { + Mode::Online(OnlineConfig { transport, ..Default::default() }) + }) + .build() + .await + .unwrap(); + ext.execute_with(|| Runtime::on_runtime_upgrade(UpgradeCheckSelect::PreAndPost)); + } + + #[tokio::test] + async fn delegate_stake_migration() { + // Intended to be run only manually. + if var("RUN_MIGRATION_TESTS").is_err() { + return; + } + use frame_support::assert_ok; + sp_tracing::try_init_simple(); + + let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9900".to_string()).into(); + let maybe_state_snapshot: Option = var("SNAP").map(|s| s.into()).ok(); + let mut ext = Builder::::default() + .mode(if let Some(state_snapshot) = maybe_state_snapshot { + Mode::OfflineOrElseOnline( + OfflineConfig { state_snapshot: state_snapshot.clone() }, + OnlineConfig { + transport, + state_snapshot: Some(state_snapshot), + pallets: vec![ + "staking".into(), + "system".into(), + "balances".into(), + "nomination-pools".into(), + "delegated-staking".into(), + ], + ..Default::default() + }, + ) + } else { + Mode::Online(OnlineConfig { transport, ..Default::default() }) + }) + .build() + .await + .unwrap(); + ext.execute_with(|| { + // create an account with some balance + let alice = AccountId::from([1u8; 32]); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&alice, 100_000 * UNITS); + + // iterate over all pools + pallet_nomination_pools::BondedPools::::iter_keys().for_each(|k| { + if pallet_nomination_pools::Pallet::::api_pool_needs_delegate_migration(k) + { + assert_ok!( + pallet_nomination_pools::Pallet::::migrate_pool_to_delegate_stake( + RuntimeOrigin::signed(alice.clone()).into(), + k, + ) + ); + } + }); + + // member migration stats + let mut success = 0; + let mut direct_stakers = 0; + let mut unexpected_errors = 0; + + // iterate over all pool members + pallet_nomination_pools::PoolMembers::::iter_keys().for_each(|k| { + if pallet_nomination_pools::Pallet::::api_member_needs_delegate_migration( + k.clone(), + ) { + // reasons migrations can fail: + let is_direct_staker = pallet_staking::Bonded::::contains_key(&k); + + let migration = pallet_nomination_pools::Pallet::::migrate_delegation( + RuntimeOrigin::signed(alice.clone()).into(), + sp_runtime::MultiAddress::Id(k.clone()), + ); + + if is_direct_staker { + // if the member is a direct staker, the migration should fail until pool + // member unstakes all funds from pallet-staking. + direct_stakers += 1; + assert_eq!( + migration.unwrap_err(), + pallet_delegated_staking::Error::::AlreadyStaking.into() + ); + } else if migration.is_err() { + unexpected_errors += 1; + log::error!(target: "remote_test", "Unexpected error {:?} while migrating {:?}", migration.unwrap_err(), k); + } else { + success += 1; + } + } + }); + + log::info!( + target: "remote_test", + "Migration stats: success: {}, direct_stakers: {}, unexpected_errors: {}", + success, + direct_stakers, + unexpected_errors + ); + }); + } +} diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index 2248e421e63948e19283fd8ceab2d70631062687..1e7b01bc472b2227ce489b4477018d7b4fabde53 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -20,6 +20,7 @@ pub mod frame_system; pub mod pallet_asset_rate; pub mod pallet_bags_list; pub mod pallet_balances; +pub mod pallet_beefy_mmr; pub mod pallet_conviction_voting; pub mod pallet_election_provider_multi_phase; pub mod pallet_fast_unstake; @@ -44,20 +45,20 @@ pub mod pallet_utility; pub mod pallet_vesting; pub mod pallet_whitelist; pub mod pallet_xcm; -pub mod runtime_common_assigned_slots; -pub mod runtime_common_auctions; -pub mod runtime_common_crowdloan; -pub mod runtime_common_identity_migrator; -pub mod runtime_common_paras_registrar; -pub mod runtime_common_slots; -pub mod runtime_parachains_configuration; -pub mod runtime_parachains_coretime; -pub mod runtime_parachains_disputes; -pub mod runtime_parachains_disputes_slashing; -pub mod runtime_parachains_hrmp; -pub mod runtime_parachains_inclusion; -pub mod runtime_parachains_initializer; -pub mod runtime_parachains_on_demand; -pub mod runtime_parachains_paras; -pub mod runtime_parachains_paras_inherent; +pub mod polkadot_runtime_common_assigned_slots; +pub mod polkadot_runtime_common_auctions; +pub mod polkadot_runtime_common_crowdloan; +pub mod polkadot_runtime_common_identity_migrator; +pub mod polkadot_runtime_common_paras_registrar; +pub mod polkadot_runtime_common_slots; +pub mod polkadot_runtime_parachains_configuration; +pub mod polkadot_runtime_parachains_coretime; +pub mod polkadot_runtime_parachains_disputes; +pub mod polkadot_runtime_parachains_disputes_slashing; +pub mod polkadot_runtime_parachains_hrmp; +pub mod polkadot_runtime_parachains_inclusion; +pub mod polkadot_runtime_parachains_initializer; +pub mod polkadot_runtime_parachains_on_demand; +pub mod polkadot_runtime_parachains_paras; +pub mod polkadot_runtime_parachains_paras_inherent; pub mod xcm; diff --git a/polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs b/polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs new file mode 100644 index 0000000000000000000000000000000000000000..5be207e3fcff484a789d209b3f3cdd7db7c49e15 --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_beefy_mmr.rs @@ -0,0 +1,89 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Autogenerated weights for `pallet_beefy_mmr` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_beefy_mmr +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_beefy_mmr`. +pub struct WeightInfo(PhantomData); +impl pallet_beefy_mmr::WeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_850_000 picoseconds. + Weight::from_parts(8_169_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `201` + // Estimated: `3505` + // Minimum execution time: 6_852_000 picoseconds. + Weight::from_parts(7_448_000, 0) + .saturating_add(Weight::from_parts(0, 3505)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `193` + // Estimated: `1517` + // Minimum execution time: 12_860_000 picoseconds. + Weight::from_parts(17_158_162, 0) + .saturating_add(Weight::from_parts(0, 1517)) + // Standard Error: 1_732 + .saturating_add(Weight::from_parts(1_489_410, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + } +} diff --git a/polkadot/runtime/westend/src/weights/runtime_common_assigned_slots.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_assigned_slots.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_assigned_slots.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_assigned_slots.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_auctions.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_auctions.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_auctions.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_auctions.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_crowdloan.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_crowdloan.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_crowdloan.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_crowdloan.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_identity_migrator.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_identity_migrator.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_identity_migrator.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_identity_migrator.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_paras_registrar.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_paras_registrar.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_common_slots.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_common_slots.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_common_slots.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_common_slots.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_configuration.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_configuration.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_configuration.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_coretime.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_coretime.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_coretime.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_disputes.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_disputes.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_disputes_slashing.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes_slashing.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_disputes_slashing.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_disputes_slashing.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_hrmp.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_hrmp.rs diff --git a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs new file mode 100644 index 0000000000000000000000000000000000000000..28d8aa8ea32335b77320b5bc249aa2d8386e740e --- /dev/null +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -0,0 +1,135 @@ +// 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 `polkadot_runtime_parachains::inclusion` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! 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: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=polkadot_runtime_parachains::inclusion +// --chain=westend-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `polkadot_runtime_parachains::inclusion`. +pub struct WeightInfo(PhantomData); +impl polkadot_runtime_parachains::inclusion::WeightInfo for WeightInfo { + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) + /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Session::Validators` (r:1 w:0) + /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Staking::ActiveEra` (r:1 w:0) + /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) + /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) + /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeCooldowns` (r:1 w:1) + /// Proof: `Paras::UpgradeCooldowns` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:2) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(131122), added: 133597, mode: `MaxEncodedLen`) + /// Storage: `Hrmp::HrmpChannelDigests` (r:3 w:3) + /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Hrmp::HrmpChannels` (r:2 w:2) + /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// 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`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) + /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// The range of component `u` is `[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: `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((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/runtime/westend/src/weights/runtime_parachains_initializer.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_initializer.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_initializer.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_initializer.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_on_demand.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_on_demand.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_on_demand.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_on_demand.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_paras.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras.rs similarity index 100% rename from polkadot/runtime/westend/src/weights/runtime_parachains_paras.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras.rs diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs similarity index 82% rename from polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs rename to polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs index 74dd55cc3f2cacd2b82bd60fe76b3060cc5f6c6c..32f6f28f242621f1cf496046fbdf94f3ba7b819c 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Autogenerated weights for `runtime_parachains::paras_inherent` +//! Autogenerated weights for `polkadot_runtime_parachains::paras_inherent` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-03-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -32,7 +32,7 @@ // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_parachains::paras_inherent +// --pallet=polkadot_runtime_parachains::paras_inherent // --chain=westend-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/westend/src/weights/ @@ -45,9 +45,49 @@ use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions for `runtime_parachains::paras_inherent`. +/// Weight functions for `polkadot_runtime_parachains::paras_inherent`. pub struct WeightInfo(PhantomData); impl polkadot_runtime_parachains::paras_inherent::WeightInfo for WeightInfo { + /// Storage: `ParaInherent::Included` (r:1 w:1) + /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::ParentHash` (r:1 w:0) + /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) + /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) + /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) + /// Proof: `Babe::AuthorVrfRandomness` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`) + /// Storage: `ParaInherent::OnChainVotes` (r:1 w:1) + /// Proof: `ParaInherent::OnChainVotes` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasDisputes::Frozen` (r:1 w:0) + /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaInclusion::V1` (r:1 w:0) + /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) + /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Session::DisabledValidators` (r:1 w:0) + /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn enter_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `37553` + // Estimated: `41018` + // Minimum execution time: 237_414_000 picoseconds. + Weight::from_parts(245_039_000, 0) + .saturating_add(Weight::from_parts(0, 41018)) + .saturating_add(T::DbWeight::get().reads(15)) + .saturating_add(T::DbWeight::get().writes(5)) + } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `System::ParentHash` (r:1 w:0) @@ -112,19 +152,19 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[10, 200]`. + /// The range of component `v` is `[10, 1024]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `67518` - // Estimated: `73458 + v * (23 ±0)` - // Minimum execution time: 844_022_000 picoseconds. - Weight::from_parts(456_682_337, 0) - .saturating_add(Weight::from_parts(0, 73458)) - // Standard Error: 16_403 - .saturating_add(Weight::from_parts(41_871_245, 0).saturating_mul(v.into())) + // Measured: `199504` + // Estimated: `205444 + v * (5 ±0)` + // Minimum execution time: 1_157_489_000 picoseconds. + Weight::from_parts(629_243_559, 0) + .saturating_add(Weight::from_parts(0, 205444)) + // Standard Error: 10_997 + .saturating_add(Weight::from_parts(50_752_930, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(28)) .saturating_add(T::DbWeight::get().writes(16)) - .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(0, 5).saturating_mul(v.into())) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -146,24 +186,6 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Frozen` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaInclusion::V1` (r:2 w:1) /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParaSessionInfo::AccountKeys` (r:1 w:0) - /// Proof: `ParaSessionInfo::AccountKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Session::Validators` (r:1 w:0) - /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `Staking::ActiveEra` (r:1 w:0) - /// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`) - /// Storage: `Staking::ErasRewardPoints` (r:1 w:1) - /// Proof: `Staking::ErasRewardPoints` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) - /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:1) - /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:1 w:1) - /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) - /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Disputes` (r:1 w:0) - /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) @@ -176,25 +198,15 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParasDisputes::Included` (r:0 w:1) - /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) - /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::Heads` (r:0 w:1) - /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) - /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Paras::MostRecentContext` (r:0 w:1) - /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `43196` - // Estimated: `49136` - // Minimum execution time: 438_637_000 picoseconds. - Weight::from_parts(458_342_000, 0) - .saturating_add(Weight::from_parts(0, 49136)) - .saturating_add(T::DbWeight::get().reads(26)) - .saturating_add(T::DbWeight::get().writes(16)) + // Measured: `75131` + // Estimated: `81071` + // Minimum execution time: 466_928_000 picoseconds. + Weight::from_parts(494_342_000, 0) + .saturating_add(Weight::from_parts(0, 81071)) + .saturating_add(T::DbWeight::get().reads(17)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -262,16 +274,16 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[101, 200]`. + /// The range of component `v` is `[2, 5]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `43269` - // Estimated: `49209` - // Minimum execution time: 5_955_361_000 picoseconds. - Weight::from_parts(1_285_398_956, 0) - .saturating_add(Weight::from_parts(0, 49209)) - // Standard Error: 57_369 - .saturating_add(Weight::from_parts(47_073_853, 0).saturating_mul(v.into())) + // Measured: `76369` + // Estimated: `82309` + // Minimum execution time: 1_468_919_000 picoseconds. + Weight::from_parts(1_433_315_477, 0) + .saturating_add(Weight::from_parts(0, 82309)) + // Standard Error: 419_886 + .saturating_add(Weight::from_parts(42_880_485, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(29)) .saturating_add(T::DbWeight::get().writes(16)) } @@ -347,11 +359,11 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `43282` - // Estimated: `49222` - // Minimum execution time: 42_128_606_000 picoseconds. - Weight::from_parts(42_822_806_000, 0) - .saturating_add(Weight::from_parts(0, 49222)) + // Measured: `76382` + // Estimated: `82322` + // Minimum execution time: 34_577_233_000 picoseconds. + Weight::from_parts(39_530_352_000, 0) + .saturating_add(Weight::from_parts(0, 82322)) .saturating_add(T::DbWeight::get().reads(31)) .saturating_add(T::DbWeight::get().writes(16)) } diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_inclusion.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_inclusion.rs deleted file mode 100644 index 25909beb6a071ea0d5e2a0dd35ee1be2ca0ed879..0000000000000000000000000000000000000000 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_inclusion.rs +++ /dev/null @@ -1,75 +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 `runtime_parachains::inclusion` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner--ss9ysm1-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=westend-dev -// --steps=50 -// --repeat=20 -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --pallet=runtime_parachains::inclusion -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/westend/src/weights/runtime_parachains_inclusion.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `runtime_parachains::inclusion`. -pub struct WeightInfo(PhantomData); -impl polkadot_runtime_parachains::inclusion::WeightInfo for WeightInfo { - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:999) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(131122), added: 133597, mode: MaxEncodedLen) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `i` is `[1, 1000]`. - fn receive_upward_messages(i: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `131297` - // Estimated: `134587` - // Minimum execution time: 209_898_000 picoseconds. - Weight::from_parts(210_955_000, 0) - .saturating_add(Weight::from_parts(0, 134587)) - // Standard Error: 97_069 - .saturating_add(Weight::from_parts(207_030_437, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(3)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) - } -} diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 9939f16aa29f84fb0df68f658fc8cef44c52eea3..e0c61c8e2bf2583733954469f5cff132a8f49406 100644 --- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 24_815_000 picoseconds. - Weight::from_parts(25_098_000, 3593) + // Minimum execution time: 31_780_000 picoseconds. + Weight::from_parts(32_602_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,12 +66,12 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 51_268_000 picoseconds. - Weight::from_parts(51_857_000, 6196) + // Minimum execution time: 41_818_000 picoseconds. + Weight::from_parts(42_902_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `System::Account` (r:2 w:2) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -83,12 +83,12 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `6196` - // Minimum execution time: 74_113_000 picoseconds. - Weight::from_parts(74_721_000, 6196) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `351` + // Estimated: `8799` + // Minimum execution time: 101_949_000 picoseconds. + Weight::from_parts(104_190_000, 8799) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Benchmark::Override` (r:0 w:0) /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -105,16 +105,18 @@ impl WeightInfo { /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 28_919_000 picoseconds. - Weight::from_parts(29_703_000, 3574) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 70_123_000 picoseconds. + Weight::from_parts(72_564_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -122,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_685_000 picoseconds. - Weight::from_parts(22_528_000, 3593) + // Minimum execution time: 31_868_000 picoseconds. + Weight::from_parts(32_388_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,27 +135,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 25_192_000 picoseconds. - Weight::from_parts(25_445_000, 3593) + // Minimum execution time: 24_532_000 picoseconds. + Weight::from_parts(25_166_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3593` - // Minimum execution time: 49_349_000 picoseconds. - Weight::from_parts(50_476_000, 3593) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 63_378_000 picoseconds. + Weight::from_parts(65_002_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -169,10 +171,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3593` - // Minimum execution time: 51_386_000 picoseconds. - Weight::from_parts(52_141_000, 3593) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 49_174_000 picoseconds. + Weight::from_parts(50_356_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/statement-table/src/generic.rs b/polkadot/statement-table/src/generic.rs index e96ed6af73d9163678906f54efc064ccde62c602..1e90338a0f18a803e572c8174de4460a3b888e33 100644 --- a/polkadot/statement-table/src/generic.rs +++ b/polkadot/statement-table/src/generic.rs @@ -477,10 +477,7 @@ impl Table { if !context.is_member_of(&from, &votes.group_id) { let sig = match vote { ValidityVote::Valid(s) => s, - ValidityVote::Issued(_) => panic!( - "implicit issuance vote only cast from `import_candidate` after \ - checking group membership of issuer; qed" - ), + ValidityVote::Issued(s) => s, }; return Err(Misbehavior::UnauthorizedStatement(UnauthorizedStatement { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 6451901279b166e116dd8dcf1d204da74db98d35..05d9046ab192ae160c68b10f865ce587bb9bd4e0 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -2457,10 +2457,14 @@ impl Pallet { ::RuntimeOrigin: From, { crate::Pallet::::set_record_xcm(true); - frame_system::Pallet::::reset_events(); // To make sure we only record events from current call. + // Clear other messages in queues... + Router::clear_messages(); + // ...and reset events to make sure we only record events from current call. + frame_system::Pallet::::reset_events(); let result = call.dispatch(origin.into()); crate::Pallet::::set_record_xcm(false); let local_xcm = crate::Pallet::::recorded_xcm(); + // Should only get messages from this call since we cleared previous ones. let forwarded_xcms = Router::get_messages(); let events: Vec<::RuntimeEvent> = frame_system::Pallet::::read_events_no_consensus() diff --git a/polkadot/xcm/xcm-builder/src/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/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/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index 8aa9602fcc297e9a402f7df11188773466b6ac90..30e0b7c72b03e4a93b6ce35995c2e9df1bb27212 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -340,6 +340,10 @@ impl InspectMessageQueues for SovereignPaidRemoteExporter { + fn clear_messages() { + Router::clear_messages() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { Router::get_messages() } diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index 279d7118f8cf90cd5b8666ada2c7a8fbc5d4c94a..7683c6025392a824200afc779f4e5ec1a700dc53 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -34,7 +34,7 @@ use xcm_executor::traits::WeightBounds; #[test] fn basic_buy_fees_message_executes() { sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let msg = Xcm(vec![ WithdrawAsset((Parent, 100).into()), @@ -75,7 +75,7 @@ fn basic_buy_fees_message_executes() { #[test] fn transact_recursion_limit_works() { sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let base_xcm = |call: polkadot_test_runtime::RuntimeCall| { Xcm(vec![ @@ -174,7 +174,7 @@ fn query_response_fires() { use polkadot_test_runtime::RuntimeEvent::TestNotifier; sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let mut block_builder = client.init_polkadot_block_builder(); @@ -256,7 +256,7 @@ fn query_response_elicits_handler() { use polkadot_test_runtime::RuntimeEvent::TestNotifier; sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let mut block_builder = client.init_polkadot_block_builder(); @@ -332,7 +332,7 @@ fn query_response_elicits_handler() { #[test] fn deposit_reserve_asset_works_for_any_xcm_sender() { sp_tracing::try_init_simple(); - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); // Init values for the simulated origin Parachain let amount_to_send: u128 = 1_000_000_000_000; diff --git a/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 1daf5ae750cfbd0bb5a407eceb8d621023586fe0..a8110ca3d19f7e622b7fe1facbd9c656d29d36ac 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, @@ -809,7 +894,7 @@ impl XcmExecutor { }; let actual_weight = maybe_actual_weight.unwrap_or(weight); let surplus = weight.saturating_sub(actual_weight); - // We assume that the `Config::Weigher` will counts the `require_weight_at_most` + // We assume that the `Config::Weigher` will count the `require_weight_at_most` // for the estimate of how much weight this instruction will take. Now that we know // that it's less, we credit it. // @@ -858,14 +943,7 @@ impl XcmExecutor { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { let deposited = self.holding.saturating_take(assets); - for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset( - &asset, - &beneficiary, - Some(&self.context), - )?; - } - Ok(()) + self.deposit_assets_with_retry(&deposited, &beneficiary) }); if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { self.holding = old_holding; @@ -885,22 +963,34 @@ 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); - for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; - } + 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 // assets. let assets = Self::reanchored(deposited, &dest, None); let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - // put back transport_fee in holding register to be charged by XcmSender - self.holding.subsume_assets(transport_fee); + // 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(()) }); @@ -978,6 +1068,10 @@ impl XcmExecutor { // should be executed. let Some(weight) = Option::::from(weight_limit) else { return Ok(()) }; let old_holding = self.holding.clone(); + // Save the asset being used for execution fees, so we later know what should be + // used for delivery fees. + self.asset_used_for_fees = Some(fees.id.clone()); + tracing::trace!(target: "xcm::executor::BuyExecution", asset_used_for_fees = ?self.asset_used_for_fees); // pay for `weight` using up to `fees` of the holding register. let max_fee = self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; @@ -1282,4 +1376,46 @@ impl XcmExecutor { }), } } + + /// Deposit `to_deposit` assets to `beneficiary`, without giving up on the first (transient) + /// error, and retrying once just in case one of the subsequently deposited assets satisfy some + /// requirement. + /// + /// Most common transient error is: `beneficiary` account does not yet exist and the first + /// asset(s) in the (sorted) list does not satisfy ED, but a subsequent one in the list does. + /// + /// This function can write into storage and also return an error at the same time, it should + /// always be called within a transactional context. + fn deposit_assets_with_retry( + &mut self, + to_deposit: &AssetsInHolding, + beneficiary: &Location, + ) -> Result<(), XcmError> { + let mut failed_deposits = Vec::with_capacity(to_deposit.len()); + + let mut deposit_result = Ok(()); + for asset in to_deposit.assets_iter() { + deposit_result = + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context)); + // if deposit failed for asset, mark it for retry after depositing the others. + if deposit_result.is_err() { + failed_deposits.push(asset); + } + } + if failed_deposits.len() == to_deposit.len() { + tracing::debug!( + target: "xcm::execute", + ?deposit_result, + "Deposit for each asset failed, returning the last error as there is no point in retrying any of them", + ); + return deposit_result; + } + tracing::trace!(target: "xcm::execute", ?failed_deposits, "Deposits to retry"); + + // retry previously failed deposits, this time short-circuiting on any error. + for asset in failed_deposits { + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context))?; + } + Ok(()) + } } diff --git a/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js b/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js index 6583173e40c38a71d4c0091b10814ff72593dd15..20d0c2a988b1d274a092296be8db5f53a632d931 100644 --- a/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js +++ b/polkadot/zombienet_tests/functional/0003-mmr-generate-and-verify-proof.js @@ -3,9 +3,9 @@ const common = require('./0003-common.js'); async function run(nodeName, networkInfo, nodeNames) { const apis = await common.getApis(networkInfo, nodeNames); - const proof = await apis[nodeName].rpc.mmr.generateProof([1, 9, 20]); - - const root = await apis[nodeName].rpc.mmr.root() + let at = await apis[nodeName].rpc.chain.getBlockHash(21); + const root = await apis[nodeName].rpc.mmr.root(at); + const proof = await apis[nodeName].rpc.mmr.generateProof([1, 9, 20], 21, at); const proofVerifications = await Promise.all( Object.values(apis).map(async (api) => { diff --git a/prdoc/pr_4791.prdoc b/prdoc/1.15.1/pr_4791.prdoc similarity index 100% rename from prdoc/pr_4791.prdoc rename to prdoc/1.15.1/pr_4791.prdoc diff --git a/prdoc/1.15.1/pr_4937.prdoc b/prdoc/1.15.1/pr_4937.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..37b7bc3dda5943550a3e822b5ac3df8b83c4f8d7 --- /dev/null +++ b/prdoc/1.15.1/pr_4937.prdoc @@ -0,0 +1,21 @@ +title: "prospective-parachains rework: take II" + +doc: + - audience: Node Dev + description: | + Add back support for backing parachain forks. Once a candidate reaches the backing quorum, + validators use a shared way of picking the winning fork to back on-chain. This was done in + order to increase the likelihood that all backers will vote on the winning fork. + The functionality of backing unconnected candidates introduced by the previous rework is preserved. + +crates: + - name: polkadot-node-core-prospective-parachains + bump: minor + - name: polkadot-node-subsystem-types + bump: minor + - name: polkadot-node-subsystem-util + bump: minor + - name: polkadot-node-core-provisioner + bump: none + - name: polkadot-statement-distribution + bump: none diff --git a/prdoc/1.15.1/pr_5273.prdoc b/prdoc/1.15.1/pr_5273.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..981172c6c13fa98be6ba3e30535ac37d40eb4a55 --- /dev/null +++ b/prdoc/1.15.1/pr_5273.prdoc @@ -0,0 +1,10 @@ +title: Fix storage weight reclaim bug. + +doc: + - audience: Runtime Dev + description: | + A bug in storage weight reclaim signed extension is fixed. The bug was causing an underestimate of the proof size when the post dispatch info was underestimating the proof size and the pre dispatch info was overestimating the proof size at the same time. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch diff --git a/prdoc/1.15.1/pr_5281.prdoc b/prdoc/1.15.1/pr_5281.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..60feab412affd84a930ba06f4dd5fbb1fa471af9 --- /dev/null +++ b/prdoc/1.15.1/pr_5281.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: PoV-Reclaim - Set `BlockWeight` to node-side PoV size if mismatch is detected + +doc: + - audience: Runtime Dev + description: | + After this change, the `StorageWeightReclaim` `SignedExtension` will check the node-side PoV size after every + extrinsic. If we detect a case where the returned proof size is higher than the `BlockWeight` value of the + runtime, we set `BlockWeight` to the size returned from the node. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch + - name: frame-system + bump: minor diff --git a/prdoc/1.15.1/pr_5321.prdoc b/prdoc/1.15.1/pr_5321.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..97f75d28dd521ee8a01384af5df5b20047f4465f --- /dev/null +++ b/prdoc/1.15.1/pr_5321.prdoc @@ -0,0 +1,11 @@ +title: fix availability-distribution Jaeger spans memory leak + +doc: + - audience: Node Dev + description: | + Fixes a memory leak which caused the Jaeger span storage in availability-distribution to never be pruned and therefore increasing indefinitely. + This was caused by improper handling of finalized heads. More info in https://github.com/paritytech/polkadot-sdk/issues/5258 + +crates: + - name: polkadot-availability-distribution + bump: patch diff --git a/prdoc/pr_3049.prdoc b/prdoc/pr_3049.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9cead8e2a4e5c6b0232f66c96a87f5efe41dd18f --- /dev/null +++ b/prdoc/pr_3049.prdoc @@ -0,0 +1,11 @@ +title: "Fix treasury benchmarks when `SpendOrigin` being `None`" + +doc: + - audience: Runtime Dev + description: | + Fix treasury benchmarks when `SpendOrigin` not returning any succesful origin. + This is for example the case when `SpendOrigin` is set to `NeverOrigin`. + +crates: + - name: pallet-treasury + bump: patch diff --git a/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_3996.prdoc b/prdoc/pr_3996.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7590d8992368d61936df1f5a82f4cdddb9dc115a --- /dev/null +++ b/prdoc/pr_3996.prdoc @@ -0,0 +1,20 @@ +title: asset-hub-rococo - genesis config presets added + +doc: + - audience: Node Dev + description: | + `asset-hub-rococo` genesis state was moved to runtime. + - audience: Runtime Dev + description: | + `asset-hub-rococo` genesis state was moved to runtime. + + +crates: + - name: parachains-common + bump: minor + - name: testnet-parachains-constants + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: polkadot-parachain-bin + bump: minor diff --git a/prdoc/pr_4129.prdoc b/prdoc/pr_4129.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dfcc9b9ef0307362d7be059271d370afd15cd7c1 --- /dev/null +++ b/prdoc/pr_4129.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Update ForeignAssets from xcm::v3::Location to xcm::v4::Location + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + As a stepping stone for XCMv5, the foreign asset ids have been updated from v3::Location to v4::Location. + +crates: + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor diff --git a/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_4460.prdoc b/prdoc/pr_4460.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..81636c3313fcff94418aa322e1d38fa2a5c9da29 --- /dev/null +++ b/prdoc/pr_4460.prdoc @@ -0,0 +1,24 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "xcm-executor: allow deposit of multiple assets if at least one of them satisfies ED" + +doc: + - audience: Runtime Dev + description: | + XCM programs that deposit assets to some new (empty) account will now succeed if at least + one of the deposited assets satisfies ED. Before this change, the requirement was that the + _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it + is not reliable. Now, ordering doesn't matter, any one(s) of them can satisfy ED for the + whole deposit to work. + - audience: Runtime User + description: | + XCM programs that deposit assets to some new (empty) account will now succeed if at least + one of the deposited assets satisfies ED. Before this change, the requirement was that the + _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it + is not reliable. Now, ordering doesn't matter, any one(s) of them can satisfy ED for the + whole deposit to work. + +crates: + - name: staging-xcm-executor + bump: patch diff --git a/prdoc/pr_4739.prdoc b/prdoc/pr_4739.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9ca230e3e763ea3aca0ca598187d8e380c038607 --- /dev/null +++ b/prdoc/pr_4739.prdoc @@ -0,0 +1,15 @@ +title: parachain-template - genesis config presets added + +doc: + - audience: Node Dev + description: | + - common DEV_RUNTIME_PRESET ("development") const added to sp-genesis-builder. + - parachain-templates are now using presets. + +crates: + - name: sp-genesis-builder + bump: minor + - name: parachain-template-node + bump: patch + - name: parachain-template-runtime + bump: patch diff --git a/prdoc/pr_4792.prdoc b/prdoc/pr_4792.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5ce4303bcf7525c77f68754bde3b502bb68886e4 --- /dev/null +++ b/prdoc/pr_4792.prdoc @@ -0,0 +1,62 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "rpc: bind to `ipv6` if available and add `CLI --experimental-rpc-endpoint` to specify listen addr" + +doc: + - audience: Node Operator + description: | + This PR changes/adds the following: + + 1. The default setting is that substrate starts a rpc server that listens to localhost both ipv4 and ipv6 on the same port. + ipv6 is allowed to fail because some platforms may not support it + 2. A new RPC CLI option `--experimental-rpc-endpoint` is introduced which allows to configure arbitrary listen addresses including the port, + if this is enabled no other interfaces are enabled. + 3. If the local addr is not found for any of the sockets the server is not started and throws an error. + 4. Remove the deny_unsafe from the RPC implementations instead this is an extension to allow different polices for different interfaces/sockets + such one may enable unsafe on local interface and safe on only the external interface. + 5. This new `--experimental-rpc-endpoint` has several options and in the help menu all possible parameters are documented. + 6. The log emitted by jsonrpc server when it has been started has been modified to indicate all started rpc endpoints. + + So for instance it's now possible to start up three RPC endpoints as follows: + ``` + $ polkadot --experimental-rpc-endpoint "listen-addr=127.0.0.1:9944,methods=unsafe" --experimental-rpc-endpoint "listen-addr=0.0.0.0:9945,methods=safe,rate-limit=100" --experimental-rpc-endpoint "listen-addr=[::1]:9944,optional=true" + ``` + +crates: + - name: sc-rpc-server + bump: major + - name: sc-rpc + bump: major + - name: sc-cli + bump: major + - name: sc-service + bump: major + - name: sc-rpc-api + bump: patch + - name: polkadot-dispute-distribution + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: substrate-frame-rpc-system + bump: major + - name: substrate-state-trie-migration-rpc + bump: major + - name: cumulus-client-cli + bump: major + validate: false + - name: sc-consensus-beefy-rpc + bump: major + validate: false + - name: sc-consensus-grandpa-rpc + bump: major + validate: false + - name: sc-consensus-babe-rpc + bump: major + validate: false + - name: polkadot-rpc + bump: major + validate: false + - name: polkadot-service + bump: major + validate: false diff --git a/prdoc/pr_4822.prdoc b/prdoc/pr_4822.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..44f3e41d8d5afd3a2859eeb68d51eb2ad49de387 --- /dev/null +++ b/prdoc/pr_4822.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Ensure as many as possible pool members can migrate to `DelegateStake` + +doc: + - audience: Runtime Dev + description: | + 1. Allows pool members to use their total balance while joining pool with `DelegateStake`. + 2. Gates call mutating pool or member in unmigrated state. + 3. Runtime apis for reading pool and member balance. + +crates: + - name: westend-runtime + bump: minor + - name: kitchensink-runtime + bump: patch + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: minor + - name: sp-staking + bump: patch + - name: pallet-nomination-pools-runtime-api + bump: minor diff --git a/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_4851.prdoc b/prdoc/pr_4851.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..923ca4bfff5df54d423e527df859c1c8a3df95f7 --- /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_4928.prdoc b/prdoc/pr_4928.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9935652dc511a47ccd0b4cedbd14090822f1eb00 --- /dev/null +++ b/prdoc/pr_4928.prdoc @@ -0,0 +1,28 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Move assignment VRF check and vote signature in approval-distribution + +doc: + - audience: Node Dev + description: | + This PR moves the assignment VRF check and approval vote signature from approval-voting into approval-distribution. + This optimization creates a better pipelining for processing new messages, because in this way approval-distribution + does not have to wait after approval-voting anymore and it will just notify it when it received a valid message that + is ready to be imported. + +crates: + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-approval-distribution + bump: major + - name: polkadot-node-core-approval-voting + bump: major + - name: polkadot-node-primitives + bump: major + - name: polkadot-service + bump: major + - name: polkadot-subsystem-bench + bump: major + - name: polkadot-overseer + bump: patch \ No newline at end of file diff --git a/prdoc/pr_4949.prdoc b/prdoc/pr_4949.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4a5c09c6fa8de62972bf193d5796e0b36b01b878 --- /dev/null +++ b/prdoc/pr_4949.prdoc @@ -0,0 +1,78 @@ +title: "[bridges-v2] Permissionless lanes" + +doc: +- audience: Runtime Dev + description: | + This PR adds support for opening and closing dynamic, also known as permissionless, lanes. + This means that authorized origins (relay chain, sibling parachains) + can open and close bridges with other bridged (substrate-like) consensuses supported by Bridge Hubs. + The Bridge Hubs, particularly the `pallet-xcm-bridge-hub`, introduce new extrinsics `open_bridge` and `close_bridge`, + which can be called using `xcm::Transact`. + +crates: +- name: bridge-runtime-common + bump: major +- name: bp-bridge-hub-rococo + bump: minor +- name: bp-bridge-hub-westend + bump: minor +- name: pallet-bridge-grandpa + bump: major +- name: pallet-bridge-messages + bump: major +- name: pallet-bridge-parachains + bump: major +- name: pallet-bridge-relayers + bump: major +- name: pallet-xcm-bridge-hub + bump: major +- name: pallet-xcm-bridge-hub-router + bump: major +- name: bp-header-chain + bump: patch +- name: bp-messages + bump: major +- name: bp-parachains + bump: major +- name: bp-polkadot-core + bump: none +- name: bp-relayers + bump: major +- name: bp-runtime + bump: minor +- name: bp-xcm-bridge-hub-router + bump: patch +- name: bp-xcm-bridge-hub + bump: major +- name: relay-substrate-client + bump: none +- name: substrate-relay-helper + bump: major +- name: messages-relay + bump: major +- name: parachains-relay + bump: none +- name: cumulus-pallet-xcmp-queue + bump: patch +- name: parachains-relay + bump: none +- name: asset-hub-rococo-runtime + bump: major +- name: asset-hub-westend-runtime + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major +- name: emulated-integration-tests-common + bump: minor +- name: asset-test-utils + bump: patch +- name: parachains-runtimes-test-utils + bump: minor +- name: bridge-hub-common + bump: minor +- name: bridge-hub-test-utils + bump: major +- name: xcm-emulator + bump: major diff --git a/prdoc/pr_4956.prdoc b/prdoc/pr_4956.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a72ca303aaca46c2bd73b46f1987fc5b1a9ecd2c --- /dev/null +++ b/prdoc/pr_4956.prdoc @@ -0,0 +1,39 @@ +title: Add build options to the srtool build step and delete `disanle-logging` feature + +doc: +- audience: Runtime Dev + description: | + This PR adds possibility to set BUILD_OPTIONS to the "Srtool Build\" step in the release pipeline while building runtimes. + And deletes the `disable-logging` feature from test runtimes to be able to build those with an activated logging. + + + +crates: +- name: people-rococo-runtime + bump: patch +- name: people-westend-runtime + bump: patch +- name: rococo-parachain-runtime + bump: patch +- name: asset-hub-rococo-runtime + bump: patch +- name: asset-hub-westend-runtime + bump: patch +- name: bridge-hub-rococo-runtime + bump: patch +- name: bridge-hub-westend-runtime + bump: patch +- name: collectives-westend-runtime + bump: patch +- name: contracts-rococo-runtime + bump: patch +- name: coretime-rococo-runtime + bump: patch +- name: coretime-westend-runtime + bump: patch +- name: glutton-westend-runtime + bump: patch +- name: rococo-runtime + bump: patch +- name: westend-runtime + bump: patch diff --git a/prdoc/pr_4962.prdoc b/prdoc/pr_4962.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0957f85b66282c4baa620d696a0e44ebc900153d --- /dev/null +++ b/prdoc/pr_4962.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Removed `pallet::getter` usage from the pallet-treasury + +doc: + - audience: Runtime Dev + description: | + This PR removed `pallet::getter`s from `pallet-treasury`s storage items. + Instead use the syntax `StorageItem::::get()`. + +crates: + - name: pallet-treasury + bump: minor \ No newline at end of file diff --git a/prdoc/pr_4998.prdoc b/prdoc/pr_4998.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..41e3886405cc7d7eaa6eb942ff246e5fde19dc83 --- /dev/null +++ b/prdoc/pr_4998.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Ensure members can always exit the pool gracefully + +doc: + - audience: Runtime Dev + description: | + Ensures when a member wants to withdraw all funds but the pool is not able to provide all their funds, the member + can receive as much as possible and exit pool. Also handles cases where some extra funds held in member's account + is released when they are removed. + +crates: + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: major + - name: sp-staking + bump: major + diff --git a/prdoc/pr_4999.prdoc b/prdoc/pr_4999.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d396fcdbe8b39e0b5e09587727ca6977d090d865 --- /dev/null +++ b/prdoc/pr_4999.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fixes entropy for derivation of proxy delegator account. + +doc: + - audience: Runtime Dev + description: | + This fixes how ProxyDelegator accounts are derived but may cause issues in Westend since it would use the old + derivative accounts. Does not affect Polkadot/Kusama as this pallet is not deployed to them yet. + +crates: + - name: westend-runtime + bump: patch + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: patch \ No newline at end of file diff --git a/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_5082.prdoc b/prdoc/pr_5082.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d309f4e7266ed1d57cb225f10b0f27c8881aa746 --- /dev/null +++ b/prdoc/pr_5082.prdoc @@ -0,0 +1,15 @@ +title: "Fix ParaInherent weight overestimation" + +doc: + - audience: Runtime Dev + description: | + This PR fixes the relay chain inherent weight overestimation allowing it + to support more cores and validators. + +crates: +- name: polkadot-runtime-parachains + bump: major +- name: westend-runtime + bump: minor +- name: rococo-runtime + bump: minor diff --git a/prdoc/pr_5127.prdoc b/prdoc/pr_5127.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c08f4e7fb8fa5b55c98683ed53076be80f1ef21b --- /dev/null +++ b/prdoc/pr_5127.prdoc @@ -0,0 +1,25 @@ +title: Add benchmark to check upcoming minimum required hw cores + +doc: + - audience: Node Operator + description: | + Add benchmark that checks hardware satisifies the minimum required hardware cores + for a validators. The new minimum requirements are schedule to come into effect + in January 2025, for more details see: https://polkadot.subsquare.io/referenda/1051. + + +crates: + - name: sc-sysinfo + bump: major + - name: frame-benchmarking-cli + bump: major + - name: staging-node-cli + bump: patch + - name: polkadot-service + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: polkadot-cli + bump: patch + - name: parachain-template-node + bump: patch diff --git a/prdoc/pr_5131.prdoc b/prdoc/pr_5131.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..db1003ab40338875891cade7258011c3d4f9fd9d --- /dev/null +++ b/prdoc/pr_5131.prdoc @@ -0,0 +1,42 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Swap for paying delivery fees in different assets + +doc: + - audience: Runtime User + description: | + If the `AssetExchanger` is configured on a runtime, the XCM executor is now able to swap assets + to pay for delivery fees. + This was already possible for execution fees via the `SwapFirstAssetTrader`. + A runtime where this will be possible is Asset Hub. + That means reserve asset transfers from Parachain A to Parachain B passing through Asset Hub no + longer need to have any DOT to pay for fees on AssetHub. + They can have any asset in a pool with DOT on Asset Hub, for example USDT or USDC. + - audience: Runtime Dev + description: | + Using the `AssetExchanger` XCM config item, the executor now swaps fees to use for delivery fees, + if possible. + If you want your runtime to support this, you need to configure this new item. + Thankfully, `xcm-builder` now has a new adapter for this, which lets you use `pallet-asset-conversion` + or any type that implements the `SwapCredit` and `QuotePrice` traits. + It's called `SingleAssetExchangeAdapter`, you can read more about it in its rust docs. + This item is already configured in Asset Hub. + + IMPORTANT: The executor now only takes the first asset for delivery fees. If you have configured a custom router + that returns more than one asset for delivery fees, then only the first one will be taken into account. + This is most likely not what you want. + +crates: + - name: staging-xcm-executor + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: staging-xcm-builder + bump: patch + - name: assets-common + bump: patch + - name: penpal-runtime + bump: minor diff --git a/prdoc/pr_5142.prdoc b/prdoc/pr_5142.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4083e5bf53cdb420cdb236e920b5e409b6c82149 --- /dev/null +++ b/prdoc/pr_5142.prdoc @@ -0,0 +1,26 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Move decompression to worker processes" + +doc: + - audience: Node Dev + description: | + Candidate validation subsystem performed the PVF code decompression as well as the PoV + decompression itself which might affect the subsystem main loop performance and required + it to run on the blocking threadpool. This change moves the decompression to PVF host + workers running synchronously in separate processes. + +crates: + - name: polkadot-node-core-candidate-validation + bump: patch + - name: polkadot-overseer + bump: patch + - name: polkadot-node-core-pvf + bump: major + - name: polkadot-node-core-pvf-common + bump: major + - name: polkadot-node-core-pvf-execute-worker + bump: patch + - name: polkadot-node-core-pvf-prepare-worker + bump: patch diff --git a/prdoc/pr_5155.prdoc b/prdoc/pr_5155.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..373522eea1c393b345b83119961933a2b6753b70 --- /dev/null +++ b/prdoc/pr_5155.prdoc @@ -0,0 +1,27 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Use umbrella crate for minimal template + +doc: + - audience: Runtime Dev + description: | + Minor additions to the `polkadot-sdk-frame` crate and making it ready for usage in more templates. This PR already integrates it in the minimal template. + + +crates: + - name: polkadot-sdk + bump: major + - name: polkadot-sdk-frame + bump: patch + - name: sp-wasm-interface + bump: patch + - name: pallet-revive + bump: patch + - name: pallet-revive-fixtures + bump: patch + - name: frame-support + bump: patch + - name: pallet-balances + bump: patch + diff --git a/prdoc/pr_5188.prdoc b/prdoc/pr_5188.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b2ab9ff6653b6abcf8b1c125d527a51d11ff88e3 --- /dev/null +++ b/prdoc/pr_5188.prdoc @@ -0,0 +1,32 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added benchmarks for BEEFY fork voting + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + This PR adds benchmarks for `report_fork_voting` and `report_future_voting` extrinsics to `pallet-beefy`. + `report_future_voting` can be called now. `report_fork_voting` can't be called yet. Even though we have added + the formula for computing its weight, we still use `Weight::MAX`. We will set the proper weight in a future PR. + In order to do this we need to also check that the ancestry proof is optimal. + The PR adds a `WeightInfo` associated trait to the `pallet_beefy_mmr::Config` and defines benchmarks for + `pallet_beefy_mmr`. + +crates: + - name: pallet-mmr + bump: minor + - name: sp-mmr-primitives + bump: minor + - name: sp-consensus-beefy + bump: minor + - name: rococo-runtime + bump: minor + - name: pallet-beefy + bump: major + - name: pallet-beefy-mmr + bump: major + - name: westend-runtime + bump: minor diff --git a/prdoc/pr_5195.prdoc b/prdoc/pr_5195.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..cfd435fa289d00ed4487c0b0baacbad14f777075 --- /dev/null +++ b/prdoc/pr_5195.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Bump Aura authoring duration to 2s. + +doc: + - audience: Node Dev + description: | + This PR bumps the Aura authoring duration in the asynchronous backing + guide and the polkadot-parachain service file to 2s in order to make + better use of the provided coretime. + +crates: + - name: polkadot-parachain-bin + bump: patch 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_5204.prdoc b/prdoc/pr_5204.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..38a73b6b00ef651db166810a54a9e9417ac84cdf --- /dev/null +++ b/prdoc/pr_5204.prdoc @@ -0,0 +1,13 @@ +title: "Pallet assets: fix doc: start_destroy never required asset to be frozen" + +doc: + - audience: Runtime Dev + description: | + In pallet assets calling `start_destroy` doesn't require the asset to be frozen. Doc is fixed. + + +crates: + - name: pallet-assets + bump: patch + - name: frame-support + bump: patch diff --git a/prdoc/pr_5252.prdoc b/prdoc/pr_5252.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fd4454ac3b9d6e17589afbb0b3576c365af7dd94 --- /dev/null +++ b/prdoc/pr_5252.prdoc @@ -0,0 +1,11 @@ +title: Additional logging in `dispute-coordinator` subsystem + +doc: + - audience: Node Dev + description: | + Additional logging in `dispute-coordinator` subsystem tracing the list of offchain disabled + validators and the reason why an import statement is considered spam. + +crates: + - name: polkadot-node-core-dispute-coordinator + bump: patch \ No newline at end of file diff --git a/prdoc/pr_5262.prdoc b/prdoc/pr_5262.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..828f0ffeb1bc68d7f7f903eee9c438a31d72a39f --- /dev/null +++ b/prdoc/pr_5262.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Correct some typos in crates' descriptions + +doc: + - audience: Runtime Dev + description: | + Corrected typos and copy-paste errors in crates' descriptions. + +crates: + - name: cumulus-client-pov-recovery + bump: patch + - name: cumulus-pallet-aura-ext + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: frame-try-runtime + bump: patch + - name: pallet-whitelist + bump: patch + - name: polkadot-sdk + bump: patch + - name: polkadot-runtime-parachains + bump: none diff --git a/prdoc/pr_5269.prdoc b/prdoc/pr_5269.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e4401f2406cef2da501c46ad71a3149898124430 --- /dev/null +++ b/prdoc/pr_5269.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added the possibility to build a parachain node with block number u64 + +doc: + - audience: Node Dev + description: | + Added the possibility to build a parachain node with block number u64. + +crates: + - name: polkadot-parachain-lib + bump: minor + - name: polkadot-parachain-bin + bump: patch diff --git a/prdoc/pr_5270.prdoc b/prdoc/pr_5270.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e6d7142cabd07e3609328fb53befb08b74650e7a --- /dev/null +++ b/prdoc/pr_5270.prdoc @@ -0,0 +1,20 @@ +title: "Inclusion: account for enact_candidate weight" + +doc: + - audience: Runtime Dev + description: | + We are now properly accounting for the `enact_candidate`s weight in + processing of a relay chain block inherent. This may result in some + of the user relay chain transactions not being included in the block if + it's really heavy. This should be fine though as we are moving towards + the minimal relay chain. + +crates: +- name: polkadot-runtime-parachains + bump: major +- name: westend-runtime + bump: patch +- name: rococo-runtime + bump: patch +- name: polkadot-node-core-pvf-common + bump: none diff --git a/prdoc/pr_5284.prdoc b/prdoc/pr_5284.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a3244a82c86092b3f9feb82b5c3b838c41bfe470 --- /dev/null +++ b/prdoc/pr_5284.prdoc @@ -0,0 +1,7 @@ +title: Minor clean up +author: conr2d +topic: runtime + +crates: + - name: sp-runtime + bump: none diff --git a/prdoc/pr_5288.prdoc b/prdoc/pr_5288.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..8241e75876f1adf1777a9ae693424ce2543c1606 --- /dev/null +++ b/prdoc/pr_5288.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Added `polkadot-parachain-lib` helper library that can be used to build a parachain node + +doc: + - audience: Node Dev + description: | + This PR adds the `polkadot-parachain-lib` helper library that can be used to build a parachain node. + +crates: + - name: polkadot-parachain-bin + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/prdoc/pr_5293.prdoc b/prdoc/pr_5293.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..90528a224e8dfc9172aee980265f42a943eeb430 --- /dev/null +++ b/prdoc/pr_5293.prdoc @@ -0,0 +1,22 @@ +title: Add initial version of pallet_revive + +doc: + - audience: Runtime Dev + description: | + Adds initial **experimental** version of the new pallet_revive. It will run PolkaVM + contracts which were recompiled from YUL using the revive compiler. Do not use the + pallet in production, yet. It is work in progress. + +crates: + - name: polkadot-sdk + bump: minor + - name: pallet-revive + bump: minor + - name: pallet-revive-fixtures + bump: minor + - name: pallet-revive-proc-macro + bump: minor + - name: pallet-revive-uapi + bump: minor + - name: pallet-revive-mock-network + bump: minor diff --git a/prdoc/pr_5316.prdoc b/prdoc/pr_5316.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..75b431c941d546c38efc57d0c5fd146f7c0f5a1e --- /dev/null +++ b/prdoc/pr_5316.prdoc @@ -0,0 +1,11 @@ +title: add timestamp function to sp-consensus-slots + +doc: + - audience: Node Dev + description: | + Added timestamp function to sp-consensus-slots to get the first timestamp + of the given slot. + +crates: + - name: sp-consensus-slots + bump: minor diff --git a/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_5326.prdoc b/prdoc/pr_5326.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0301b8c17a309d0a0493bf49f106b51617efd43a --- /dev/null +++ b/prdoc/pr_5326.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Update Readme of the `polkadot` crate + +doc: + - audience: Node Operator + description: | + Updated Readme of the `polkadot` crate. + +crates: + - name: polkadot + bump: patch diff --git a/prdoc/pr_5339.prdoc b/prdoc/pr_5339.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..850ba903e126547ace404bd1e2e9a64f1fe08b5a --- /dev/null +++ b/prdoc/pr_5339.prdoc @@ -0,0 +1,45 @@ +title: Replace unnecessary `&mut self` with `&self` in `BlockImport::import_block()` + +doc: + - audience: Node Dev + description: | + Simplifies block import API to match intended design where independent blocks can technically be imported + concurrently and in practice was called through `Arc` anyway + +crates: + - name: cumulus-client-consensus-common + bump: major + - name: cumulus-client-network + bump: none + - name: cumulus-relay-chain-inprocess-interface + bump: none + - name: cumulus-pallet-parachain-system + bump: none + - name: sc-basic-authorship + bump: patch + - name: sc-consensus-babe + bump: major + - name: sc-consensus-beefy + bump: major + - name: sc-consensus + bump: major + - name: sc-consensus-grandpa + bump: major + - name: sc-consensus-pow + bump: major + - name: mmr-gadget + bump: none + - name: sc-network + bump: none + - name: sc-network-sync + bump: none + - name: sc-offchain + bump: none + - name: sc-rpc-spec-v2 + bump: none + - name: sc-rpc + bump: none + - name: sc-service + bump: major + - name: sc-transaction-pool + bump: none diff --git a/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_5344.prdoc b/prdoc/pr_5344.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9f83c113686d79e23d2915233754569a8fca421b --- /dev/null +++ b/prdoc/pr_5344.prdoc @@ -0,0 +1,10 @@ +title: Fix storage weight reclaim bug. + +doc: + - audience: Node Dev + description: | + Improvement in slot worker loop that will not call create inherent data providers if the major sync is in progress. Before it was called every slot and the results were discarded during major sync. + +crates: + - name: sc-consensus-slots + bump: minor diff --git a/prdoc/pr_5348.prdoc b/prdoc/pr_5348.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c2282c4c74c4f6e0e2d427dacea79de3b1bc6176 --- /dev/null +++ b/prdoc/pr_5348.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: allow for u8 to be used as hold/freeze reason + +doc: + - audience: Runtime Dev + description: | + Allows for `u8` type to be configured as `HoldReason` and `FreezeReason` + +crates: + - name: frame-support + bump: patch diff --git a/prdoc/pr_5352.prdoc b/prdoc/pr_5352.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4b055d81cb515ebcbc47c99cc1e466a9bede3c70 --- /dev/null +++ b/prdoc/pr_5352.prdoc @@ -0,0 +1,10 @@ +title: "Aura: Ensure parachains are building on all relay chain forks" + +doc: + - audience: Node Dev + description: | + Ensure that parachains using the `basic` collator are building on all relay chain forks. + +crates: + - name: cumulus-client-consensus-aura + bump: patch diff --git a/prdoc/pr_5354.prdoc b/prdoc/pr_5354.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e3037b66fbca070d7fe15ceca7556741f86d8ad6 --- /dev/null +++ b/prdoc/pr_5354.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix benchmark failures when using insecure_zero_ed flag + +doc: + - audience: Runtime Dev + description: | + Currently, when the pallet is compiled with the insecure_zero_ed flag, benchmarks fail because the minimum balance is set to zero. + + The PR aims to resolve this issue by implementing a placeholder value for the minimum balance when the insecure_zero_ed flag is active. it ensures that benchmarks run successfully regardless of whether this flag is used or not + +crates: +- name: pallet-balances + bump: minor diff --git a/prdoc/pr_5356.prdoc b/prdoc/pr_5356.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a306be335440735fdc088d4a506f275e65ff2a73 --- /dev/null +++ b/prdoc/pr_5356.prdoc @@ -0,0 +1,18 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix OurViewChange small race + +doc: + - audience: Node Dev + description: | + Always queue OurViewChange event before we send view changes to our peers, because otherwise we risk + the peers sending us a message that can be processed by our subsystems before OurViewChange. + Normally, this is not really a problem because the latency of the ViewChange we send to our peers + is way higher than that of our subsystem processing OurViewChange, however on testnets like versi + where CPUs are sometimes overcommitted this race gets triggered occasionally, so let's fix it by + sending the messages in the right order. + +crates: + - name: polkadot-network-bridge + bump: minor diff --git a/prdoc/pr_5359.prdoc b/prdoc/pr_5359.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bf059129a4362473fb9de83613b1a8ddde88f575 --- /dev/null +++ b/prdoc/pr_5359.prdoc @@ -0,0 +1,21 @@ +title: Make ticket non-optional and add ensure_successful method to Consideration trait + +doc: + - audience: Runtime Dev + description: | + Make ticket non-optional and add ensure_successful method to Consideration trait. + + Reverts the optional return ticket type for the new function introduced in + [polkadot-sdk/4596](https://github.com/paritytech/polkadot-sdk/pull/4596) and adds a helper + `ensure_successful` function for the runtime benchmarks. + Since the existing FRAME pallet represents zero cost with a zero balance type rather than + `None` in an option, maintaining the ticket type as a non-optional balance is beneficial + for backward compatibility and helps avoid unnecessary migrations. + +crates: + - name: frame-support + bump: major + - name: pallet-preimage + bump: major + - name: pallet-balances + bump: patch diff --git a/prdoc/pr_5360.prdoc b/prdoc/pr_5360.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4b07f30bfd09d4835c998e447e86b0ef4b26acae --- /dev/null +++ b/prdoc/pr_5360.prdoc @@ -0,0 +1,3 @@ +crates: + - name: sc-service + bump: none diff --git a/prdoc/pr_5364.prdoc b/prdoc/pr_5364.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..35a72f65fb1e20997dca27cec035b2b5aa9e7fb0 --- /dev/null +++ b/prdoc/pr_5364.prdoc @@ -0,0 +1,39 @@ +title: Improve `sc-service` API + +doc: + - audience: Node Dev + description: | + This improves `sc-service` API by not requiring the whole `&Configuration`, using specific configuration options + instead. `RpcConfiguration` and `ExecutorConfiguration` were also extracted from `Configuration` to group all RPC + and executor options together. + If `sc-service` is used as a library with lower-level APIs, `Configuration` can now be avoided in most cases. + + This mainly impacts you on your node implementation. There you need to change this: + ``` + with_execution_method(config.wasm_method) + ``` + + to this: + ``` + with_execution_method(config.executor.wasm_method) + ``` + + There are similar changes required as well, but all are around the initialization of the wasm executor. + +crates: + - name: sc-service + bump: major + - name: sc-network-common + bump: patch + - name: sc-cli + bump: major + - name: polkadot-service + bump: patch + - name: cumulus-relay-chain-minimal-node + bump: none + - name: polkadot-parachain-bin + bump: major + - name: polkadot-parachain-lib + bump: major + - name: staging-node-inspect + bump: major diff --git a/prdoc/pr_5369.prdoc b/prdoc/pr_5369.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1baa5e1cbe7dbeff44f5e23ef5dc28722d071262 --- /dev/null +++ b/prdoc/pr_5369.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix failing XCM from relay to Coretime Chain when revenue is zero + +doc: + - audience: Runtime Dev + description: | + The coretime assigner now always includes UnpaidExecution when calling `notify_revenue` via a + `Transact`, not just when revenue is nonzero. This fixes an issue where the XCM would fail to + process on the receiving side. + +crates: + - name: polkadot-runtime-parachains + bump: patch diff --git a/prdoc/pr_5376.prdoc b/prdoc/pr_5376.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c9874ef7d865693c5afe82fd74166dd01a171f05 --- /dev/null +++ b/prdoc/pr_5376.prdoc @@ -0,0 +1,3 @@ +crates: + - name: binary-merkle-tree + bump: none diff --git a/prdoc/pr_5380.prdoc b/prdoc/pr_5380.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..75063e3353439508745a0ef3e38b6e798690ae0e --- /dev/null +++ b/prdoc/pr_5380.prdoc @@ -0,0 +1,15 @@ +title: Fix leases with gaps and time slice calculation in MigrateToCoretime + +doc: + - audience: Runtime Dev + description: | + Agile Coretime storage migration wasn't transferring correctly leases with gaps and was + miscalculating time lease period. This patch provides fixes for both issues. + +crates: + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: major + - name: polkadot-runtime-parachains + bump: patch \ No newline at end of file diff --git a/prdoc/pr_5384.prdoc b/prdoc/pr_5384.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..74d477f8e153d32e3da698acfa02f990141a96d9 --- /dev/null +++ b/prdoc/pr_5384.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "`MaybeConsideration` extension trait for `Consideration`" + +doc: + - audience: Runtime Dev + description: | + The trait allows for the management of tickets that may represent no cost. While + the `MaybeConsideration` still requires proper handling, it introduces the ability + to determine if a ticket represents no cost and can be safely forgotten without any + side effects. + + The new trait is particularly useful when a consumer expects the cost to be zero under + certain conditions (e.g., when the proposal count is below a threshold N) and does not want + to store such consideration tickets in storage. The extension approach allows us to avoid + breaking changes to the existing trait and to continue using it as a non-optional version + for migrating pallets that utilize the `Currency` and `fungible` traits for `holds` and + `freezes`, without requiring any storage migration. + +crates: + - name: frame-support + bump: minor + - name: pallet-balances + bump: patch diff --git a/prdoc/pr_5392.prdoc b/prdoc/pr_5392.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..aeeb05de0bc31ec3b18a9b7496953da9d3fca8b3 --- /dev/null +++ b/prdoc/pr_5392.prdoc @@ -0,0 +1,11 @@ +title: "Don't disconnect disabled nodes sending us dispute messages" + +doc: + - audience: Node Operator + description: | + No longer disconnect peers which we consider disabled when raising + disputes as this will affect the approval process and thus finality. + +crates: + - name: polkadot-dispute-distribution + bump: patch diff --git a/prdoc/pr_5393.prdoc b/prdoc/pr_5393.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7fcf3067fabc580d9c40af0ffb413a50bf5b3d8a --- /dev/null +++ b/prdoc/pr_5393.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Allow to enable full PoV size + +doc: + - audience: Node Dev + description: | + A feature is introduced allowing a collator to enable full PoV size at compile time. + WARNING: To use this feature, security considerations must be understood and the latest + SDK version must be used. + +crates: + - name: cumulus-client-consensus-aura + bump: minor diff --git a/prdoc/pr_5396.prdoc b/prdoc/pr_5396.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d78e9ac46932b2a812fbb2ed30569aef40fe8d02 --- /dev/null +++ b/prdoc/pr_5396.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Simplify `SyncingEngine::new()` + +doc: + - audience: Node Dev + description: | + Tiny changes to simplify the internal implemenation of API `SyncingEngine::new()` to prevent panics while fetching the genesis hash and to eliminate unnecessary allocation for reserved peers. + +crates: +- name: sc-network-sync + bump: none diff --git a/prdoc/pr_5407.prdoc b/prdoc/pr_5407.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f7e6b86f9d1e7cb16022c50391e301d9fb493e69 --- /dev/null +++ b/prdoc/pr_5407.prdoc @@ -0,0 +1,17 @@ +title: Prepare PVFs if node is a validator in the next session + +doc: + - audience: [Node Operator, Node Dev] + description: | + This PR aims to remove the noise caused by the peer store's reputation system. + A warning was emitted each time a reputation was reported for a banned peer, + regardless of the reputation being positive. This has led in the past to + situations where it was hard to identify the actual reason of the ban and + caused noise for node operators. + + The `Banned, disconnecting.` warning is logged only when the peer is banned. + Other misbehaves are logged as `Misbehaved during the ban threshold`. + +crates: + - name: sc-network + bump: patch diff --git a/prdoc/pr_5410.prdoc b/prdoc/pr_5410.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d0a32bec74239c484073d1e93927ed1434d8b83e --- /dev/null +++ b/prdoc/pr_5410.prdoc @@ -0,0 +1,11 @@ +title: Reactive syncing metrics + +doc: + - audience: Node Dev + description: | + Syncing metrics are now updated immediate as changes happen rather than every 1100ms as it was happening before. + This resulted in minor, but breaking API changes. + +crates: + - name: sc-network-sync + bump: major diff --git a/prdoc/pr_5411.prdoc b/prdoc/pr_5411.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c24001d77bda27b8c711be74acfeb526276c853a --- /dev/null +++ b/prdoc/pr_5411.prdoc @@ -0,0 +1,3 @@ +crates: + - name: polkadot-approval-distribution + bump: none diff --git a/prdoc/pr_5424.prdoc b/prdoc/pr_5424.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a94bf7aaeba21812c48c61fe4a56955d9d501115 --- /dev/null +++ b/prdoc/pr_5424.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Suppress the log output for transaction propagation when no transactions are present + +doc: + - audience: Node Dev + description: | + Previously, the log message `Propagating transactions` would always be printed, even when there were no transactions to propagate. This patch optimizes the logging by returning early when no transactions are present, resulting in cleaner and more relevant log output. + +crates: +- name: sc-network-transactions + bump: none diff --git a/prdoc/pr_5430.prdoc b/prdoc/pr_5430.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..83d6d81e252e215d0dfdd49f0107ba866f2328f4 --- /dev/null +++ b/prdoc/pr_5430.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "pallet-collator-selection: correctly register weight in `new_session`" + +doc: + - audience: Runtime Dev + description: | + - Fixes an incorrect usage of the `WeightInfo` trait for `new_session`. + +crates: + - name: pallet-collator-selection + bump: patch diff --git a/prdoc/pr_5431.prdoc b/prdoc/pr_5431.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9f6db7136a587a9d9f20844b9a2c322c3b698e77 --- /dev/null +++ b/prdoc/pr_5431.prdoc @@ -0,0 +1,20 @@ +title: Remove the need to wait for target block header in warp sync implementation + +doc: + - audience: Node Dev + description: | + Previously warp sync needed to wait for target block header of the relay chain to become available before warp + sync can start, which resulted in cumbersome APIs. Parachain initialization was refactored to initialize warp sync + with target block header from the very beginning, improving and simplifying sync API. + +crates: + - name: sc-service + bump: major + - name: sc-network-sync + bump: major + - name: polkadot-service + bump: major + - name: cumulus-client-service + bump: major + - name: sc-informant + bump: major diff --git a/prdoc/pr_5436.prdoc b/prdoc/pr_5436.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ea624b7bc32de06bc4f4dd50fd0f94d6fdf70384 --- /dev/null +++ b/prdoc/pr_5436.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add Polkadot Coretime Chain genesis chain-spec + +doc: + - audience: Node Operator + description: | + The Polkadot Coretime Chain can now be run as all other system parachains without specifying a + chain-spec. However this will soon be deprecated and `--chain ./chain-spec.json` should continue + to be used instead. + + - audience: Runtime User + description: | + The Polkadot Coretime Chain chain-specs have been added to the polkadot-sdk repo and can now be + pulled from there along with all other system chains. + +crates: + - name: polkadot-parachain-bin + bump: minor \ No newline at end of file diff --git a/prdoc/pr_5439.prdoc b/prdoc/pr_5439.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..00fa48de0e2548b0973dd98bb76c9ae368518314 --- /dev/null +++ b/prdoc/pr_5439.prdoc @@ -0,0 +1,16 @@ +title: "Deprecated calls removed in cumulus parachain system pallet" + +doc: + - audience: [Runtime Dev, Runtime User] + description: | + Call `authorize_upgrade` in parachain system pallet `cumulus-pallet-parachain-system` has + been removed, use `authorize_upgrade` or `authorize_upgrade_without_checks` calls in system + pallet `frame-system` instead. + Call `enact_authorized_upgrade` in parachain system pallet `cumulus-pallet-parachain-system` + has been removed, use `apply_authorized_upgrade` call in system pallet `frame-system` instead. + +crates: + - name: cumulus-pallet-parachain-system + bump: major + - name: cumulus-pallet-xcmp-queue + bump: none diff --git a/prdoc/pr_5442.prdoc b/prdoc/pr_5442.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6adc34d71ad34ecb11da9f89c8380fea8770d868 --- /dev/null +++ b/prdoc/pr_5442.prdoc @@ -0,0 +1,10 @@ +title: Derive `Clone` on `EncodableOpaqueLeaf` + +doc: + - audience: Runtime Dev + description: | + `Clone` was derived on `EncodableOpaqueLeaf` for convenience of downstream users + +crates: + - name: sp-mmr-primitives + bump: patch diff --git a/prdoc/pr_5443.prdoc b/prdoc/pr_5443.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0fd396be06a2c0ceb8b49aa5b06bb7b40a72517f --- /dev/null +++ b/prdoc/pr_5443.prdoc @@ -0,0 +1,10 @@ +crates: +- name: frame-remote-externalities + bump: patch +doc: +- audience: Runtime Dev + description: as part of https://github.com/paritytech/devops/issues/3502, try-runtime + nodes were migrated to a new provider with a new domain address. The PR fixes + all the references to try-runtime nodes on rococo, westend, kusama and polkadot + networks +title: change try-runtime rpc domains diff --git a/prdoc/pr_5450.prdoc b/prdoc/pr_5450.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ccd319cad246135143980eec274e286aa2349ce2 --- /dev/null +++ b/prdoc/pr_5450.prdoc @@ -0,0 +1,18 @@ +title: Sync status refactoring + +doc: + - audience: Node Dev + description: | + `SyncingService` API in `sc-network-sync` has changed with some of the redundant methods related to sync status + removed that were mostly used internally or for testing purposes and is unlikely to impact external code. + `ExtendedPeerInfo` now has working `Clone` and `Copy` implementation. + +crates: + - name: sc-informant + bump: major + - name: sc-network-sync + bump: major + - name: sc-network-test + bump: major + - name: sc-service + bump: major diff --git a/prdoc/pr_5465.prdoc b/prdoc/pr_5465.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ae185dc250f6d271aef71d7e4c0c0acfc60205ea --- /dev/null +++ b/prdoc/pr_5465.prdoc @@ -0,0 +1,10 @@ +title: try-state check invariant for nomination-pools (points >= stake) + +doc: + - audience: Runtime Dev + description: | + Adds a new try-state invariant to the nomination pools that checks that for each bonded pool, the pool's points can never be lower than its staked balance. + +crates: + - name: pallet-nomination-pools + bump: minor diff --git a/prdoc/pr_5466.prdoc b/prdoc/pr_5466.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..57f20b3585b4211b00a94986b433631d2462f7c6 --- /dev/null +++ b/prdoc/pr_5466.prdoc @@ -0,0 +1,14 @@ +crates: +- bump: patch + name: frame-omni-bencher +- bump: patch + name: frame-benchmarking-cli +doc: +- audience: Runtime Dev + description: | + Changes: + - Set default level to `Info` again. Seems like a dependency update set it to something higher. + - Fix docs to not use `--locked` since we rely on dependency bumps via cargo. + - Add README with rust docs. + - Fix bug where the node ignored `--heap-pages` argument. +title: frame-omni-bencher maintenance diff --git a/prdoc/pr_5467.prdoc b/prdoc/pr_5467.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2634c255e168f7c5ce052cd986f3fb674de1d695 --- /dev/null +++ b/prdoc/pr_5467.prdoc @@ -0,0 +1,10 @@ +title: Make PendingConfigs storage item public + +doc: + - audience: Runtime Dev + description: | + Make PendingConfigs storage item in polkadot's configuration crate public. + +crates: + - name: polkadot-runtime-parachains + bump: minor \ No newline at end of file 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_5509.prdoc b/prdoc/pr_5509.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..154146034e6fd8dad28c3d0cb299154886bd6449 --- /dev/null +++ b/prdoc/pr_5509.prdoc @@ -0,0 +1,17 @@ +title: Add `pallet_proxy` to People Chain and Coretime Chain testnet runtimes. + +doc: + - audience: Runtime User + description: | + Proxies can now be used on `coretime-rococo`, `coretime-westend`, `people-rococo` and + `people-westend` in the same way as they can be on Kusama and Polkadot chains. + +crates: + - name: coretime-rococo-runtime + bump: major + - name: coretime-westend-runtime + bump: major + - name: people-rococo-runtime + bump: major + - name: people-westend-runtime + bump: major diff --git a/prdoc/pr_5513.prdoc b/prdoc/pr_5513.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0eda8b02cde04a1b99b61e04a396b701cf824b3f --- /dev/null +++ b/prdoc/pr_5513.prdoc @@ -0,0 +1,10 @@ +title: Add more logs to trace AcceptanceCheckErr + +doc: + - audience: Runtime Dev + description: | + `Debug` was derived on `AcceptanceCheckErr` to print the error. This PR adds more logs to trace the error. + +crates: + - name: polkadot-runtime-parachains + bump: patch 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_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_5527.prdoc b/prdoc/pr_5527.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..38eb75affe44730319ed2d5b4f68292f59790639 --- /dev/null +++ b/prdoc/pr_5527.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Report BestBlock events only for newBlock reports + +doc: + - audience: Node Dev + description: | + This PR ensures that the chainHead_v1_follow method of the RPC-v2 API is always + reporting a `BestBlock` event after a `NewBlock`. + There was a race condition in the chainHead follow logic which led to the `BestBlock` + event to be emitted without an associated `NewBlock` event. + +crates: + - name: sc-rpc-spec-v2 + bump: minor + diff --git a/prdoc/pr_5538.prdoc b/prdoc/pr_5538.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5924f97890403fe2003912363241a1fa0ff5c7a3 --- /dev/null +++ b/prdoc/pr_5538.prdoc @@ -0,0 +1,11 @@ +title: "collator-protocol: Remove race condition" + +doc: + - audience: Node Dev + description: | + Remove a race condition in the collator protocol that could lead + to collations not being announced to a validator. + +crates: + - name: polkadot-collator-protocol + bump: patch 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_5546.prdoc b/prdoc/pr_5546.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..95f02dbe13b25c5e66149a0b7bbe99bdea7f4302 --- /dev/null +++ b/prdoc/pr_5546.prdoc @@ -0,0 +1,36 @@ +title: "Transfer polkadot native assets to Ethereum through snowbridge" + +doc: + - audience: Runtime Dev + description: | + Transfer polkadot native asset to Ethereum through snowbridge. + +crates: + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-outbound-queue + bump: patch + - name: snowbridge-pallet-system + bump: minor + validate: false + - name: snowbridge-core + bump: minor + validate: false + - name: snowbridge-router-primitives + bump: minor + validate: false + - name: bridge-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-emulated-chain + bump: patch + - name: bridge-hub-westend-integration-tests + bump: minor + - name: asset-hub-westend-emulated-chain + bump: patch + - name: emulated-integration-tests-common + bump: patch + 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_5580.prdoc b/prdoc/pr_5580.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e03b946070aa25ad4bbbd448d21c89bdbf7db6fa --- /dev/null +++ b/prdoc/pr_5580.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix error message on pallet macro + +doc: + - audience: Runtime Dev + description: | + Improve error message for pallet macro generated code. + +crates: + - name: frame-support-procedural + bump: patch diff --git a/prdoc/pr_5581.prdoc b/prdoc/pr_5581.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e33690939d8ba54554c4a72f6345246915874f51 --- /dev/null +++ b/prdoc/pr_5581.prdoc @@ -0,0 +1,30 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Clear other messages before dry-running + +doc: + - audience: Runtime Dev + description: | + The DryRunApi.dry_run_call and DryRunApi.dry_run_xcm functions used to populate + `forwarded_xcms` with all the existing messages in the queues at the time. + Now, existing (irrelevant) messages are cleared when dry-running, meaning only the + messages produced by the dry-run call (or xcm) will be returned in `forwarded_xcms`. + +crates: + - name: pallet-xcm + bump: minor + - name: staging-xcm-builder + bump: major + - name: pallet-xcm-bridge-hub-router + bump: minor + - name: cumulus-pallet-parachain-system + bump: minor + - name: cumulus-pallet-xcmp-queue + bump: minor + - name: cumulus-primitives-utility + bump: minor + - name: polkadot-runtime-common + bump: minor + - name: pallet-xcm-bridge-hub + bump: minor 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_5594.prdoc b/prdoc/pr_5594.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dbdc7937b73d33be17c7cf6acf6fd1258a5a8ba2 --- /dev/null +++ b/prdoc/pr_5594.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Add debugging info for `StorageWeightReclaim`" + +doc: + - audience: Runtime Dev + description: | + - Includes extrinsic index to be displayed in the logs when the consumed weight is higher than the measured one. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch 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_5632.prdoc b/prdoc/pr_5632.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f76428bbc8f64db05e2fe2b9df2786a8afebf72b --- /dev/null +++ b/prdoc/pr_5632.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix `alloc` not found error in `format_runtime_string!` + +doc: + - audience: Runtime Dev + description: | + Fixes the macro hygiene in the `format_runtime_string!` macro to fix the `alloc` not found build error. + +crates: + - name: sp-runtime + bump: patch diff --git a/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_5644.prdoc b/prdoc/pr_5644.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3300d557fce40f274120fb76b449e675eccb2a2f --- /dev/null +++ b/prdoc/pr_5644.prdoc @@ -0,0 +1,8 @@ +title: 'pallet-utility: Improve weight annotations' +doc: +- audience: Runtime Dev + description: |- + Prevent allocations when calculating the weights. +crates: +- name: pallet-utility + bump: patch diff --git a/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_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_5678.prdoc b/prdoc/pr_5678.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..af1fac31c56099fb5540c8f0fd1b518461178edb --- /dev/null +++ b/prdoc/pr_5678.prdoc @@ -0,0 +1,14 @@ +title: 'rpc server: fix deny unsafe on RpcMethods::Auto' +doc: +- audience: Node User + 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/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..a17bacd2fb949598d9ca9b1b0fa248bd8a6befc2 --- /dev/null +++ b/prdoc/pr_5684.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet-revive]" + +doc: + - audience: Runtime Devs + description: | + Update xcm runtime api, and fix pallet-revive xcm tests + +crates: + - name: pallet-revive + bump: patch + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-mock-network + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/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_5688.prdoc b/prdoc/pr_5688.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..88e712fcba68ca2b1f7c2d63cfe6a4e951f3a987 --- /dev/null +++ b/prdoc/pr_5688.prdoc @@ -0,0 +1,10 @@ +title: "Fix `paras_inherent` benchmark" + +doc: + - audience: Runtime Dev + description: | + Fixes the benchmark for relay chains like rococo-dev with `max_validators_per_core` value lower than 2. + +crates: +- name: polkadot-runtime-parachains + bump: patch diff --git a/prdoc/pr_5695.prdoc b/prdoc/pr_5695.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..202eafa26ebd8db98e3a759530b581b652cca18a --- /dev/null +++ b/prdoc/pr_5695.prdoc @@ -0,0 +1,15 @@ +title: 'pallet-migrations: fix index access for singluar migrations' +doc: +- audience: Runtime Dev + description: |- + Discovered a bug in the migrations pallet while debugging https://github.com/paritytech/try-runtime-cli/pull/90. + It only occurs when a single MBM is configured - hence it did not happen when Ajuna Network tried it... + + Changes: + - Check len of the tuple before accessing its nth_id + - Make nth_id return `None` on unary tuples and n>0 +crates: +- name: pallet-migrations + bump: patch +- name: frame-support + bump: patch diff --git a/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/scripts/bench-all.sh b/scripts/bench-all.sh deleted file mode 100755 index e5512e26bbad75248f2983a2e3a653e3d1328435..0000000000000000000000000000000000000000 --- a/scripts/bench-all.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail -shopt -s inherit_errexit -shopt -s globstar - -. "$(realpath "$(dirname "${BASH_SOURCE[0]}")/command-utils.sh")" - -get_arg optional --pallet "$@" -PALLET="${out:-""}" - -if [[ ! -z "$PALLET" ]]; then - . "$(dirname "${BASH_SOURCE[0]}")/lib/bench-all-pallet.sh" "$@" -else - . "$(dirname "${BASH_SOURCE[0]}")/bench.sh" --subcommand=all "$@" -fi diff --git a/scripts/bench.sh b/scripts/bench.sh deleted file mode 100755 index 2f4ef7ec6a14118630964a835d181d9631a0c605..0000000000000000000000000000000000000000 --- a/scripts/bench.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -# Initially based on https://github.com/paritytech/bench-bot/blob/cd3b2943d911ae29e41fe6204788ef99c19412c3/bench.js - -# Most external variables used in this script, such as $GH_CONTRIBUTOR, are -# related to https://github.com/paritytech/try-runtime-bot - -# This script relies on $GITHUB_TOKEN which is probably a protected GitLab CI -# variable; if this assumption holds true, it is implied that this script should -# be ran only on protected pipelines - -set -eu -o pipefail -shopt -s inherit_errexit - -# realpath allows to reuse the current -BENCH_ROOT_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")") - -. "$(realpath "$(dirname "${BASH_SOURCE[0]}")/command-utils.sh")" - -repository_name="$(basename "$PWD")" - -get_arg optional --target_dir "$@" -target_dir="${out:-""}" - -get_arg optional --noexit "$@" -noexit="${out:-""}" - -output_path="." - -profile="production" - -if [[ "$repository_name" == "polkadot-sdk" ]]; then - output_path="./$target_dir" -fi - -cargo_run_benchmarks="cargo run --quiet --profile=${profile}" - -echo "Repository: $repository_name" -echo "Target Dir: $target_dir" -echo "Output Path: $output_path" - -cargo_run() { - echo "Running $cargo_run_benchmarks" "${args[@]}" - - # if not patched with PATCH_something=123 then use --locked - if [[ -z "${BENCH_PATCHED:-}" ]]; then - cargo_run_benchmarks+=" --locked" - fi - - $cargo_run_benchmarks "${args[@]}" -} - - -main() { - - # Remove the "github" remote since the same repository might be reused by a - # GitLab runner, therefore the remote might already exist from a previous run - # in case it was not cleaned up properly for some reason - &>/dev/null git remote remove github || : - - tmp_dirs=() - cleanup() { - exit_code=$? - # Clean up the "github" remote at the end since it contains the - # $GITHUB_TOKEN secret, which is only available for protected pipelines on - # GitLab - &>/dev/null git remote remove github || : - rm -rf "${tmp_dirs[@]}" - echo "Done, exit: $exit_code" - exit $exit_code - } - - # avoid exit if --noexit is passed - if [ -z "$noexit" ]; then - trap cleanup EXIT - fi - - # set -x - - get_arg required --subcommand "$@" - local subcommand="${out:-""}" - - case "$subcommand" in - runtime|pallet|xcm) - echo 'Running bench_pallet' - . "$BENCH_ROOT_DIR/lib/bench-pallet.sh" "$@" - ;; - overhead) - echo 'Running bench_overhead' - . "$BENCH_ROOT_DIR/lib/bench-overhead.sh" "$@" - ;; - all) - echo "Running all-$target_dir" - . "$BENCH_ROOT_DIR/lib/bench-all-${target_dir}.sh" "$@" - ;; - *) - die "Invalid subcommand $subcommand to process_args" - ;; - esac - - # set +x - - # in case we used diener to patch some dependency during benchmark execution, - # revert the patches so that they're not included in the diff - git checkout --quiet HEAD Cargo.toml - - # Save the generated weights to GitLab artifacts in case commit+push fails - echo "Showing weights diff for command" - git diff -P | tee -a "${ARTIFACTS_DIR}/weights.patch" - echo "Wrote weights patch to \"${ARTIFACTS_DIR}/weights.patch\"" - - - # instead of using `cargo run --locked`, we allow the Cargo files to be updated - # but avoid committing them. It is so `cmd_runner_apply_patches` can work - git restore --staged Cargo.* -} - -main "$@" diff --git a/scripts/command-utils.sh b/scripts/command-utils.sh deleted file mode 100644 index 252e4c86480e6b259af8a46038e1b9e0658e70fb..0000000000000000000000000000000000000000 --- a/scripts/command-utils.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash - -if [ "${LOADED_UTILS_SH:-}" ]; then - return -else - export LOADED_UTILS_SH=true -fi - -export ARTIFACTS_DIR="$PWD/.git/.artifacts" - -die() { - if [ "${1:-}" ]; then - >&2 echo "$1" - fi - exit 1 -} - -get_arg() { - local arg_type="$1" - shift - - local is_required - case "$arg_type" in - required|required-many) - is_required=true - ;; - optional|optional-many) ;; - *) - die "Invalid is_required argument \"$2\" in get_arg" - ;; - esac - - local has_many_values - if [ "${arg_type: -6}" == "-many" ]; then - has_many_values=true - fi - - local option_arg="$1" - shift - - local args=("$@") - - unset out - out=() - - local get_next_arg - for arg in "${args[@]}"; do - if [ "${get_next_arg:-}" ]; then - out+=("$arg") - unset get_next_arg - if [ ! "${has_many_values:-}" ]; then - break - fi - # --foo=bar (get the value after '=') - elif [ "${arg:0:$(( ${#option_arg} + 1 ))}" == "$option_arg=" ]; then - out+=("${arg:$(( ${#option_arg} + 1 ))}") - if [ ! "${has_many_values:-}" ]; then - break - fi - # --foo bar (get the next argument) - elif [ "$arg" == "$option_arg" ]; then - get_next_arg=true - fi - done - - # arg list ended with --something but no argument was provided next - if [ "${get_next_arg:-}" ]; then - die "Expected argument after \"${args[-1]}"\" - fi - - if [ "${out[0]:-}" ]; then - if [ ! "${has_many_values:-}" ]; then - out="${out[0]}" - fi - elif [ "${is_required:-}" ]; then - die "Argument $option_arg is required, but was not found" - else - unset out - fi -} diff --git a/scripts/generate-umbrella.py b/scripts/generate-umbrella.py index 3293c30bc828ea8f53522f238d14808db154ee55..e1ef6de86f9c46d783a2c19bcf7db815f4063085 100644 --- a/scripts/generate-umbrella.py +++ b/scripts/generate-umbrella.py @@ -64,7 +64,7 @@ def main(path, version): if manifest['lib']['proc-macro']: nostd_crates.append((crate, path)) continue - + # Crates without a lib.rs cannot be no_std if not os.path.exists(lib_path): print(f"Skipping {crate.name} as it does not have a 'src/lib.rs'") @@ -86,16 +86,18 @@ def main(path, version): # Sort by name std_crates.sort(key=lambda x: x[0].name) nostd_crates.sort(key=lambda x: x[0].name) + + runtime_crates = [crate for crate in nostd_crates if 'frame' in crate[0].name or crate[0].name.startswith('sp-')] all_crates = std_crates + nostd_crates all_crates.sort(key=lambda x: x[0].name) dependencies = {} for (crate, path) in nostd_crates: dependencies[crate.name] = {"path": f"../{path}", "default-features": False, "optional": True} - + for (crate, path) in std_crates: dependencies[crate.name] = {"path": f"../{path}", "default-features": False, "optional": True} - + # The empty features are filled by Zepter features = { "default": [ "std" ], @@ -105,9 +107,11 @@ def main(path, version): "serde": [], "experimental": [], "with-tracing": [], - "runtime": list([f"{d.name}" for d, _ in nostd_crates]), + "runtime-full": list([f"{d.name}" for d, _ in nostd_crates]), + "runtime": list([f"{d.name}" for d, _ in runtime_crates]), "node": ["std"] + list([f"{d.name}" for d, _ in std_crates]), "tuples-96": [], + "riscv": [], } manifest = { @@ -119,7 +123,7 @@ def main(path, version): "description": "Polkadot SDK umbrella crate.", "license": "Apache-2.0", "metadata": { "docs": { "rs": { - "features": ["runtime", "node"], + "features": ["runtime-full", "node"], "targets": ["x86_64-unknown-linux-gnu"] }}} }, @@ -159,9 +163,9 @@ def main(path, version): f.write(f'\n/// {desc}') f.write(f'\n#[cfg(feature = "{crate.name}")]\n') f.write(f"pub use {use};\n") - + print(f"Wrote {lib_path}") - + add_to_workspace(workspace.path) """ @@ -188,7 +192,7 @@ def add_to_workspace(path): manifest = re.sub(r'^members = \[', 'members = [\n "umbrella",', manifest, flags=re.M) with open(os.path.join(path, "Cargo.toml"), "w") as f: f.write(manifest) - + os.chdir(path) # hack os.system("cargo metadata --format-version 1 > /dev/null") # update the lockfile os.system(f"zepter") # enable the features @@ -203,3 +207,4 @@ def parse_args(): if __name__ == "__main__": args = parse_args() main(args.sdk, args.version) + 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/lib/bench-all-cumulus.sh b/scripts/lib/bench-all-cumulus.sh deleted file mode 100755 index f4c2a35c6b6b7ed8d30fa058666453f5c321b8e1..0000000000000000000000000000000000000000 --- a/scripts/lib/bench-all-cumulus.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env bash -# originally moved from https://github.com/paritytech/cumulus/blob/445f9277ab55b4d930ced4fbbb38d27c617c6658/scripts/benchmarks-ci.sh - -# default RUST_LOG is warn, but could be overridden -export RUST_LOG="${RUST_LOG:-error}" - -THIS_DIR=$(dirname "${BASH_SOURCE[0]}") -. "$THIS_DIR/../command-utils.sh" - -POLKADOT_PARACHAIN="./target/$profile/polkadot-parachain" - -run_cumulus_bench() { - local artifactsDir="$ARTIFACTS_DIR" - local category=$1 - local runtimeName=$2 - local paraId=${3:-} - - local benchmarkOutput="$output_path/parachains/runtimes/$category/$runtimeName/src/weights" - local benchmarkRuntimeChain - if [[ ! -z "$paraId" ]]; then - benchmarkRuntimeChain="${runtimeName}-dev-$paraId" - else - benchmarkRuntimeChain="$runtimeName-dev" - fi - - local benchmarkMetadataOutputDir="$artifactsDir/$runtimeName" - mkdir -p "$benchmarkMetadataOutputDir" - - # Load all pallet names in an array. - echo "[+] Listing pallets for runtime $runtimeName for chain: $benchmarkRuntimeChain ..." - local pallets=($( - $POLKADOT_PARACHAIN benchmark pallet --list --chain="${benchmarkRuntimeChain}" |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq - )) - - if [ ${#pallets[@]} -ne 0 ]; then - echo "[+] Benchmarking ${#pallets[@]} pallets for runtime $runtimeName for chain: $benchmarkRuntimeChain, pallets:" - for pallet in "${pallets[@]}"; do - echo " [+] $pallet" - done - else - echo "$runtimeName pallet list not found in benchmarks-ci.sh" - exit 1 - fi - - for pallet in "${pallets[@]}"; do - # (by default) do not choose output_file, like `pallet_assets.rs` because it does not work for multiple instances - # `benchmark pallet` command will decide the output_file name if there are multiple instances - local output_file="" - local extra_args="" - # a little hack for pallet_xcm_benchmarks - we want to force custom implementation for XcmWeightInfo - if [[ "$pallet" == "pallet_xcm_benchmarks::generic" ]] || [[ "$pallet" == "pallet_xcm_benchmarks::fungible" ]]; then - output_file="xcm/${pallet//::/_}.rs" - extra_args="--template=$output_path/templates/xcm-bench-template.hbs" - fi - $POLKADOT_PARACHAIN benchmark pallet \ - $extra_args \ - --chain="${benchmarkRuntimeChain}" \ - --wasm-execution=compiled \ - --pallet="$pallet" \ - --no-storage-info \ - --no-median-slopes \ - --no-min-squares \ - --extrinsic='*' \ - --steps=50 \ - --repeat=20 \ - --json \ - --header="$output_path/file_header.txt" \ - --output="${benchmarkOutput}/${output_file}" >> "$benchmarkMetadataOutputDir/${pallet//::/_}_benchmark.json" - done -} - - -echo "[+] Compiling benchmarks..." -cargo build --profile $profile --locked --features=runtime-benchmarks -p polkadot-parachain-bin - -# Run benchmarks for all pallets of a given runtime if runtime argument provided -get_arg optional --runtime "$@" -runtime="${out:-""}" - -if [[ $runtime ]]; then - paraId="" - case "$runtime" in - asset-*) - category="assets" - ;; - collectives-*) - category="collectives" - ;; - coretime-*) - category="coretime" - ;; - bridge-*) - category="bridge-hubs" - ;; - contracts-*) - category="contracts" - ;; - people-*) - category="people" - ;; - glutton-*) - category="glutton" - paraId="1300" - ;; - *) - echo "Unknown runtime: $runtime" - exit 1 - ;; - esac - - run_cumulus_bench $category $runtime $paraId - -else # run all - # Assets - run_cumulus_bench assets asset-hub-rococo - run_cumulus_bench assets asset-hub-westend - - # Collectives - run_cumulus_bench collectives collectives-westend - - # Coretime - run_cumulus_bench coretime coretime-rococo - run_cumulus_bench coretime coretime-westend - - # People - run_cumulus_bench people people-rococo - run_cumulus_bench people people-westend - - # Bridge Hubs - run_cumulus_bench bridge-hubs bridge-hub-rococo - run_cumulus_bench bridge-hubs bridge-hub-westend - - # Glutton - run_cumulus_bench glutton glutton-westend 1300 -fi diff --git a/scripts/lib/bench-all-pallet.sh b/scripts/lib/bench-all-pallet.sh deleted file mode 100644 index e6908045ddbd7f34ba7cad0e4c102e4606dff7aa..0000000000000000000000000000000000000000 --- a/scripts/lib/bench-all-pallet.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail -shopt -s inherit_errexit -shopt -s globstar - -. "$(dirname "${BASH_SOURCE[0]}")/../command-utils.sh" - -get_arg required --pallet "$@" -PALLET="${out:-""}" - -REPO_NAME="$(basename "$PWD")" -BASE_COMMAND="$(dirname "${BASH_SOURCE[0]}")/../../bench/bench.sh --noexit=true --subcommand=pallet" - -WEIGHT_FILE_PATHS=( $(find . -type f -name "${PALLET}.rs" -path "**/weights/*" | sed 's|^\./||g') ) - -# convert pallet_ranked_collective to ranked-collective -CLEAN_PALLET=$(echo $PALLET | sed 's/pallet_//g' | sed 's/_/-/g') - -# add substrate pallet weights to a list -SUBSTRATE_PALLET_PATH=$(ls substrate/frame/$CLEAN_PALLET/src/weights.rs || :) -if [ ! -z "${SUBSTRATE_PALLET_PATH}" ]; then - WEIGHT_FILE_PATHS+=("$SUBSTRATE_PALLET_PATH") -fi - -# add trappist pallet weights to a list -TRAPPIST_PALLET_PATH=$(ls pallet/$CLEAN_PALLET/src/weights.rs || :) -if [ ! -z "${TRAPPIST_PALLET_PATH}" ]; then - WEIGHT_FILE_PATHS+=("$TRAPPIST_PALLET_PATH") -fi - -COMMANDS=() - -if [ "${#WEIGHT_FILE_PATHS[@]}" -eq 0 ]; then - echo "No weights files found for pallet: $PALLET" - exit 1 -else - echo "Found weights files for pallet: $PALLET" -fi - -for f in ${WEIGHT_FILE_PATHS[@]}; do - echo "- $f" - # f examples: - # cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs - # polkadot/runtime/rococo/src/weights/pallet_balances.rs - # runtime/trappist/src/weights/pallet_assets.rs - TARGET_DIR=$(echo $f | cut -d'/' -f 1) - - if [ "$REPO_NAME" == "polkadot-sdk" ]; then - case $TARGET_DIR in - cumulus) - TYPE=$(echo $f | cut -d'/' -f 2) - # Example: cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_balances.rs - if [ "$TYPE" == "parachains" ]; then - RUNTIME=$(echo $f | cut -d'/' -f 5) - RUNTIME_DIR=$(echo $f | cut -d'/' -f 4) - COMMANDS+=("$BASE_COMMAND --runtime=$RUNTIME --runtime_dir=$RUNTIME_DIR --target_dir=$TARGET_DIR --pallet=$PALLET") - fi - ;; - polkadot) - # Example: polkadot/runtime/rococo/src/weights/pallet_balances.rs - RUNTIME=$(echo $f | cut -d'/' -f 3) - COMMANDS+=("$BASE_COMMAND --runtime=$RUNTIME --target_dir=$TARGET_DIR --pallet=$PALLET") - ;; - substrate) - # Example: substrate/frame/contracts/src/weights.rs - COMMANDS+=("$BASE_COMMAND --target_dir=$TARGET_DIR --runtime=dev --pallet=$PALLET") - ;; - *) - echo "Unknown dir: $TARGET_DIR" - exit 1 - ;; - esac - fi - - if [ "$REPO_NAME" == "trappist" ]; then - case $TARGET_DIR in - runtime) - TYPE=$(echo $f | cut -d'/' -f 2) - if [ "$TYPE" == "trappist" || "$TYPE" == "stout" ]; then - # Example: runtime/trappist/src/weights/pallet_assets.rs - COMMANDS+=("$BASE_COMMAND --target_dir=trappist --runtime=$TYPE --pallet=$PALLET") - fi - ;; - *) - echo "Unknown dir: $TARGET_DIR" - exit 1 - ;; - esac - fi -done - -for cmd in "${COMMANDS[@]}"; do - echo "Running command: $cmd" - . $cmd -done diff --git a/scripts/lib/bench-all-polkadot.sh b/scripts/lib/bench-all-polkadot.sh deleted file mode 100644 index ac52e00140e38de76fe082aa3fd0aaf670b465bb..0000000000000000000000000000000000000000 --- a/scripts/lib/bench-all-polkadot.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash - -# Runs all benchmarks for all pallets, for a given runtime, provided by $1 -# Should be run on a reference machine to gain accurate benchmarks -# current reference machine: https://github.com/paritytech/polkadot/pull/6508/files -# original source: https://github.com/paritytech/polkadot/blob/b9842c4b52f6791fef6c11ecd020b22fe614f041/scripts/run_all_benches.sh - -get_arg required --runtime "$@" -runtime="${out:-""}" - -# default RUST_LOG is error, but could be overridden -export RUST_LOG="${RUST_LOG:-error}" - -echo "[+] Compiling benchmarks..." -cargo build --profile $profile --locked --features=runtime-benchmarks -p polkadot - -POLKADOT_BIN="./target/$profile/polkadot" - -# Update the block and extrinsic overhead weights. -echo "[+] Benchmarking block and extrinsic overheads..." -OUTPUT=$( - $POLKADOT_BIN benchmark overhead \ - --chain="${runtime}-dev" \ - --wasm-execution=compiled \ - --weight-path="$output_path/runtime/${runtime}/constants/src/weights/" \ - --warmup=10 \ - --repeat=100 \ - --header="$output_path/file_header.txt" -) -if [ $? -ne 0 ]; then - echo "$OUTPUT" >> "$ERR_FILE" - echo "[-] Failed to benchmark the block and extrinsic overheads. Error written to $ERR_FILE; continuing..." -fi - - -# Load all pallet names in an array. -PALLETS=($( - $POLKADOT_BIN benchmark pallet --list --chain="${runtime}-dev" |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq -)) - -echo "[+] Benchmarking ${#PALLETS[@]} pallets for runtime $runtime" - -# Define the error file. -ERR_FILE="${ARTIFACTS_DIR}/benchmarking_errors.txt" -# Delete the error file before each run. -rm -f $ERR_FILE - -# Benchmark each pallet. -for PALLET in "${PALLETS[@]}"; do - echo "[+] Benchmarking $PALLET for $runtime"; - - output_file="" - if [[ $PALLET == *"::"* ]]; then - # translates e.g. "pallet_foo::bar" to "pallet_foo_bar" - output_file="${PALLET//::/_}.rs" - fi - - OUTPUT=$( - $POLKADOT_BIN benchmark pallet \ - --chain="${runtime}-dev" \ - --steps=50 \ - --repeat=20 \ - --no-storage-info \ - --no-median-slopes \ - --no-min-squares \ - --pallet="$PALLET" \ - --extrinsic="*" \ - --execution=wasm \ - --wasm-execution=compiled \ - --header="$output_path/file_header.txt" \ - --output="$output_path/runtime/${runtime}/src/weights/${output_file}" 2>&1 - ) - if [ $? -ne 0 ]; then - echo "$OUTPUT" >> "$ERR_FILE" - echo "[-] Failed to benchmark $PALLET. Error written to $ERR_FILE; continuing..." - fi -done - -# Check if the error file exists. -if [ -f "$ERR_FILE" ]; then - echo "[-] Some benchmarks failed. See: $ERR_FILE" -else - echo "[+] All benchmarks passed." -fi diff --git a/scripts/lib/bench-all-substrate.sh b/scripts/lib/bench-all-substrate.sh deleted file mode 100644 index eeb18cdd8bbb31ee58daa93b35e45cf2a1f33e86..0000000000000000000000000000000000000000 --- a/scripts/lib/bench-all-substrate.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env bash - -# This file is part of Substrate. -# Copyright (C) 2022 Parity Technologies (UK) Ltd. -# SPDX-License-Identifier: Apache-2.0 -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script has three parts which all use the Substrate runtime: -# - Pallet benchmarking to update the pallet weights -# - Overhead benchmarking for the Extrinsic and Block weights -# - Machine benchmarking -# -# Should be run on a reference machine to gain accurate benchmarks -# current reference machine: https://github.com/paritytech/substrate/pull/5848 - -# Original source: https://github.com/paritytech/substrate/blob/ff9921a260a67e3a71f25c8b402cd5c7da787a96/scripts/run_all_benchmarks.sh -# Fail if any sub-command in a pipe fails, not just the last one. -set -o pipefail -# Fail on undeclared variables. -set -u -# Fail if any sub-command fails. -set -e -# Fail on traps. -# set -E - -# default RUST_LOG is warn, but could be overridden -export RUST_LOG="${RUST_LOG:-error}" - -echo "[+] Compiling Substrate benchmarks..." -cargo build --profile=$profile --locked --features=runtime-benchmarks -p staging-node-cli - -# The executable to use. -SUBSTRATE="./target/$profile/substrate-node" - -# Manually exclude some pallets. -EXCLUDED_PALLETS=( - # Helper pallets - "pallet_election_provider_support_benchmarking" - # Pallets without automatic benchmarking - "pallet_babe" - "pallet_grandpa" - "pallet_mmr" - "pallet_offences" - # Only used for testing, does not need real weights. - "frame_benchmarking_pallet_pov" - "pallet_example_tasks" - "pallet_example_basic" - "pallet_example_split" - "pallet_example_kitchensink" - "pallet_example_mbm" - "tasks_example" -) - -# Load all pallet names in an array. -ALL_PALLETS=($( - $SUBSTRATE benchmark pallet --list --chain=dev |\ - tail -n+2 |\ - cut -d',' -f1 |\ - sort |\ - uniq -)) - -# Define the error file. -ERR_FILE="${ARTIFACTS_DIR}/benchmarking_errors.txt" - -# Delete the error file before each run. -rm -f "$ERR_FILE" - -mkdir -p "$(dirname "$ERR_FILE")" - -# Update the block and extrinsic overhead weights. -echo "[+] Benchmarking block and extrinsic overheads..." -OUTPUT=$( - $SUBSTRATE benchmark overhead \ - --chain=dev \ - --wasm-execution=compiled \ - --weight-path="$output_path/frame/support/src/weights/" \ - --header="$output_path/HEADER-APACHE2" \ - --warmup=10 \ - --repeat=100 2>&1 -) -if [ $? -ne 0 ]; then - echo "$OUTPUT" >> "$ERR_FILE" - echo "[-] Failed to benchmark the block and extrinsic overheads. Error written to $ERR_FILE; continuing..." -fi - -echo "[+] Benchmarking ${#ALL_PALLETS[@]} Substrate pallets and excluding ${#EXCLUDED_PALLETS[@]}." - -echo "[+] Excluded pallets ${EXCLUDED_PALLETS[@]}" -echo "[+] ------ " -echo "[+] Whole list pallets ${ALL_PALLETS[@]}" - -# Benchmark each pallet. -for PALLET in "${ALL_PALLETS[@]}"; do - FOLDER="$(echo "${PALLET#*_}" | tr '_' '-')"; - WEIGHT_FILE="$output_path/frame/${FOLDER}/src/weights.rs" - - # Skip the pallet if it is in the excluded list. - - if [[ " ${EXCLUDED_PALLETS[@]} " =~ " ${PALLET} " ]]; then - echo "[+] Skipping $PALLET as it is in the excluded list." - continue - fi - - echo "[+] Benchmarking $PALLET with weight file $WEIGHT_FILE"; - - set +e # Disable exit on error for the benchmarking of the pallets - OUTPUT=$( - $SUBSTRATE benchmark pallet \ - --chain=dev \ - --steps=50 \ - --repeat=20 \ - --pallet="$PALLET" \ - --no-storage-info \ - --no-median-slopes \ - --no-min-squares \ - --extrinsic="*" \ - --wasm-execution=compiled \ - --heap-pages=4096 \ - --output="$WEIGHT_FILE" \ - --header="$output_path/HEADER-APACHE2" \ - --template="$output_path/.maintain/frame-weight-template.hbs" 2>&1 - ) - if [ $? -ne 0 ]; then - echo -e "$PALLET: $OUTPUT\n" >> "$ERR_FILE" - echo "[-] Failed to benchmark $PALLET. Error written to $ERR_FILE; continuing..." - fi - set -e # Re-enable exit on error -done - - -# Check if the error file exists. -if [ -s "$ERR_FILE" ]; then - echo "[-] Some benchmarks failed. See: $ERR_FILE" - exit 1 -else - echo "[+] All benchmarks passed." -fi diff --git a/scripts/lib/bench-overhead.sh b/scripts/lib/bench-overhead.sh deleted file mode 100644 index c4cca8b4c128ca201adebb1e353ac11ad30b0825..0000000000000000000000000000000000000000 --- a/scripts/lib/bench-overhead.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash - -THIS_DIR=$(dirname "${BASH_SOURCE[0]}") -. "$THIS_DIR/../command-utils.sh" - -bench_overhead_common_args=( - -- - benchmark - overhead - --wasm-execution=compiled - --warmup=10 - --repeat=100 -) -bench_overhead() { - local args - case "$target_dir" in - substrate) - args=( - --bin=substrate - "${bench_overhead_common_args[@]}" - --header="$output_path/HEADER-APACHE2" - --weight-path="$output_path/frame/support/src/weights" - --chain="dev" - ) - ;; - polkadot) - get_arg required --runtime "$@" - local runtime="${out:-""}" - args=( - --bin=polkadot - "${bench_overhead_common_args[@]}" - --header="$output_path/file_header.txt" - --weight-path="$output_path/runtime/$runtime/constants/src/weights" - --chain="$runtime-dev" - ) - ;; - cumulus) - get_arg required --runtime "$@" - local runtime="${out:-""}" - args=( - -p=polkadot-parachain-bin - "${bench_overhead_common_args[@]}" - --header="$output_path/file_header.txt" - --weight-path="$output_path/parachains/runtimes/assets/$runtime/src/weights" - --chain="$runtime" - ) - ;; - trappist) - get_arg required --runtime "$@" - local runtime="${out:-""}" - args=( - "${bench_overhead_common_args[@]}" - --header="$output_path/templates/file_header.txt" - --weight-path="$output_path/runtime/$runtime/src/weights" - --chain="$runtime-dev" - ) - ;; - *) - die "Target Dir \"$target_dir\" is not supported in bench_overhead" - ;; - esac - - cargo_run "${args[@]}" -} - -bench_overhead "$@" diff --git a/scripts/lib/bench-pallet.sh b/scripts/lib/bench-pallet.sh deleted file mode 100644 index 15eac31e3a45cbb3c4f7c2dde3a1006164259f3a..0000000000000000000000000000000000000000 --- a/scripts/lib/bench-pallet.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/bin/bash - -THIS_DIR=$(dirname "${BASH_SOURCE[0]}") -. "$THIS_DIR/../command-utils.sh" - -bench_pallet_common_args=( - -- - benchmark - pallet - --steps=50 - --repeat=20 - --extrinsic="*" - --wasm-execution=compiled - --heap-pages=4096 - --json-file="${ARTIFACTS_DIR}/bench.json" -) -bench_pallet() { - get_arg required --subcommand "$@" - local subcommand="${out:-""}" - - get_arg required --runtime "$@" - local runtime="${out:-""}" - - get_arg required --pallet "$@" - local pallet="${out:-""}" - - local args - case "$target_dir" in - substrate) - args=( - --features=runtime-benchmarks - --manifest-path="$output_path/bin/node/cli/Cargo.toml" - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="$runtime" - ) - - case "$subcommand" in - pallet) - # Translates e.g. "pallet_foo::bar" to "pallet_foo_bar" - local output_dir="${pallet//::/_}" - - # Substrate benchmarks are output to the "frame" directory but they aren't - # named exactly after the $pallet argument. For example: - # - When $pallet == pallet_balances, the output folder is frame/balances - # - When $pallet == frame_benchmarking, the output folder is frame/benchmarking - # The common pattern we infer from those examples is that we should remove - # the prefix - if [[ "$output_dir" =~ ^[A-Za-z]*[^A-Za-z](.*)$ ]]; then - output_dir="${BASH_REMATCH[1]}" - fi - - # We also need to translate '_' to '-' due to the folders' naming - # conventions - output_dir="${output_dir//_/-}" - - args+=( - --header="$output_path/HEADER-APACHE2" - --output="$output_path/frame/$output_dir/src/weights.rs" - --template="$output_path/.maintain/frame-weight-template.hbs" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - polkadot) - # For backward compatibility: replace "-dev" with "" - runtime=${runtime/-dev/} - - local weights_dir="$output_path/runtime/${runtime}/src/weights" - - args=( - --bin=polkadot - --features=runtime-benchmarks - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="${runtime}-dev" - ) - - case "$subcommand" in - pallet) - args+=( - --header="$output_path/file_header.txt" - --output="${weights_dir}/" - ) - ;; - xcm) - args+=( - --header="$output_path/file_header.txt" - --template="$output_path/xcm/pallet-xcm-benchmarks/template.hbs" - --output="${weights_dir}/xcm/" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - cumulus) - get_arg required --runtime_dir "$@" - local runtime_dir="${out:-""}" - local chain="$runtime" - - # to support specifying parachain id from runtime name (e.g. ["glutton-westend", "glutton-westend-dev-1300"]) - # If runtime ends with "-dev" or "-dev-\d+", leave as it is, otherwise concat "-dev" at the end of $chain - if [[ ! "$runtime" =~ -dev(-[0-9]+)?$ ]]; then - chain="${runtime}-dev" - fi - - # replace "-dev" or "-dev-\d+" with "" for runtime - runtime=$(echo "$runtime" | sed 's/-dev.*//g') - - args=( - -p=polkadot-parachain-bin - --features=runtime-benchmarks - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="${chain}" - --header="$output_path/file_header.txt" - ) - - case "$subcommand" in - pallet) - args+=( - --output="$output_path/parachains/runtimes/$runtime_dir/$runtime/src/weights/" - ) - ;; - xcm) - mkdir -p "$output_path/parachains/runtimes/$runtime_dir/$runtime/src/weights/xcm" - args+=( - --template="$output_path/templates/xcm-bench-template.hbs" - --output="$output_path/parachains/runtimes/$runtime_dir/$runtime/src/weights/xcm/" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - trappist) - local weights_dir="$output_path/runtime/$runtime/src/weights" - - args=( - --features=runtime-benchmarks - "${bench_pallet_common_args[@]}" - --pallet="$pallet" - --chain="${runtime}-dev" - --header="$output_path/templates/file_header.txt" - ) - - case "$subcommand" in - pallet) - args+=( - --output="${weights_dir}/" - ) - ;; - xcm) - args+=( - --template="$output_path/templates/xcm-bench-template.hbs" - --output="${weights_dir}/xcm/" - ) - ;; - *) - die "Subcommand $subcommand is not supported for $target_dir in bench_pallet" - ;; - esac - ;; - *) - die "Repository $target_dir is not supported in bench_pallet" - ;; - esac - - cargo_run "${args[@]}" -} - -bench_pallet "$@" diff --git a/scripts/sync.sh b/scripts/sync.sh deleted file mode 100755 index b5d8a521993717d23469ddce4e1399bdd265220f..0000000000000000000000000000000000000000 --- a/scripts/sync.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail - -. "$(realpath "$(dirname "${BASH_SOURCE[0]}")/command-utils.sh")" - - -# Function to check syncing status -check_syncing() { - # Send the system_health request and parse the isSyncing field - RESPONSE=$(curl -sSX POST http://127.0.0.1:9944 \ - --header 'Content-Type: application/json' \ - --data-raw '{"jsonrpc": "2.0", "method": "system_health", "params": [], "id": "1"}') - - # Check for errors in the curl command - if [ $? -ne 0 ]; then - echo "Error: Unable to send request to Polkadot node" - fi - - IS_SYNCING=$(echo $RESPONSE | jq -r '.result.isSyncing') - - # Check for errors in the jq command or missing field in the response - if [ $? -ne 0 ] || [ "$IS_SYNCING" == "null" ]; then - echo "Error: Unable to parse sync status from response" - fi - - # Return the isSyncing value - echo $IS_SYNCING -} - -main() { - get_arg required --chain "$@" - local chain="${out:-""}" - - get_arg required --type "$@" - local type="${out:-""}" - - export RUST_LOG="${RUST_LOG:-remote-ext=debug,runtime=trace}" - - cargo build --release - - cp "./target/release/polkadot" ./polkadot-bin - - # Start sync. - # "&" runs the process in the background - # "> /dev/tty" redirects the output of the process to the terminal - ./polkadot-bin --sync="$type" --chain="$chain" > "$ARTIFACTS_DIR/sync.log" 2>&1 & - - # Get the PID of process - POLKADOT_SYNC_PID=$! - - sleep 10 - - # Poll the node every 100 seconds until syncing is complete - while :; do - SYNC_STATUS="$(check_syncing)" - if [ "$SYNC_STATUS" == "true" ]; then - echo "Node is still syncing..." - sleep 100 - elif [ "$SYNC_STATUS" == "false" ]; then - echo "Node sync is complete!" - kill "$POLKADOT_SYNC_PID" # Stop the Polkadot node process once syncing is complete - exit 0 # Success - elif [[ "$SYNC_STATUS" = Error:* ]]; then - echo "$SYNC_STATUS" - exit 1 # Error - else - echo "Unknown error: $SYNC_STATUS" - exit 1 # Unknown error - fi - done -} - -main "$@" diff --git a/scripts/update-ui-tests.sh b/scripts/update-ui-tests.sh old mode 100755 new mode 100644 index d363e51e40414c8272a4266444f381efd472ec25..a1f380c4712d3e9819fd4544cd7a450db1f32b7a --- a/scripts/update-ui-tests.sh +++ b/scripts/update-ui-tests.sh @@ -21,7 +21,7 @@ if [ ! -z "$1" ]; then echo "rustup needs to be installed" exit fi - + rustup install $RUST_VERSION rustup component add rust-src --toolchain $RUST_VERSION fi @@ -38,4 +38,4 @@ $RUSTUP_RUN cargo test --manifest-path substrate/primitives/runtime-interface/Ca $RUSTUP_RUN cargo test -p sp-api-test ui $RUSTUP_RUN cargo test -p frame-election-provider-solution-type ui $RUSTUP_RUN cargo test -p frame-support-test --features=no-metadata-docs,try-runtime,experimental ui -$RUSTUP_RUN cargo test -p xcm-procedural ui +$RUSTUP_RUN cargo test -p xcm-procedural ui \ No newline at end of file diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 5b827c9d718fefecf3c27d23f9c9e4274c321f64..6e734a723cd36f4f86ae6a7f1ad032d8c702ecab 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -104,6 +104,10 @@ try-runtime = [ "polkadot-sdk/try-runtime", "substrate-cli-test-utils/try-runtime", ] +riscv = [ + "kitchensink-runtime/riscv", + "polkadot-sdk/riscv", +] [[bench]] name = "transaction_pool" diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index 8239637b3a9f3236fc746bab887a58f17b578242..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, @@ -124,7 +127,7 @@ fn extrinsic_set_time(now: u64) -> OpaqueExtrinsic { .into() } -fn import_block(mut client: &FullClient, built: BuiltBlock) { +fn import_block(client: &FullClient, built: BuiltBlock) { let mut params = BlockImportParams::new(BlockOrigin::File, built.block.header); params.state_action = StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(built.storage_changes)); diff --git a/substrate/bin/node/cli/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 d58ca888d419bb1a6350d73b904dc3e4d13ff601..1b345a23f27e42fc650731dff8441966cbeb8676 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -37,7 +37,7 @@ use sc_consensus_babe::{self, SlotProportion}; use sc_network::{ event::Event, service::traits::NetworkService, NetworkBackend, NetworkEventStream, }; -use sc_network_sync::{strategy::warp::WarpSyncParams, SyncingService}; +use sc_network_sync::{strategy::warp::WarpSyncConfig, SyncingService}; use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; use sc_statement_store::Store as StatementStore; use sc_telemetry::{Telemetry, TelemetryWorker}; @@ -177,7 +177,6 @@ pub fn new_partial( sc_transaction_pool::FullPool, ( impl Fn( - node_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor, ) -> Result, sc_service::Error>, ( @@ -209,7 +208,7 @@ pub fn new_partial( }) .transpose()?; - let executor = sc_service::new_wasm_executor(&config); + let executor = sc_service::new_wasm_executor(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( @@ -318,13 +317,12 @@ pub fn new_partial( let rpc_backend = backend.clone(); let rpc_statement_store = statement_store.clone(); let rpc_extensions_builder = - move |deny_unsafe, subscription_executor: node_rpc::SubscriptionTaskExecutor| { + move |subscription_executor: node_rpc::SubscriptionTaskExecutor| { let deps = node_rpc::FullDeps { client: client.clone(), pool: pool.clone(), select_chain: select_chain.clone(), chain_spec: chain_spec.cloned_box(), - deny_unsafe, babe: node_rpc::BabeDeps { keystore: keystore.clone(), babe_worker_handle: babe_worker_handle.clone(), @@ -406,7 +404,7 @@ pub fn new_full_base::Hash>>( ), ) -> Result { let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks = Some(sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging::default()); @@ -418,7 +416,7 @@ pub fn new_full_base::Hash>>( let hwbench = (!disable_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) })) .flatten(); @@ -517,7 +515,7 @@ pub fn new_full_base::Hash>>( spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; @@ -555,7 +553,7 @@ pub fn new_full_base::Hash>>( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, false) { Err(err) if role.is_authority() => { log::warn!( "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", @@ -719,7 +717,7 @@ pub fn new_full_base::Hash>>( name: Some(name), observer_enabled: false, keystore, - local_role: role.clone(), + local_role: role, telemetry: telemetry.as_ref().map(|x| x.handle()), protocol_name: grandpa_protocol_name, }; diff --git a/substrate/bin/node/cli/tests/basic.rs b/substrate/bin/node/cli/tests/basic.rs index 0a2e3fd25047f39c51ca75c59a0791fe5e040a56..037ddbb1e47b573df0e019b25a28c09601d95b83 100644 --- a/substrate/bin/node/cli/tests/basic.rs +++ b/substrate/bin/node/cli/tests/basic.rs @@ -850,7 +850,7 @@ fn should_import_block_with_test_client() { sp_consensus::BlockOrigin, ClientBlockImportExt, TestClientBuilder, TestClientBuilderExt, }; - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let block1 = changes_trie_block(); let block_data = block1.0; let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap(); diff --git a/substrate/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/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index d85998e3c87b05bd6821a1769320e602bee7d6ee..02f5d9a4a70258f1259042bbd1b6f3096c70725c 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -31,7 +31,6 @@ sc-consensus-grandpa = { workspace = true, default-features = true } sc-consensus-grandpa-rpc = { workspace = true, default-features = true } sc-mixnet = { workspace = true, default-features = true } sc-rpc = { workspace = true, default-features = true } -sc-rpc-api = { workspace = true, default-features = true } sc-sync-state-rpc = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } diff --git a/substrate/bin/node/rpc/src/lib.rs b/substrate/bin/node/rpc/src/lib.rs index c55e03ee9d6f376b81519186e1335478fd893878..988502bb2bfdc498ce0d9662912a2c93ac074add 100644 --- a/substrate/bin/node/rpc/src/lib.rs +++ b/substrate/bin/node/rpc/src/lib.rs @@ -44,7 +44,6 @@ use sc_consensus_grandpa::{ FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState, }; pub use sc_rpc::SubscriptionTaskExecutor; -pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_application_crypto::RuntimeAppPublic; @@ -97,8 +96,6 @@ pub struct FullDeps { pub select_chain: SC, /// A copy of the chain spec. pub chain_spec: Box, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, /// BABE specific dependencies. pub babe: BabeDeps, /// GRANDPA specific dependencies. @@ -120,7 +117,6 @@ pub fn create_full( pool, select_chain, chain_spec, - deny_unsafe, babe, grandpa, beefy, @@ -175,7 +171,7 @@ where finality_provider, } = grandpa; - io.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + io.merge(System::new(client.clone(), pool).into_rpc())?; // Making synchronous calls in light client freezes the browser currently, // more context: https://github.com/paritytech/substrate/pull/3480 // These RPCs should use an asynchronous caller instead. @@ -190,8 +186,7 @@ where )?; io.merge(TransactionPayment::new(client.clone()).into_rpc())?; io.merge( - Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain, deny_unsafe) - .into_rpc(), + Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain).into_rpc(), )?; io.merge( Grandpa::new( @@ -209,10 +204,9 @@ where .into_rpc(), )?; - io.merge(StateMigration::new(client.clone(), backend, deny_unsafe).into_rpc())?; - io.merge(Dev::new(client, deny_unsafe).into_rpc())?; - let statement_store = - sc_rpc::statement::StatementStore::new(statement_store, deny_unsafe).into_rpc(); + io.merge(StateMigration::new(client.clone(), backend).into_rpc())?; + io.merge(Dev::new(client).into_rpc())?; + let statement_store = sc_rpc::statement::StatementStore::new(statement_store).into_rpc(); io.merge(statement_store)?; if let Some(mixnet_api) = mixnet_api { diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 2ad655883916dbfc189b8c66f81d79db55921e48..6310e16d5a14d4d3e4e3b4053448ba488bb125cb 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -31,7 +31,7 @@ serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true } # pallet-asset-conversion: turn on "num-traits" feature primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } -polkadot-sdk = { features = ["runtime", "tuples-96"], workspace = true } +polkadot-sdk = { features = ["runtime-full", "tuples-96"], workspace = true } # shared code between runtime and node node-primitives = { workspace = true } @@ -71,5 +71,5 @@ try-runtime = [ experimental = [ "pallet-example-tasks/experimental", ] - metadata-hash = ["substrate-wasm-builder/metadata-hash"] +riscv = ["polkadot-sdk/riscv"] diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index a94838cf20c05fbc44b01b4eba09e4f6b5c12e47..c8409078af571d4718e73e5141c3df1fa18940eb 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -91,7 +91,7 @@ use sp_consensus_beefy::{ mmr::MmrLeafVersion, }; use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, @@ -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; } @@ -1114,6 +1113,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 +1130,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 +1203,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< @@ -1381,6 +1398,30 @@ impl pallet_contracts::Config for Runtime { type Xcm = (); } +impl pallet_revive::Config for Runtime { + type Time = Timestamp; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type WeightPrice = pallet_transaction_payment::Pallet; + type WeightInfo = pallet_revive::weights::SubstrateWeight; + type ChainExtension = (); + type AddressMapper = pallet_revive::DefaultAddressMapper; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type RuntimeHoldReason = RuntimeHoldReason; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type Debug = (); + type Xcm = (); +} + impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -1488,7 +1529,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; } @@ -1612,6 +1653,7 @@ impl pallet_beefy_mmr::Config for Runtime { type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = Vec; type BeefyDataProvider = (); + type WeightInfo = (); } parameter_types! { @@ -1998,6 +2040,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! { @@ -2481,6 +2526,9 @@ mod runtime { #[runtime::pallet_index(79)] pub type AssetConversionMigration = pallet_asset_conversion_ops::Pallet; + + #[runtime::pallet_index(80)] + pub type Revive = pallet_revive::Pallet; } /// The address format for describing accounts. @@ -2560,7 +2608,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; } @@ -2585,12 +2633,14 @@ mod benches { [pallet_babe, Babe] [pallet_bags_list, VoterList] [pallet_balances, Balances] + [pallet_beefy_mmr, MmrLeaf] [pallet_bounties, Bounties] [pallet_broker, Broker] [pallet_child_bounties, ChildBounties] [pallet_collective, Council] [pallet_conviction_voting, ConvictionVoting] [pallet_contracts, Contracts] + [pallet_revive, Revive] [pallet_core_fellowship, CoreFellowship] [tasks_example, TasksExample] [pallet_democracy, Democracy] @@ -2782,6 +2832,14 @@ impl_runtime_apis! { fn member_needs_delegate_migration(member: AccountId) -> bool { NominationPools::api_member_needs_delegate_migration(member) } + + fn member_total_balance(member: AccountId) -> Balance { + NominationPools::api_member_total_balance(member) + } + + fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance { + NominationPools::api_pool_balance(pool_id) + } } impl pallet_staking_runtime_api::StakingApi for Runtime { @@ -2945,6 +3003,75 @@ impl_runtime_apis! { } } + impl pallet_revive::ReviveApi for Runtime + { + fn call( + origin: AccountId, + dest: H160, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> pallet_revive::ContractExecResult { + Revive::bare_call( + RuntimeOrigin::signed(origin), + dest, + value, + gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + input_data, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: pallet_revive::Code, + data: Vec, + salt: Option<[u8; 32]>, + ) -> pallet_revive::ContractInstantiateResult + { + Revive::bare_instantiate( + RuntimeOrigin::signed(origin), + value, + gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + code, + data, + salt, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + ) -> pallet_revive::CodeUploadResult + { + Revive::bare_upload_code( + RuntimeOrigin::signed(origin), + code, + storage_deposit_limit.unwrap_or(u128::MAX), + ) + } + + fn get_storage( + address: H160, + key: [u8; 32], + ) -> pallet_revive::GetStorageResult { + Revive::get_storage( + address, + key + ) + } + } + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< Block, Balance, 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/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/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs index 74805488792ad5f1eb810d9c22f00d719f1b1b21..527a3d12d9e766c3b15c158ed1fafed8dc56f08c 100644 --- a/substrate/client/basic-authorship/src/basic_authorship.rs +++ b/substrate/client/basic-authorship/src/basic_authorship.rs @@ -852,7 +852,7 @@ mod tests { block }; - let import_and_maintain = |mut client: Arc, block: TestBlock| { + let import_and_maintain = |client: Arc, block: TestBlock| { let hash = block.hash(); block_on(client.import(BlockOrigin::Own, block)).unwrap(); block_on(txpool.maintain(chain_event( diff --git a/substrate/client/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/lib.rs b/substrate/client/chain-spec/src/lib.rs index c43f9e89b8a993f6a349b3eb612b41346acc62ec..5451428d34811bfa54cb87a767d8434b5f4b7524 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -172,6 +172,12 @@ //! //! //! +//! The main purpose of the `RuntimeGenesisConfig` patch is to: +//! - minimize the maintenance effort when RuntimeGenesisConfig is changed in the future (e.g. new +//! pallets added to the runtime or pallet's genesis config changed), +//! - increase the readability - it only contains the relevant fields, +//! - allow to apply numerous changes in distinct domains (e.g. for zombienet). +//! //! For production or long-lasting blockchains, using the `raw` format in the chain specification is //! recommended. Only the `raw` format guarantees that storage root hash will remain unchanged when //! the `RuntimeGenesisConfig` format changes due to software upgrade. diff --git a/substrate/client/cli/src/arg_enums.rs b/substrate/client/cli/src/arg_enums.rs index b5819d03447a8bfb6eb3e9e40d2f129af80283ea..cd245dc0155453e49347c5fb9fc8e13f75a803db 100644 --- a/substrate/client/cli/src/arg_enums.rs +++ b/substrate/client/cli/src/arg_enums.rs @@ -168,6 +168,19 @@ pub enum RpcMethods { Unsafe, } +impl FromStr for RpcMethods { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "safe" => Ok(RpcMethods::Safe), + "unsafe" => Ok(RpcMethods::Unsafe), + "auto" => Ok(RpcMethods::Auto), + invalid => Err(format!("Invalid rpc methods {invalid}")), + } + } +} + impl Into for RpcMethods { fn into(self) -> sc_service::config::RpcMethods { match self { diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index c1288b502c95a4045215b66d57e18d7ade0e38ab..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, SharedParams, - TransactionPoolParams, + ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, RpcEndpoint, + SharedParams, TransactionPoolParams, }, - CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, - RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, - 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::{IpAddr, Ipv4Addr, SocketAddr}, - num::NonZeroU32, -}; +use std::num::NonZeroU32; /// The `run` command used to run a node. #[derive(Debug, Clone, Parser)] @@ -59,113 +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, - - /// 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, @@ -304,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> { @@ -386,83 +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()) - } - - fn rpc_addr(&self, default_listen_port: u16) -> Result> { - let interface = rpc_interface( - self.rpc_external, - self.unsafe_rpc_external, - self.rpc_methods, - self.validator, - )?; - - Ok(Some(SocketAddr::new(interface, self.rpc_port.unwrap_or(default_listen_port)))) + self.rpc_params.rpc_cors(is_dev) + } + + fn rpc_addr(&self, default_listen_port: u16) -> Result>> { + 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 { @@ -496,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 { - 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.into()) - } else { - Ok(Ipv4Addr::LOCALHOST.into()) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 406d1fb264ddfafdbabdfe8c5413bc194f2514a4..59238b3307cf25bced2bfa3c5b99391214954bca 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -20,21 +20,22 @@ use crate::{ arg_enums::Database, error::Result, DatabaseParams, ImportParams, KeystoreParams, - NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, SharedParams, SubstrateCli, + NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, RpcEndpoint, SharedParams, + SubstrateCli, }; use log::warn; use names::{Generator, Name}; 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, }; use sc_tracing::logging::LoggerBuilder; -use std::{net::SocketAddr, num::NonZeroU32, path::PathBuf}; +use std::{num::NonZeroU32, path::PathBuf}; /// The maximum number of characters for a node name. pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; @@ -301,7 +302,7 @@ pub trait CliConfiguration: Sized { } /// Get the RPC address. - fn rpc_addr(&self, _default_listen_port: u16) -> Result> { + fn rpc_addr(&self, _default_listen_port: u16) -> Result>> { Ok(None) } @@ -504,6 +505,10 @@ pub trait CliConfiguration: Sized { let telemetry_endpoints = self.telemetry_endpoints(&chain_spec)?; let runtime_cache_size = self.runtime_cache_size()?; + let rpc_addrs: Option> = self + .rpc_addr(DCV::rpc_listen_port())? + .map(|addrs| addrs.into_iter().map(Into::into).collect()); + Ok(Configuration { impl_name: C::impl_name(), impl_version: C::impl_version(), @@ -525,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: self.rpc_addr(DCV::rpc_listen_port())?, - 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()?, @@ -552,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, }) } @@ -613,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); @@ -640,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/mod.rs b/substrate/client/cli/src/params/mod.rs index f07223ec6a73ea93b2b1ae6b76238ed9a45aab50..977b57ba06586cfa2141abbe90a23225cb6cf2db 100644 --- a/substrate/client/cli/src/params/mod.rs +++ b/substrate/client/cli/src/params/mod.rs @@ -25,6 +25,7 @@ mod node_key_params; mod offchain_worker_params; mod prometheus_params; mod pruning_params; +mod rpc_params; mod runtime_params; mod shared_params; mod telemetry_params; @@ -32,6 +33,7 @@ mod transaction_pool_params; use crate::arg_enums::{CryptoScheme, OutputType}; use clap::Args; +use sc_service::config::{IpNetwork, RpcBatchRequestConfig}; use sp_core::crypto::{Ss58AddressFormat, Ss58AddressFormatRegistry}; use sp_runtime::{ generic::BlockId, @@ -42,7 +44,7 @@ use std::{fmt::Debug, str::FromStr}; pub use crate::params::{ database_params::*, import_params::*, keystore_params::*, message_params::*, mixnet_params::*, network_params::*, node_key_params::*, offchain_worker_params::*, prometheus_params::*, - pruning_params::*, runtime_params::*, shared_params::*, telemetry_params::*, + pruning_params::*, rpc_params::*, runtime_params::*, shared_params::*, telemetry_params::*, transaction_pool_params::*, }; diff --git a/substrate/client/cli/src/params/node_key_params.rs b/substrate/client/cli/src/params/node_key_params.rs index 0e12c7a2a2d3742ef3d9aa3681f900036e67a467..cdd637888114af4e7a3f8ab8213fc9c8b2c9f730 100644 --- a/substrate/client/cli/src/params/node_key_params.rs +++ b/substrate/client/cli/src/params/node_key_params.rs @@ -237,7 +237,6 @@ mod tests { |params| { let dir = PathBuf::from(net_config_dir.clone()); let typ = params.node_key_type; - let role = role.clone(); params.node_key(net_config_dir, role, is_dev).and_then(move |c| match c { NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && diff --git a/substrate/client/cli/src/params/rpc_params.rs b/substrate/client/cli/src/params/rpc_params.rs new file mode 100644 index 0000000000000000000000000000000000000000..f9937b59bbaf4fb00ccd5a3e09ad1cbc67801b13 --- /dev/null +++ b/substrate/client/cli/src/params/rpc_params.rs @@ -0,0 +1,681 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + arg_enums::{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 clap::Args; +use std::{ + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + num::NonZeroU32, +}; + +const RPC_LISTEN_ADDR: &str = "listen-addr"; +const RPC_CORS: &str = "cors"; +const RPC_MAX_CONNS: &str = "max-connections"; +const RPC_MAX_REQUEST_SIZE: &str = "max-request-size"; +const RPC_MAX_RESPONSE_SIZE: &str = "max-response-size"; +const RPC_MAX_SUBS_PER_CONN: &str = "max-subscriptions-per-connection"; +const RPC_MAX_BUF_CAP_PER_CONN: &str = "max-buffer-capacity-per-connection"; +const RPC_RATE_LIMIT: &str = "rate-limit"; +const RPC_RATE_LIMIT_TRUST_PROXY_HEADERS: &str = "rate-limit-trust-proxy-headers"; +const RPC_RATE_LIMIT_WHITELISTED_IPS: &str = "rate-limit-whitelisted-ips"; +const RPC_RETRY_RANDOM_PORT: &str = "retry-random-port"; +const RPC_METHODS: &str = "methods"; +const RPC_OPTIONAL: &str = "optional"; +const RPC_DISABLE_BATCH: &str = "disable-batch-requests"; +const RPC_BATCH_LIMIT: &str = "max-batch-request-len"; + +/// 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 { + /// Listen address. + pub listen_addr: SocketAddr, + /// Batch request configuration. + pub batch_config: RpcBatchRequestConfig, + /// Maximum number of connections. + pub max_connections: u32, + /// Maximum inbound payload size in MB. + pub max_payload_in_mb: u32, + /// Maximum outbound payload size in MB. + pub max_payload_out_mb: u32, + /// Maximum number of subscriptions per connection. + pub max_subscriptions_per_connection: u32, + /// Maximum buffer capacity per connection. + pub max_buffer_capacity_per_connection: u32, + /// Rate limit per minute. + pub rate_limit: Option, + /// Whether to trust proxy headers for rate limiting. + pub rate_limit_trust_proxy_headers: bool, + /// Whitelisted IPs for rate limiting. + pub rate_limit_whitelisted_ips: Vec, + /// CORS. + pub cors: Option>, + /// RPC methods to expose. + pub rpc_methods: RpcMethods, + /// Whether it's an optional listening address i.e, it's ignored if it fails to bind. + /// For example substrate tries to bind both ipv4 and ipv6 addresses but some platforms + /// may not support ipv6. + pub is_optional: bool, + /// Whether to retry with a random port if the provided port is already in use. + pub retry_random_port: bool, +} + +impl std::str::FromStr for RpcEndpoint { + type Err = String; + + fn from_str(s: &str) -> Result { + let mut listen_addr = None; + let mut max_connections = None; + let mut max_payload_in_mb = None; + let mut max_payload_out_mb = None; + let mut max_subscriptions_per_connection = None; + let mut max_buffer_capacity_per_connection = None; + let mut cors: Option> = None; + let mut rpc_methods = None; + let mut is_optional = None; + let mut disable_batch_requests = None; + let mut max_batch_request_len = None; + let mut rate_limit = None; + let mut rate_limit_trust_proxy_headers = None; + let mut rate_limit_whitelisted_ips = Vec::new(); + let mut retry_random_port = None; + + for input in s.split(',') { + let (key, val) = input.trim().split_once('=').ok_or_else(|| invalid_input(input))?; + let key = key.trim(); + let val = val.trim(); + + match key { + RPC_LISTEN_ADDR => { + if listen_addr.is_some() { + return Err(only_once_err(RPC_LISTEN_ADDR)); + } + let val: SocketAddr = + val.parse().map_err(|_| invalid_value(RPC_LISTEN_ADDR, &val))?; + listen_addr = Some(val); + }, + RPC_CORS => { + if val.is_empty() { + return Err(invalid_value(RPC_CORS, &val)); + } + + if let Some(cors) = cors.as_mut() { + cors.push(val.to_string()); + } else { + cors = Some(vec![val.to_string()]); + } + }, + RPC_MAX_CONNS => { + if max_connections.is_some() { + return Err(only_once_err(RPC_MAX_CONNS)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_MAX_CONNS, &val))?; + max_connections = Some(val); + }, + RPC_MAX_REQUEST_SIZE => { + if max_payload_in_mb.is_some() { + return Err(only_once_err(RPC_MAX_REQUEST_SIZE)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_RESPONSE_SIZE, &val))?; + max_payload_in_mb = Some(val); + }, + RPC_MAX_RESPONSE_SIZE => { + if max_payload_out_mb.is_some() { + return Err(only_once_err(RPC_MAX_RESPONSE_SIZE)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_RESPONSE_SIZE, &val))?; + max_payload_out_mb = Some(val); + }, + RPC_MAX_SUBS_PER_CONN => { + if max_subscriptions_per_connection.is_some() { + return Err(only_once_err(RPC_MAX_SUBS_PER_CONN)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_SUBS_PER_CONN, &val))?; + max_subscriptions_per_connection = Some(val); + }, + RPC_MAX_BUF_CAP_PER_CONN => { + if max_buffer_capacity_per_connection.is_some() { + return Err(only_once_err(RPC_MAX_BUF_CAP_PER_CONN)); + } + + let val = + val.parse().map_err(|_| invalid_value(RPC_MAX_BUF_CAP_PER_CONN, &val))?; + max_buffer_capacity_per_connection = Some(val); + }, + RPC_RATE_LIMIT => { + if rate_limit.is_some() { + return Err(only_once_err("rate-limit")); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_RATE_LIMIT, &val))?; + rate_limit = Some(val); + }, + RPC_RATE_LIMIT_TRUST_PROXY_HEADERS => { + if rate_limit_trust_proxy_headers.is_some() { + return Err(only_once_err(RPC_RATE_LIMIT_TRUST_PROXY_HEADERS)); + } + + let val = val + .parse() + .map_err(|_| invalid_value(RPC_RATE_LIMIT_TRUST_PROXY_HEADERS, &val))?; + rate_limit_trust_proxy_headers = Some(val); + }, + RPC_RATE_LIMIT_WHITELISTED_IPS => { + let ip: IpNetwork = val + .parse() + .map_err(|_| invalid_value(RPC_RATE_LIMIT_WHITELISTED_IPS, &val))?; + rate_limit_whitelisted_ips.push(ip); + }, + RPC_RETRY_RANDOM_PORT => { + if retry_random_port.is_some() { + return Err(only_once_err(RPC_RETRY_RANDOM_PORT)); + } + let val = + val.parse().map_err(|_| invalid_value(RPC_RETRY_RANDOM_PORT, &val))?; + retry_random_port = Some(val); + }, + RPC_METHODS => { + if rpc_methods.is_some() { + return Err(only_once_err("methods")); + } + let val = val.parse().map_err(|_| invalid_value(RPC_METHODS, &val))?; + rpc_methods = Some(val); + }, + RPC_OPTIONAL => { + if is_optional.is_some() { + return Err(only_once_err(RPC_OPTIONAL)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_OPTIONAL, &val))?; + is_optional = Some(val); + }, + RPC_DISABLE_BATCH => { + if disable_batch_requests.is_some() { + return Err(only_once_err(RPC_DISABLE_BATCH)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_DISABLE_BATCH, &val))?; + disable_batch_requests = Some(val); + }, + RPC_BATCH_LIMIT => { + if max_batch_request_len.is_some() { + return Err(only_once_err(RPC_BATCH_LIMIT)); + } + + let val = val.parse().map_err(|_| invalid_value(RPC_BATCH_LIMIT, &val))?; + max_batch_request_len = Some(val); + }, + _ => return Err(invalid_key(key)), + } + } + + let listen_addr = listen_addr.ok_or("`listen-addr` must be specified exactly once")?; + + let batch_config = match (disable_batch_requests, max_batch_request_len) { + (Some(true), Some(_)) => { + return Err(format!("`{RPC_BATCH_LIMIT}` and `{RPC_DISABLE_BATCH}` are mutually exclusive and can't be used together")); + }, + (Some(false), None) => RpcBatchRequestConfig::Disabled, + (None, Some(len)) => RpcBatchRequestConfig::Limit(len), + _ => RpcBatchRequestConfig::Unlimited, + }; + + Ok(Self { + listen_addr, + batch_config, + max_connections: max_connections.unwrap_or(RPC_DEFAULT_MAX_CONNECTIONS), + max_payload_in_mb: max_payload_in_mb.unwrap_or(RPC_DEFAULT_MAX_REQUEST_SIZE_MB), + max_payload_out_mb: max_payload_out_mb.unwrap_or(RPC_DEFAULT_MAX_RESPONSE_SIZE_MB), + cors, + max_buffer_capacity_per_connection: max_buffer_capacity_per_connection + .unwrap_or(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN), + max_subscriptions_per_connection: max_subscriptions_per_connection + .unwrap_or(RPC_DEFAULT_MAX_SUBS_PER_CONN), + rpc_methods: rpc_methods.unwrap_or(RpcMethods::Auto), + rate_limit, + rate_limit_trust_proxy_headers: rate_limit_trust_proxy_headers.unwrap_or(false), + rate_limit_whitelisted_ips, + is_optional: is_optional.unwrap_or(false), + retry_random_port: retry_random_port.unwrap_or(false), + }) + } +} + +impl Into for RpcEndpoint { + fn into(self) -> sc_service::config::RpcEndpoint { + sc_service::config::RpcEndpoint { + batch_config: self.batch_config, + listen_addr: self.listen_addr, + max_buffer_capacity_per_connection: self.max_buffer_capacity_per_connection, + max_connections: self.max_connections, + max_payload_in_mb: self.max_payload_in_mb, + max_payload_out_mb: self.max_payload_out_mb, + max_subscriptions_per_connection: self.max_subscriptions_per_connection, + rpc_methods: self.rpc_methods.into(), + rate_limit: self.rate_limit, + rate_limit_trust_proxy_headers: self.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rate_limit_whitelisted_ips, + cors: self.cors, + retry_random_port: self.retry_random_port, + is_optional: self.is_optional, + } + } +} + +impl RpcEndpoint { + /// Returns whether the endpoint is globally exposed. + pub fn is_global(&self) -> bool { + let ip = IpNetwork::from(self.listen_addr.ip()); + ip.is_global() + } +} + +fn only_once_err(reason: &str) -> String { + format!("`{reason}` is only allowed be specified once") +} + +fn invalid_input(input: &str) -> String { + format!("`{input}`, expects: `key=value`") +} + +fn invalid_value(key: &str, value: &str) -> String { + format!("value=`{value}` key=`{key}`") +} + +fn invalid_key(key: &str) -> String { + format!("unknown key=`{key}`, see `--help` for available options") +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{num::NonZeroU32, str::FromStr}; + + #[test] + fn parse_rpc_endpoint_works() { + assert!(RpcEndpoint::from_str("listen-addr=127.0.0.1:9944").is_ok()); + assert!(RpcEndpoint::from_str("listen-addr=[::1]:9944").is_ok()); + assert!(RpcEndpoint::from_str("listen-addr=127.0.0.1:9944,methods=auto").is_ok()); + assert!(RpcEndpoint::from_str("listen-addr=[::1]:9944,methods=auto").is_ok()); + assert!(RpcEndpoint::from_str( + "listen-addr=127.0.0.1:9944,methods=auto,cors=*,optional=true" + ) + .is_ok()); + + assert!(RpcEndpoint::from_str("listen-addrs=127.0.0.1:9944,foo=*").is_err()); + assert!(RpcEndpoint::from_str("listen-addrs=127.0.0.1:9944,cors=").is_err()); + } + + #[test] + fn parse_rpc_endpoint_all() { + let endpoint = RpcEndpoint::from_str( + "listen-addr=127.0.0.1:9944,methods=unsafe,cors=*,optional=true,retry-random-port=true,rate-limit=99,\ + max-batch-request-len=100,rate-limit-trust-proxy-headers=true,max-connections=33,max-request-size=4,\ + max-response-size=3,max-subscriptions-per-connection=7,max-buffer-capacity-per-connection=8,\ + rate-limit-whitelisted-ips=192.168.1.0/24,rate-limit-whitelisted-ips=ff01::0/32" + ).unwrap(); + assert_eq!(endpoint.listen_addr, ([127, 0, 0, 1], 9944).into()); + assert_eq!(endpoint.rpc_methods, RpcMethods::Unsafe); + assert_eq!(endpoint.cors, Some(vec!["*".to_string()])); + assert_eq!(endpoint.is_optional, true); + assert_eq!(endpoint.retry_random_port, true); + assert_eq!(endpoint.rate_limit, Some(NonZeroU32::new(99).unwrap())); + assert!(matches!(endpoint.batch_config, RpcBatchRequestConfig::Limit(l) if l == 100)); + assert_eq!(endpoint.rate_limit_trust_proxy_headers, true); + assert_eq!( + endpoint.rate_limit_whitelisted_ips, + vec![ + IpNetwork::V4("192.168.1.0/24".parse().unwrap()), + IpNetwork::V6("ff01::0/32".parse().unwrap()) + ] + ); + assert_eq!(endpoint.max_connections, 33); + assert_eq!(endpoint.max_payload_in_mb, 4); + assert_eq!(endpoint.max_payload_out_mb, 3); + assert_eq!(endpoint.max_subscriptions_per_connection, 7); + assert_eq!(endpoint.max_buffer_capacity_per_connection, 8); + } + + #[test] + fn parse_rpc_endpoint_multiple_cors() { + let addr = RpcEndpoint::from_str( + "listen-addr=127.0.0.1:9944,methods=auto,cors=https://polkadot.js.org,cors=*,cors=localhost:*", + ) + .unwrap(); + + assert_eq!( + addr.cors, + Some(vec![ + "https://polkadot.js.org".to_string(), + "*".to_string(), + "localhost:*".to_string() + ]) + ); + } + + #[test] + fn parse_rpc_endpoint_whitespaces() { + let addr = RpcEndpoint::from_str( + " listen-addr = 127.0.0.1:9944, methods = auto, optional = true ", + ) + .unwrap(); + assert_eq!(addr.rpc_methods, RpcMethods::Auto); + assert_eq!(addr.is_optional, true); + } + + #[test] + fn parse_rpc_endpoint_batch_options_mutually_exclusive() { + assert!(RpcEndpoint::from_str( + "listen-addr = 127.0.0.1:9944,disable-batch-requests=true,max-batch-request-len=100", + ) + .is_err()); + } +} diff --git a/substrate/client/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/rpc/src/lib.rs b/substrate/client/consensus/babe/rpc/src/lib.rs index a3e811baecffd6de1b9f94952be40d00d8b61de3..338d71a432565a8f9af2cf0ee7562278fc9f6b2d 100644 --- a/substrate/client/consensus/babe/rpc/src/lib.rs +++ b/substrate/client/consensus/babe/rpc/src/lib.rs @@ -25,12 +25,13 @@ use jsonrpsee::{ core::async_trait, proc_macros::rpc, types::{ErrorObject, ErrorObjectOwned}, + Extensions, }; use serde::{Deserialize, Serialize}; use sc_consensus_babe::{authorship, BabeWorkerHandle}; use sc_consensus_epochs::Epoch as EpochT; -use sc_rpc_api::{DenyUnsafe, UnsafeRpcError}; +use sc_rpc_api::{check_if_safe, UnsafeRpcError}; use sp_api::ProvideRuntimeApi; use sp_application_crypto::AppCrypto; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; @@ -47,7 +48,7 @@ const BABE_ERROR: i32 = 9000; pub trait BabeApi { /// Returns data about which slots (primary or secondary) can be claimed in the current epoch /// with the keys in the keystore. - #[method(name = "babe_epochAuthorship")] + #[method(name = "babe_epochAuthorship", with_extensions)] async fn epoch_authorship(&self) -> Result, Error>; } @@ -61,8 +62,6 @@ pub struct Babe { keystore: KeystorePtr, /// The SelectChain strategy select_chain: SC, - /// Whether to deny unsafe calls - deny_unsafe: DenyUnsafe, } impl Babe { @@ -72,9 +71,8 @@ impl Babe { babe_worker_handle: BabeWorkerHandle, keystore: KeystorePtr, select_chain: SC, - deny_unsafe: DenyUnsafe, ) -> Self { - Self { client, babe_worker_handle, keystore, select_chain, deny_unsafe } + Self { client, babe_worker_handle, keystore, select_chain } } } @@ -89,8 +87,11 @@ where C::Api: BabeRuntimeApi, SC: SelectChain + Clone + 'static, { - async fn epoch_authorship(&self) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + async fn epoch_authorship( + &self, + ext: &Extensions, + ) -> Result, Error> { + check_if_safe(ext)?; let best_header = self.select_chain.best_chain().map_err(Error::SelectChain).await?; @@ -193,6 +194,7 @@ impl From for ErrorObjectOwned { mod tests { use super::*; use sc_consensus_babe::ImportQueueParams; + use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::{OffchainTransactionPoolFactory, RejectAllTxPool}; use sp_consensus_babe::inherents::InherentDataProvider; use sp_core::{crypto::key_types::BABE, testing::TaskExecutor}; @@ -211,9 +213,8 @@ mod tests { keystore.into() } - fn test_babe_rpc_module( - deny_unsafe: DenyUnsafe, - ) -> Babe> { + fn test_babe_rpc_module() -> Babe> + { let builder = TestClientBuilder::new(); let (client, longest_chain) = builder.build_with_longest_chain(); let client = Arc::new(client); @@ -248,29 +249,31 @@ mod tests { }) .unwrap(); - Babe::new(client.clone(), babe_worker_handle, keystore, longest_chain, deny_unsafe) + Babe::new(client.clone(), babe_worker_handle, keystore, longest_chain) } #[tokio::test] async fn epoch_authorship_works() { - let babe_rpc = test_babe_rpc_module(DenyUnsafe::No); - let api = babe_rpc.into_rpc(); + let babe_rpc = test_babe_rpc_module(); + let mut api = babe_rpc.into_rpc(); + api.extensions_mut().insert(DenyUnsafe::No); - let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#; + let request = r#"{"jsonrpc":"2.0","id":1,"method":"babe_epochAuthorship","params":[]}"#; let (response, _) = api.raw_json_request(request, 1).await.unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[],"secondary_vrf":[1,2,4]}},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[],"secondary_vrf":[1,2,4]}}}"#; assert_eq!(response, expected); } #[tokio::test] async fn epoch_authorship_is_unsafe() { - let babe_rpc = test_babe_rpc_module(DenyUnsafe::Yes); - let api = babe_rpc.into_rpc(); + let babe_rpc = test_babe_rpc_module(); + let mut api = babe_rpc.into_rpc(); + api.extensions_mut().insert(DenyUnsafe::Yes); let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params":[],"id":1}"#; let (response, _) = api.raw_json_request(request, 1).await.unwrap(); - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"RPC call is unsafe to be called externally"}}"#; assert_eq!(response, expected); } diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index 0c1eb88758644c0d661c25c4feb670c6054781f4..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. @@ -1342,7 +1344,7 @@ where // This function makes multiple transactions to the DB. If one of them fails we may // end up in an inconsistent state and have to resync. async fn import_state( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -1405,7 +1407,7 @@ where type Error = ConsensusError; async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -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/babe/src/tests.rs b/substrate/client/consensus/babe/src/tests.rs index 6f805188b9a42d806f45c01715f1a088770c3bfe..5c2e0eae959c1584e01176ae683364a5802abcd3 100644 --- a/substrate/client/consensus/babe/src/tests.rs +++ b/substrate/client/consensus/babe/src/tests.rs @@ -150,7 +150,7 @@ where type Error = BI::Error; async fn import_block( - &mut self, + &self, block: BlockImportParams, ) -> Result { Ok(self.0.import_block(block).await.expect("importing block failed")) diff --git a/substrate/client/consensus/beefy/README.md b/substrate/client/consensus/beefy/README.md index a7956cfcd42efab055ea7cc5d0efc14f54ea20ba..cb9a9267f77ef8bad83f4b8d3d215e1467abc3c1 100644 --- a/substrate/client/consensus/beefy/README.md +++ b/substrate/client/consensus/beefy/README.md @@ -159,7 +159,7 @@ ambiguity despite using block number instead of a hash. A collection of **votes* a Commitment and a collection of signatures is going to be called **Signed Commitment**. A valid (see later for the rules) Signed Commitment is also called a **BEEFY Justification** or **BEEFY Finality Proof**. For more details on the actual data structures please see -[BEEFY primitives definitions](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/primitives/beefy/src). +[BEEFY primitives definitions](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/primitives/consensus/beefy/src). A **round** is an attempt by BEEFY validators to produce a BEEFY Justification. **Round number** is simply defined as a block number the validators are voting for, or to be more precise, the diff --git a/substrate/client/consensus/beefy/rpc/src/lib.rs b/substrate/client/consensus/beefy/rpc/src/lib.rs index 83477d223dd2d1cc043f8d63ef0a6f3057b87436..ab58f6866275853d371ec71f9237a97244e2d529 100644 --- a/substrate/client/consensus/beefy/rpc/src/lib.rs +++ b/substrate/client/consensus/beefy/rpc/src/lib.rs @@ -201,7 +201,7 @@ mod tests { async fn uninitialized_rpc_handler() { let (rpc, _) = setup_io_handler(); let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#; - let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#; + let expected_response = r#"{"jsonrpc":"2.0","id":1,"error":{"code":1,"message":"BEEFY RPC endpoint not ready"}}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); assert_eq!(expected_response, response); @@ -220,13 +220,13 @@ mod tests { let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#; let expected = "{\ \"jsonrpc\":\"2.0\",\ - \"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\",\ - \"id\":1\ + \"id\":1,\ + \"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\"\ }"; - let not_ready = "{\ + let not_ready: &str = "{\ \"jsonrpc\":\"2.0\",\ - \"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"},\ - \"id\":1\ + \"id\":1,\ + \"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"}\ }"; let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); @@ -262,7 +262,7 @@ mod tests { ) .await .unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"result":false}"#; assert_eq!(response, expected); } diff --git a/substrate/client/consensus/beefy/src/import.rs b/substrate/client/consensus/beefy/src/import.rs index 8480268529338fe09333719a2e26141e37c792ea..17a4a586663614ad7136b02beb8a240df297bf0b 100644 --- a/substrate/client/consensus/beefy/src/import.rs +++ b/substrate/client/consensus/beefy/src/import.rs @@ -132,7 +132,7 @@ where type Error = ConsensusError; async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index 4b1dd447320bffdc5736a0a8983c47c68295904a..afa6191d8bfec42f6d3b0afdf1bde2678fd5ca97 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -790,7 +790,7 @@ async fn beefy_importing_justifications() { let client = net.peer(0).client().clone(); let full_client = client.as_client(); - let (mut block_import, _, peer_data) = net.make_block_import(client.clone()); + let (block_import, _, peer_data) = net.make_block_import(client.clone()); let PeerData { beefy_voter_links, .. } = peer_data; let justif_stream = beefy_voter_links.lock().take().unwrap().from_block_import_justif_stream; let mut justif_recv = justif_stream.subscribe(100_000); diff --git a/substrate/client/consensus/common/src/block_import.rs b/substrate/client/consensus/common/src/block_import.rs index c5adbb5a5fca0634b1cdb038c569a1de312cf859..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, } } @@ -310,10 +313,7 @@ pub trait BlockImport { async fn check_block(&self, block: BlockCheckParams) -> Result; /// Import a block. - async fn import_block( - &mut self, - block: BlockImportParams, - ) -> Result; + async fn import_block(&self, block: BlockImportParams) -> Result; } #[async_trait::async_trait] @@ -326,10 +326,7 @@ impl BlockImport for crate::import_queue::BoxBlockImport { } /// Import a block. - async fn import_block( - &mut self, - block: BlockImportParams, - ) -> Result { + async fn import_block(&self, block: BlockImportParams) -> Result { (**self).import_block(block).await } } @@ -346,10 +343,7 @@ where (&**self).check_block(block).await } - async fn import_block( - &mut self, - block: BlockImportParams, - ) -> Result { + async fn import_block(&self, block: BlockImportParams) -> Result { (&**self).import_block(block).await } } diff --git a/substrate/client/consensus/common/src/import_queue.rs b/substrate/client/consensus/common/src/import_queue.rs index 35fc8ad4a402e72d9f6c8d110a468bea5f3c5195..1baa67398a49c3461a2d4f85d9c23de3191e9c22 100644 --- a/substrate/client/consensus/common/src/import_queue.rs +++ b/substrate/client/consensus/common/src/import_queue.rs @@ -225,7 +225,7 @@ pub async fn import_single_block>( import_handle: &mut impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, - verifier: &mut V, + verifier: &V, ) -> BlockImportResult { match verify_single_block_metered(import_handle, block_origin, block, verifier, None).await? { SingleBlockVerificationOutcome::Imported(import_status) => Ok(import_status), @@ -295,7 +295,7 @@ pub(crate) async fn verify_single_block_metered>( import_handle: &impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, - verifier: &mut V, + verifier: &V, metrics: Option<&Metrics>, ) -> Result, BlockImportError> { let peer = block.origin; diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 05f2b252796146f62ba7c258b208fa4c2eb4a3d1..7b371145e2e7df871d8c959bdc04fd405451ff39 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -222,7 +222,7 @@ mod worker_messages { /// Returns when `block_import` ended. async fn block_import_process( mut block_import: BoxBlockImport, - mut verifier: impl Verifier, + verifier: impl Verifier, mut result_sender: BufferedLinkSender, mut block_import_receiver: TracingUnboundedReceiver>, metrics: Option, @@ -241,8 +241,7 @@ async fn block_import_process( }; let res = - import_many_blocks(&mut block_import, origin, blocks, &mut verifier, metrics.clone()) - .await; + import_many_blocks(&mut block_import, origin, blocks, &verifier, metrics.clone()).await; result_sender.blocks_processed(res.imported, res.block_count, res.results); } @@ -388,7 +387,7 @@ async fn import_many_blocks>( import_handle: &mut BoxBlockImport, blocks_origin: BlockOrigin, blocks: Vec>, - verifier: &mut V, + verifier: &V, metrics: Option, ) -> ImportManyBlocksResult { let count = blocks.len(); @@ -526,7 +525,7 @@ mod tests { } async fn import_block( - &mut self, + &self, _block: BlockImportParams, ) -> Result { Ok(ImportResult::imported(true)) diff --git a/substrate/client/consensus/grandpa/rpc/src/lib.rs b/substrate/client/consensus/grandpa/rpc/src/lib.rs index a41b142990899afaa71a16c78c8c696d7b96d46b..99f98b81261a2fd34a7f8495c3c5cc859c91b881 100644 --- a/substrate/client/consensus/grandpa/rpc/src/lib.rs +++ b/substrate/client/consensus/grandpa/rpc/src/lib.rs @@ -275,7 +275,7 @@ mod tests { #[tokio::test] async fn uninitialized_rpc_handler() { let (rpc, _) = setup_io_handler(EmptyVoterState); - let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"GRANDPA RPC endpoint not ready"},"id":0}"#.to_string(); + let expected_response = r#"{"jsonrpc":"2.0","id":0,"error":{"code":1,"message":"GRANDPA RPC endpoint not ready"}}"#.to_string(); let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); @@ -285,7 +285,7 @@ mod tests { #[tokio::test] async fn working_rpc_handler() { let (rpc, _) = setup_io_handler(TestVoterState); - let expected_response = "{\"jsonrpc\":\"2.0\",\"result\":{\ + let expected_response = "{\"jsonrpc\":\"2.0\",\"id\":0,\"result\":{\ \"setId\":1,\ \"best\":{\ \"round\":2,\"totalWeight\":100,\"thresholdWeight\":67,\ @@ -297,7 +297,7 @@ mod tests { \"prevotes\":{\"currentWeight\":100,\"missing\":[]},\ \"precommits\":{\"currentWeight\":100,\"missing\":[]}\ }]\ - },\"id\":0}".to_string(); + }}".to_string(); let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); @@ -321,7 +321,7 @@ mod tests { ) .await .unwrap(); - let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","id":1,"result":false}"#; assert_eq!(response, expected); } diff --git a/substrate/client/consensus/grandpa/src/finality_proof.rs b/substrate/client/consensus/grandpa/src/finality_proof.rs index af965f2e4ae64737bc3626e51f8245328af12d71..8a47d121e86932cdc9ca86bfa3f4d42c1bd9c6e7 100644 --- a/substrate/client/consensus/grandpa/src/finality_proof.rs +++ b/substrate/client/consensus/grandpa/src/finality_proof.rs @@ -319,7 +319,7 @@ mod tests { ) -> (Arc, Arc, Vec) { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let mut blocks = Vec::new(); for _ in 0..number_of_blocks { diff --git a/substrate/client/consensus/grandpa/src/import.rs b/substrate/client/consensus/grandpa/src/import.rs index 8b7b02f180ecd582063c5b02e1f4217c0a507e13..5cec5204c7396774d210c5b3152a46a95fc8dc4a 100644 --- a/substrate/client/consensus/grandpa/src/import.rs +++ b/substrate/client/consensus/grandpa/src/import.rs @@ -20,6 +20,7 @@ use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use codec::Decode; use log::debug; +use parking_lot::Mutex; use sc_client_api::{backend::Backend, utils::is_descendent_of}; use sc_consensus::{ @@ -62,7 +63,8 @@ pub struct GrandpaBlockImport { select_chain: SC, authority_set: SharedAuthoritySet>, send_voter_commands: TracingUnboundedSender>>, - authority_set_hard_forks: HashMap>>, + authority_set_hard_forks: + Mutex>>>, justification_sender: GrandpaJustificationSender, telemetry: Option, _phantom: PhantomData, @@ -78,7 +80,7 @@ impl Clone select_chain: self.select_chain.clone(), authority_set: self.authority_set.clone(), send_voter_commands: self.send_voter_commands.clone(), - authority_set_hard_forks: self.authority_set_hard_forks.clone(), + authority_set_hard_forks: Mutex::new(self.authority_set_hard_forks.lock().clone()), justification_sender: self.justification_sender.clone(), telemetry: self.telemetry.clone(), _phantom: PhantomData, @@ -242,7 +244,7 @@ where hash: Block::Hash, ) -> Option>> { // check for forced authority set hard forks - if let Some(change) = self.authority_set_hard_forks.get(&hash) { + if let Some(change) = self.authority_set_hard_forks.lock().get(&hash) { return Some(change.clone()) } @@ -461,7 +463,7 @@ where /// Import whole new state and reset authority set. async fn import_state( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -474,7 +476,7 @@ where // We've just imported a new state. We trust the sync module has verified // finality proofs and that the state is correct and final. // So we can read the authority list and set id from the state. - self.authority_set_hard_forks.clear(); + self.authority_set_hard_forks.lock().clear(); let authorities = self .inner .runtime_api() @@ -523,7 +525,7 @@ where type Error = ConsensusError; async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let hash = block.post_hash(); @@ -750,7 +752,7 @@ impl GrandpaBlockImport, justification: Justification, diff --git a/substrate/client/consensus/grandpa/src/tests.rs b/substrate/client/consensus/grandpa/src/tests.rs index 2aa1b5f6ee1b268e5c80ff557e203964f496debf..9cca28a395995c54c5ae7b416b203d366a7202b5 100644 --- a/substrate/client/consensus/grandpa/src/tests.rs +++ b/substrate/client/consensus/grandpa/src/tests.rs @@ -906,7 +906,7 @@ async fn allows_reimporting_change_blocks() { let mut net = GrandpaTestNet::new(api.clone(), 3, 0); let client = net.peer(0).client().clone(); - let (mut block_import, ..) = net.make_block_import(client.clone()); + let (block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); let mut builder = BlockBuilderBuilder::new(&*full_client) @@ -954,7 +954,7 @@ async fn test_bad_justification() { let mut net = GrandpaTestNet::new(api.clone(), 3, 0); let client = net.peer(0).client().clone(); - let (mut block_import, ..) = net.make_block_import(client.clone()); + let (block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); let mut builder = BlockBuilderBuilder::new(&*full_client) @@ -2083,7 +2083,7 @@ async fn imports_justification_for_regular_blocks_on_import() { let mut net = GrandpaTestNet::new(api.clone(), 1, 0); let client = net.peer(0).client().clone(); - let (mut block_import, ..) = net.make_block_import(client.clone()); + let (block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); // create a new block (without importing it) @@ -2122,7 +2122,7 @@ async fn imports_justification_for_regular_blocks_on_import() { GrandpaJustification::from_commit(&full_client, round, commit).unwrap() }; - let mut generate_and_import_block_with_justification = |parent| { + let generate_and_import_block_with_justification = |parent| { // we import the block with justification attached let block = generate_block(parent); let block_hash = block.hash(); diff --git a/substrate/client/consensus/grandpa/src/voting_rule.rs b/substrate/client/consensus/grandpa/src/voting_rule.rs index c37596d20f68625b21f443e16db3c03fc08357da..c1d3cd2fbd6ab6b5f8d45e555a43aeb289791a80 100644 --- a/substrate/client/consensus/grandpa/src/voting_rule.rs +++ b/substrate/client/consensus/grandpa/src/voting_rule.rs @@ -367,7 +367,7 @@ mod tests { // where each subtracts 50 blocks from the current target let rule = VotingRulesBuilder::new().add(Subtract(50)).add(Subtract(50)).build(); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let mut hashes = Vec::with_capacity(200); for _ in 0..200 { @@ -416,7 +416,7 @@ mod tests { fn before_best_by_has_cutoff_at_base() { let rule = BeforeBestBlockBy(2); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let n = 5; let mut hashes = Vec::with_capacity(n); diff --git a/substrate/client/consensus/grandpa/src/warp_proof.rs b/substrate/client/consensus/grandpa/src/warp_proof.rs index c836ab09fd5d5ce1f0885e02da5420407853548b..a79581b1e9f13092b917b64a443af31aa7cd8a27 100644 --- a/substrate/client/consensus/grandpa/src/warp_proof.rs +++ b/substrate/client/consensus/grandpa/src/warp_proof.rs @@ -338,7 +338,7 @@ mod tests { let mut rng = rand::rngs::StdRng::from_seed([0; 32]); let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let available_authorities = Ed25519Keyring::iter().collect::>(); let genesis_authorities = vec![(Ed25519Keyring::Alice.public().into(), 1)]; diff --git a/substrate/client/consensus/pow/src/lib.rs b/substrate/client/consensus/pow/src/lib.rs index 50e9533abb36ab24e5d4942d154a378f84c4beec..cd7da128549f0d8ded3b073568e735fae970028f 100644 --- a/substrate/client/consensus/pow/src/lib.rs +++ b/substrate/client/consensus/pow/src/lib.rs @@ -317,7 +317,7 @@ where } async fn import_block( - &mut self, + &self, mut block: BlockImportParams, ) -> Result { let best_header = self diff --git a/substrate/client/consensus/pow/src/worker.rs b/substrate/client/consensus/pow/src/worker.rs index 9e9c4fc137d86dd3945d5cbc3aa44b43558a757d..73400136483a7cf8e3f03aa75a64e1a3aaf8fba2 100644 --- a/substrate/client/consensus/pow/src/worker.rs +++ b/substrate/client/consensus/pow/src/worker.rs @@ -192,7 +192,7 @@ where import_block.insert_intermediate(INTERMEDIATE_KEY, intermediate); let header = import_block.post_header(); - let mut block_import = self.block_import.lock(); + let block_import = self.block_import.lock(); match block_import.import_block(import_block).await { Ok(res) => { diff --git a/substrate/client/consensus/slots/src/lib.rs b/substrate/client/consensus/slots/src/lib.rs index 7cdf90877dffad20f91a1d1f846648cc22dfe4ea..06e0756fc968910cf1333c13303c2fa65479df71 100644 --- a/substrate/client/consensus/slots/src/lib.rs +++ b/substrate/client/consensus/slots/src/lib.rs @@ -517,16 +517,15 @@ pub async fn start_slot_worker( CIDP: CreateInherentDataProviders + Send + 'static, CIDP::InherentDataProviders: InherentDataProviderExt + Send, { - let mut slots = Slots::new(slot_duration.as_duration(), create_inherent_data_providers, client); + let mut slots = Slots::new( + slot_duration.as_duration(), + create_inherent_data_providers, + client, + sync_oracle, + ); loop { let slot_info = slots.next_slot().await; - - if sync_oracle.is_major_syncing() { - debug!(target: LOG_TARGET, "Skipping proposal slot due to sync."); - continue - } - let _ = worker.on_slot(slot_info).await; } } diff --git a/substrate/client/consensus/slots/src/slots.rs b/substrate/client/consensus/slots/src/slots.rs index 203764310601af5f6225582a1e775a6beeb4d1dc..c0b412e8ad5b08402d02505a2594026ef179e483 100644 --- a/substrate/client/consensus/slots/src/slots.rs +++ b/substrate/client/consensus/slots/src/slots.rs @@ -21,7 +21,7 @@ //! This is used instead of `futures_timer::Interval` because it was unreliable. use super::{InherentDataProviderExt, Slot, LOG_TARGET}; -use sp_consensus::SelectChain; +use sp_consensus::{SelectChain, SyncOracle}; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; @@ -87,21 +87,23 @@ impl SlotInfo { } /// A stream that returns every time there is a new slot. -pub(crate) struct Slots { +pub(crate) struct Slots { last_slot: Slot, slot_duration: Duration, until_next_slot: Option, create_inherent_data_providers: IDP, select_chain: SC, + sync_oracle: SO, _phantom: std::marker::PhantomData, } -impl Slots { +impl Slots { /// Create a new `Slots` stream. pub fn new( slot_duration: Duration, create_inherent_data_providers: IDP, select_chain: SC, + sync_oracle: SO, ) -> Self { Slots { last_slot: 0.into(), @@ -109,17 +111,19 @@ impl Slots { until_next_slot: None, create_inherent_data_providers, select_chain, + sync_oracle, _phantom: Default::default(), } } } -impl Slots +impl Slots where Block: BlockT, SC: SelectChain, IDP: CreateInherentDataProviders + 'static, IDP::InherentDataProviders: crate::InherentDataProviderExt, + SO: SyncOracle, { /// Returns a future that fires when the next slot starts. pub async fn next_slot(&mut self) -> SlotInfo { @@ -138,6 +142,11 @@ where let wait_dur = time_until_next_slot(self.slot_duration); self.until_next_slot = Some(Delay::new(wait_dur)); + if self.sync_oracle.is_major_syncing() { + log::debug!(target: LOG_TARGET, "Skipping slot: major sync is in progress."); + continue; + } + let chain_head = match self.select_chain.best_chain().await { Ok(x) => x, Err(e) => { 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/informant/src/display.rs b/substrate/client/informant/src/display.rs index 655bf21c71154a457320a1e7eda2783e70d686f0..2decd76747827bdeb702e19fbf1d2e0cceca28e9 100644 --- a/substrate/client/informant/src/display.rs +++ b/substrate/client/informant/src/display.rs @@ -65,11 +65,11 @@ impl InformantDisplay { info: &ClientInfo, net_status: NetworkStatus, sync_status: SyncStatus, + num_connected_peers: usize, ) { let best_number = info.chain.best_number; let best_hash = info.chain.best_hash; let finalized_number = info.chain.finalized_number; - let num_connected_peers = sync_status.num_connected_peers; let speed = speed::(best_number, self.last_number, self.last_update); let total_bytes_inbound = net_status.total_bytes_inbound; let total_bytes_outbound = net_status.total_bytes_outbound; @@ -101,17 +101,9 @@ impl InformantDisplay { _, Some(WarpSyncProgress { phase: WarpSyncPhase::DownloadingBlocks(n), .. }), ) if !sync_status.is_major_syncing() => ("⏩", "Block history".into(), format!(", #{}", n)), - ( - _, - _, - Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingTargetBlock, .. }), - ) => ("⏩", "Waiting for pending target block".into(), "".into()), // Handle all phases besides the two phases we already handle above. (_, _, Some(warp)) - if !matches!( - warp.phase, - WarpSyncPhase::AwaitingTargetBlock | WarpSyncPhase::DownloadingBlocks(_) - ) => + if !matches!(warp.phase, WarpSyncPhase::DownloadingBlocks(_)) => ( "⏩", "Warping".into(), diff --git a/substrate/client/informant/src/lib.rs b/substrate/client/informant/src/lib.rs index d44364539a291bb2bd1d149adaff8af9ce136735..0b0e13dc08bbbdad372a29b497578a43b09925d7 100644 --- a/substrate/client/informant/src/lib.rs +++ b/substrate/client/informant/src/lib.rs @@ -24,7 +24,7 @@ use futures_timer::Delay; use log::{debug, info, trace}; use sc_client_api::{BlockchainEvents, UsageProvider}; use sc_network::NetworkStatusProvider; -use sc_network_sync::SyncStatusProvider; +use sc_network_sync::{SyncStatusProvider, SyncingService}; use sp_blockchain::HeaderMetadata; use sp_runtime::traits::{Block as BlockT, Header}; use std::{collections::VecDeque, fmt::Display, sync::Arc, time::Duration}; @@ -37,10 +37,9 @@ fn interval(duration: Duration) -> impl Stream + Unpin { } /// Builds the informant and returns a `Future` that drives the informant. -pub async fn build(client: Arc, network: N, syncing: S) +pub async fn build(client: Arc, network: N, syncing: Arc>) where N: NetworkStatusProvider, - S: SyncStatusProvider, C: UsageProvider + HeaderMetadata + BlockchainEvents, >::Error: Display, { @@ -52,13 +51,14 @@ where .filter_map(|_| async { let net_status = network.status().await; let sync_status = syncing.status().await; + let num_connected_peers = syncing.num_connected_peers(); - match (net_status.ok(), sync_status.ok()) { - (Some(net), Some(sync)) => Some((net, sync)), + match (net_status, sync_status) { + (Ok(net), Ok(sync)) => Some((net, sync, num_connected_peers)), _ => None, } }) - .for_each(move |(net_status, sync_status)| { + .for_each(move |(net_status, sync_status, num_connected_peers)| { let info = client_1.usage_info(); if let Some(ref usage) = info.usage { trace!(target: "usage", "Usage statistics: {}", usage); @@ -68,7 +68,7 @@ where "Usage statistics not displayed as backend does not provide it", ) } - display.display(&info, net_status, sync_status); + display.display(&info, net_status, sync_status, num_connected_peers); future::ready(()) }); diff --git a/substrate/client/merkle-mountain-range/src/test_utils.rs b/substrate/client/merkle-mountain-range/src/test_utils.rs index fcf9fa25b593c86eb780d8b563e6ecc55ecb50a7..3b0506ef55d329866fba4af71fb6351caa54c4b2 100644 --- a/substrate/client/merkle-mountain-range/src/test_utils.rs +++ b/substrate/client/merkle-mountain-range/src/test_utils.rs @@ -122,7 +122,7 @@ impl MockClient { name: &[u8], maybe_leaf_idx: Option, ) -> MmrBlock { - let mut client = self.client.lock(); + let client = self.client.lock(); let hash = client.expect_block_hash_from_id(&at).unwrap(); let mut block_builder = BlockBuilderBuilder::new(&*client) diff --git a/substrate/client/network/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/src/bitswap/mod.rs b/substrate/client/network/src/bitswap/mod.rs index 22f1973adcb2ece0b2fe751ef9c5e94b1bb1fd85..1e20572eeeb11e90f60461ad04c8f24f5f0f7909 100644 --- a/substrate/client/network/src/bitswap/mod.rs +++ b/substrate/client/network/src/bitswap/mod.rs @@ -468,7 +468,7 @@ mod tests { #[tokio::test] async fn transaction_found() { - let mut client = TestClientBuilder::with_tx_storage(u32::MAX).build(); + let client = TestClientBuilder::with_tx_storage(u32::MAX).build(); let mut block_builder = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) .with_parent_block_number(0) diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index 62d5f0fb6f06a864f2d4c9f134c33ba3686f67fd..bf2005df34d75ecafb7522aed789acd5102db1ff 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -243,11 +243,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 { 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/peerstore.rs b/substrate/client/network/src/litep2p/peerstore.rs index 55e912c31f184b59ba682a06b552da0d8c425451..dd8d92bcee642d3a580e3c181ddafdb0d832916a 100644 --- a/substrate/client/network/src/litep2p/peerstore.rs +++ b/substrate/client/network/src/litep2p/peerstore.rs @@ -192,38 +192,63 @@ impl PeerStoreProvider for PeerstoreHandle { } /// Adjust peer reputation. - fn report_peer(&self, peer: PeerId, reputation_change: ReputationChange) { + fn report_peer(&self, peer_id: PeerId, change: ReputationChange) { let mut lock = self.0.lock(); + let peer_info = lock.peers.entry(peer_id).or_default(); + let was_banned = peer_info.is_banned(); + peer_info.add_reputation(change.value); + let peer_reputation = peer_info.reputation; + + log::trace!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_reputation, + change.reason, + ); - log::trace!(target: LOG_TARGET, "report peer {reputation_change:?}"); - - match lock.peers.get_mut(&peer) { - Some(info) => { - info.add_reputation(reputation_change.value); - }, - None => { - lock.peers.insert( - peer, - PeerInfo { - reputation: reputation_change.value, - last_updated: Instant::now(), - role: None, - }, + if !peer_info.is_banned() { + if was_banned { + log::info!( + target: LOG_TARGET, + "Peer {} is now unbanned: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_reputation, + change.reason, ); - }, + } + return; } - if lock - .peers - .get(&peer) - .expect("peer exist since it was just modified; qed") - .is_banned() - { - log::warn!(target: LOG_TARGET, "{peer:?} banned, disconnecting, reason: {}", reputation_change.reason); - - for sender in &lock.protocols { - sender.disconnect_peer(peer); - } + // Peer is currently banned, disconnect it from all protocols. + lock.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id.into())); + + // The peer is banned for the first time. + if !was_banned { + log::warn!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}. Banned, disconnecting.", + peer_id, + change.value, + peer_reputation, + change.reason, + ); + return; + } + + // The peer was already banned and it got another negative report. + // This may happen during a batch report. + if change.value < 0 { + log::debug!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}. Misbehaved during the ban threshold.", + peer_id, + change.value, + peer_reputation, + change.reason, + ); } } diff --git a/substrate/client/network/src/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/peer_store.rs b/substrate/client/network/src/peer_store.rs index 63e98a2fb4bf6371fc9c4a9d1fced60870d71a4f..0e57791542e1628b872fd47ae771b5c4dd1141cd 100644 --- a/substrate/client/network/src/peer_store.rs +++ b/substrate/client/network/src/peer_store.rs @@ -260,11 +260,37 @@ impl PeerStoreInner { fn report_peer(&mut self, peer_id: PeerId, change: ReputationChange) { let peer_info = self.peers.entry(peer_id).or_default(); + let was_banned = peer_info.is_banned(); peer_info.add_reputation(change.value); - if peer_info.is_banned() { - self.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id.into())); + log::trace!( + target: LOG_TARGET, + "Report {}: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_info.reputation, + change.reason, + ); + + if !peer_info.is_banned() { + if was_banned { + log::info!( + target: LOG_TARGET, + "Peer {} is now unbanned: {:+} to {}. Reason: {}.", + peer_id, + change.value, + peer_info.reputation, + change.reason, + ); + } + return; + } + + // Peer is currently banned, disconnect it from all protocols. + self.protocols.iter().for_each(|handle| handle.disconnect_peer(peer_id.into())); + // The peer is banned for the first time. + if !was_banned { log::warn!( target: LOG_TARGET, "Report {}: {:+} to {}. Reason: {}. Banned, disconnecting.", @@ -273,10 +299,15 @@ impl PeerStoreInner { peer_info.reputation, change.reason, ); - } else { - log::trace!( + return; + } + + // The peer was already banned and it got another negative report. + // This may happen during a batch report. + if change.value < 0 { + log::debug!( target: LOG_TARGET, - "Report {}: {:+} to {}. Reason: {}.", + "Report {}: {:+} to {}. Reason: {}. Misbehaved during the ban threshold.", peer_id, change.value, peer_info.reputation, diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index ee7576c22f16b98355b206105eedbcf23ab770a4..86c1a7abf7446ea11fbd09f44ca58e1f7aa1d6d5 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -32,8 +32,8 @@ use crate::{ syncing_service::{SyncingService, ToServiceCommand}, }, strategy::{ - warp::{EncodedProof, WarpProofRequest, WarpSyncParams}, - StrategyKey, SyncingAction, SyncingConfig, SyncingStrategy, + warp::{EncodedProof, WarpProofRequest, WarpSyncConfig}, + PolkadotSyncingStrategy, StrategyKey, SyncingAction, SyncingConfig, SyncingStrategy, }, types::{ BadPeer, ExtendedPeerInfo, OpaqueStateRequest, OpaqueStateResponse, PeerRequest, SyncEvent, @@ -42,11 +42,7 @@ use crate::{ }; use codec::{Decode, DecodeAll, Encode}; -use futures::{ - channel::oneshot, - future::{BoxFuture, Fuse}, - FutureExt, StreamExt, -}; +use futures::{channel::oneshot, FutureExt, StreamExt}; use libp2p::request_response::OutboundFailure; use log::{debug, error, trace, warn}; use prometheus_endpoint::{ @@ -193,7 +189,7 @@ pub struct Peer { pub struct SyncingEngine { /// Syncing strategy. - strategy: SyncingStrategy, + strategy: PolkadotSyncingStrategy, /// Blockchain client. client: Arc, @@ -257,10 +253,6 @@ pub struct SyncingEngine { /// The `PeerId`'s of all boot nodes. boot_node_ids: HashSet, - /// A channel to get target block header if we skip over proofs downloading during warp sync. - warp_sync_target_block_header_rx_fused: - Fuse>>, - /// Protocol name used for block announcements block_announce_protocol_name: ProtocolName, @@ -309,7 +301,7 @@ where protocol_id: ProtocolId, fork_id: &Option, block_announce_validator: Box + Send>, - warp_sync_params: Option>, + warp_sync_config: Option>, network_service: service::network::NetworkServiceHandle, import_queue: Box>, block_downloader: Arc>, @@ -326,8 +318,7 @@ where if net_config.network_config.max_blocks_per_request > MAX_BLOCKS_IN_RESPONSE as u32 { log::info!( target: LOG_TARGET, - "clamping maximum blocks per request to {}", - MAX_BLOCKS_IN_RESPONSE, + "clamping maximum blocks per request to {MAX_BLOCKS_IN_RESPONSE}", ); MAX_BLOCKS_IN_RESPONSE as u32 } else { @@ -348,12 +339,7 @@ where imp_p.insert(reserved.peer_id); } for config in net_config.notification_protocols() { - let peer_ids = config - .set_config() - .reserved_nodes - .iter() - .map(|info| info.peer_id) - .collect::>(); + let peer_ids = config.set_config().reserved_nodes.iter().map(|info| info.peer_id); imp_p.extend(peer_ids); } @@ -387,48 +373,29 @@ where total.saturating_sub(net_config.network_config.default_peers_set_num_full) as usize }; + let info = client.info(); + let (block_announce_config, notification_service) = Self::get_block_announce_proto_config::( protocol_id, fork_id, roles, - client.info().best_number, - client.info().best_hash, - client - .block_hash(Zero::zero()) - .ok() - .flatten() - .expect("Genesis block exists; qed"), + info.best_number, + info.best_hash, + info.genesis_hash, &net_config.network_config.default_peers_set, network_metrics, Arc::clone(&peer_store_handle), ); - // Split warp sync params into warp sync config and a channel to retrieve target block - // header. - let (warp_sync_config, warp_sync_target_block_header_rx) = - warp_sync_params.map_or((None, None), |params| { - let (config, target_block_rx) = params.split(); - (Some(config), target_block_rx) - }); - - // Make sure polling of the target block channel is a no-op if there is no block to - // retrieve. - let warp_sync_target_block_header_rx_fused = warp_sync_target_block_header_rx - .map_or(futures::future::pending().boxed().fuse(), |rx| rx.boxed().fuse()); - // Initialize syncing strategy. - let strategy = SyncingStrategy::new(syncing_config, client.clone(), warp_sync_config)?; + let strategy = + PolkadotSyncingStrategy::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)); let is_major_syncing = Arc::new(AtomicBool::new(false)); - let genesis_hash = client - .block_hash(0u32.into()) - .ok() - .flatten() - .expect("Genesis block exists; qed"); // `default_peers_set.in_peers` contains an unspecified amount of light peers so the number // of full inbound peers must be calculated from the total full peer count @@ -457,10 +424,9 @@ where num_connected: num_connected.clone(), is_major_syncing: is_major_syncing.clone(), service_rx, - genesis_hash, + genesis_hash: info.genesis_hash, important_peers, default_peers_set_no_slot_connected_peers: HashSet::new(), - warp_sync_target_block_header_rx_fused, boot_node_ids, default_peers_set_no_slot_peers, default_peers_set_num_full, @@ -493,15 +459,6 @@ where )) } - /// Report Prometheus metrics. - pub fn report_metrics(&self) { - if let Some(metrics) = &self.metrics { - let n = u64::try_from(self.peers.len()).unwrap_or(std::u64::MAX); - metrics.peers.set(n); - } - self.strategy.report_metrics(); - } - fn update_peer_info( &mut self, peer_id: &PeerId, @@ -565,7 +522,7 @@ where "Received block announce from disconnected peer {peer_id}", ); debug_assert!(false); - return + return; }, }; peer.known_blocks.insert(hash); @@ -590,17 +547,17 @@ where Ok(Some(header)) => header, Ok(None) => { log::warn!(target: LOG_TARGET, "Trying to announce unknown block: {hash}"); - return + return; }, Err(e) => { log::warn!(target: LOG_TARGET, "Error reading block header {hash}: {e}"); - return + return; }, }; // don't announce genesis block since it will be ignored if header.number().is_zero() { - return + return; } let is_best = self.client.info().best_hash == hash; @@ -628,24 +585,17 @@ where pub async fn run(mut self) { loop { tokio::select! { - _ = self.tick_timeout.tick() => self.perform_periodic_actions(), + _ = self.tick_timeout.tick() => { + // TODO: This tick should not be necessary, but + // `self.process_strategy_actions()` is not called in some cases otherwise and + // some tests fail because of this + }, command = self.service_rx.select_next_some() => self.process_service_command(command), notification_event = self.notification_service.next_event() => match notification_event { Some(event) => self.process_notification_event(event), None => return, }, - // TODO: setting of warp sync target block should be moved to the initialization of - // `SyncingEngine`, see https://github.com/paritytech/polkadot-sdk/issues/3537. - warp_target_block_header = &mut self.warp_sync_target_block_header_rx_fused => { - if let Err(_) = self.pass_warp_sync_target_block_header(warp_target_block_header) { - error!( - target: LOG_TARGET, - "Failed to set warp sync target block header, terminating `SyncingEngine`.", - ); - return - } - }, response_event = self.pending_responses.select_next_some() => self.process_response_event(response_event), validation_result = self.block_announce_validator.select_next_some() => @@ -653,7 +603,6 @@ where } // Update atomic variables - self.num_connected.store(self.peers.len(), Ordering::Relaxed); self.is_major_syncing.store(self.strategy.is_major_syncing(), Ordering::Relaxed); // Process actions requested by a syncing strategy. @@ -662,7 +611,7 @@ where target: LOG_TARGET, "Terminating `SyncingEngine` due to fatal error: {e:?}.", ); - return + return; } } } @@ -749,7 +698,7 @@ where number, ) }, - // Nothing to do, this is handled internally by `SyncingStrategy`. + // Nothing to do, this is handled internally by `PolkadotSyncingStrategy`. SyncingAction::Finished => {}, } } @@ -757,10 +706,6 @@ where Ok(()) } - fn perform_periodic_actions(&mut self) { - self.report_metrics(); - } - fn process_service_command(&mut self, command: ToServiceCommand) { match command { ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { @@ -803,25 +748,11 @@ where ); }, ToServiceCommand::Status(tx) => { - let mut status = self.strategy.status(); - status.num_connected_peers = self.peers.len() as u32; - let _ = tx.send(status); + let _ = tx.send(self.strategy.status()); }, ToServiceCommand::NumActivePeers(tx) => { let _ = tx.send(self.num_active_peers()); }, - ToServiceCommand::SyncState(tx) => { - let _ = tx.send(self.strategy.status()); - }, - ToServiceCommand::BestSeenBlock(tx) => { - let _ = tx.send(self.strategy.status().best_seen_block); - }, - ToServiceCommand::NumSyncPeers(tx) => { - let _ = tx.send(self.strategy.status().num_peers); - }, - ToServiceCommand::NumQueuedBlocks(tx) => { - let _ = tx.send(self.strategy.status().queued_blocks); - }, ToServiceCommand::NumDownloadedBlocks(tx) => { let _ = tx.send(self.strategy.num_downloaded_blocks()); }, @@ -829,11 +760,8 @@ where let _ = tx.send(self.strategy.num_sync_requests()); }, ToServiceCommand::PeersInfo(tx) => { - let peers_info = self - .peers - .iter() - .map(|(peer_id, peer)| (*peer_id, peer.info.clone())) - .collect(); + let peers_info = + self.peers.iter().map(|(peer_id, peer)| (*peer_id, peer.info)).collect(); let _ = tx.send(peers_info); }, ToServiceCommand::OnBlockFinalized(hash, header) => @@ -885,12 +813,12 @@ where target: LOG_TARGET, "received notification from {peer} who had been earlier refused by `SyncingEngine`", ); - return + return; } let Ok(announce) = BlockAnnounce::decode(&mut notification.as_ref()) else { log::warn!(target: LOG_TARGET, "failed to decode block announce"); - return + return; }; self.push_block_announce_validation(peer, announce); @@ -898,30 +826,18 @@ where } } - fn pass_warp_sync_target_block_header( - &mut self, - header: Result, - ) -> Result<(), ()> { - match header { - Ok(header) => self.strategy.set_warp_sync_target_block_header(header), - Err(err) => { - error!( - target: LOG_TARGET, - "Failed to get target block for warp sync. Error: {err:?}", - ); - Err(()) - }, - } - } - /// Called by peer when it is disconnecting. /// /// Returns a result if the handshake of this peer was indeed accepted. fn on_sync_peer_disconnected(&mut self, peer_id: PeerId) { let Some(info) = self.peers.remove(&peer_id) else { log::debug!(target: LOG_TARGET, "{peer_id} does not exist in `SyncingEngine`"); - return + return; }; + if let Some(metrics) = &self.metrics { + metrics.peers.dec(); + } + self.num_connected.fetch_sub(1, Ordering::AcqRel); if self.important_peers.contains(&peer_id) { log::warn!(target: LOG_TARGET, "Reserved peer {peer_id} disconnected"); @@ -990,7 +906,7 @@ where ); } - return Err(true) + return Err(true); } Ok(handshake) @@ -1025,7 +941,7 @@ where "Called `validate_connection()` with already connected peer {peer_id}", ); debug_assert!(false); - return Err(false) + return Err(false); } let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&peer_id); @@ -1038,7 +954,7 @@ where this_peer_reserved_slot { log::debug!(target: LOG_TARGET, "Too many full nodes, rejecting {peer_id}"); - return Err(false) + return Err(false); } // make sure to accept no more than `--in-peers` many full nodes @@ -1048,7 +964,7 @@ where self.num_in_peers == self.max_in_peers { log::debug!(target: LOG_TARGET, "All inbound slots have been consumed, rejecting {peer_id}"); - return Err(false) + return Err(false); } // make sure that all slots are not occupied by light peers @@ -1059,7 +975,7 @@ where (self.peers.len() - self.strategy.num_peers()) >= self.default_peers_set_num_light { log::debug!(target: LOG_TARGET, "Too many light nodes, rejecting {peer_id}"); - return Err(false) + return Err(false); } Ok(handshake) @@ -1097,7 +1013,12 @@ where log::debug!(target: LOG_TARGET, "Connected {peer_id}"); - self.peers.insert(peer_id, peer); + if self.peers.insert(peer_id, peer).is_none() { + if let Some(metrics) = &self.metrics { + metrics.peers.inc(); + } + self.num_connected.fetch_add(1, Ordering::AcqRel); + } self.peer_store_handle.set_peer_role(&peer_id, status.roles.into()); if self.default_peers_set_no_slot_peers.contains(&peer_id) { @@ -1116,7 +1037,7 @@ where if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send block request to unknown peer {peer_id}"); debug_assert!(false); - return + return; } let downloader = self.block_downloader.clone(); @@ -1138,7 +1059,7 @@ where if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send state request to unknown peer {peer_id}"); debug_assert!(false); - return + return; } let (tx, rx) = oneshot::channel(); @@ -1173,7 +1094,7 @@ where if !self.peers.contains_key(&peer_id) { trace!(target: LOG_TARGET, "Cannot send warp proof request to unknown peer {peer_id}"); debug_assert!(false); - return + return; } let (tx, rx) = oneshot::channel(); @@ -1236,7 +1157,7 @@ where peer_id, self.block_announce_protocol_name.clone(), ); - return + return; }, Err(BlockResponseError::ExtractionFailed(e)) => { debug!( @@ -1246,7 +1167,7 @@ where e ); self.network_service.report_peer(peer_id, rep::BAD_MESSAGE); - return + return; }, } }, @@ -1263,7 +1184,7 @@ where peer_id, self.block_announce_protocol_name.clone(), ); - return + return; }, }; diff --git a/substrate/client/network/sync/src/justification_requests.rs b/substrate/client/network/sync/src/justification_requests.rs index 2b50c85602d78bcbb04ce5ecb23882f81e184d81..f2d7488b2c35c83f9f9fe0af43c5a581b3624c27 100644 --- a/substrate/client/network/sync/src/justification_requests.rs +++ b/substrate/client/network/sync/src/justification_requests.rs @@ -21,12 +21,14 @@ //! that don't make sense after one of the forks is finalized). use crate::{ - request_metrics::Metrics, strategy::chain_sync::{PeerSync, PeerSyncState}, LOG_TARGET, }; use fork_tree::ForkTree; use log::{debug, trace, warn}; +use prometheus_endpoint::{ + prometheus::core::GenericGauge, register, GaugeVec, Opts, PrometheusError, Registry, U64, +}; use sc_network_types::PeerId; use sp_blockchain::Error as ClientError; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero}; @@ -41,6 +43,34 @@ const EXTRA_RETRY_WAIT: Duration = Duration::from_secs(10); /// Pending extra data request for the given block (hash and number). type ExtraRequest = (::Hash, NumberFor); +#[derive(Debug)] +struct Metrics { + pending: GenericGauge, + active: GenericGauge, + failed: GenericGauge, + importing: GenericGauge, +} + +impl Metrics { + fn register(registry: &Registry) -> Result { + let justifications = GaugeVec::::new( + Opts::new( + "substrate_sync_extra_justifications", + "Number of extra justifications requests", + ), + &["status"], + )?; + let justifications = register(justifications, registry)?; + + Ok(Self { + pending: justifications.with_label_values(&["pending"]), + active: justifications.with_label_values(&["active"]), + failed: justifications.with_label_values(&["failed"]), + importing: justifications.with_label_values(&["importing"]), + }) + } +} + /// Manages pending block extra data (e.g. justification) requests. /// /// Multiple extras may be requested for competing forks, or for the same branch @@ -62,10 +92,14 @@ pub(crate) struct ExtraRequests { importing_requests: HashSet>, /// the name of this type of extra request (useful for logging.) request_type_name: &'static str, + metrics: Option, } impl ExtraRequests { - pub(crate) fn new(request_type_name: &'static str) -> Self { + pub(crate) fn new( + request_type_name: &'static str, + metrics_registry: Option<&Registry>, + ) -> Self { Self { tree: ForkTree::new(), best_seen_finalized_number: Zero::zero(), @@ -74,6 +108,16 @@ impl ExtraRequests { failed_requests: HashMap::new(), importing_requests: HashSet::new(), request_type_name, + metrics: metrics_registry.and_then(|registry| { + Metrics::register(registry) + .inspect_err(|error| { + log::error!( + target: LOG_TARGET, + "Failed to register `ExtraRequests` metrics {error}", + ); + }) + .ok() + }), } } @@ -83,6 +127,12 @@ impl ExtraRequests { self.pending_requests.clear(); self.active_requests.clear(); self.failed_requests.clear(); + + if let Some(metrics) = &self.metrics { + metrics.pending.set(0); + metrics.active.set(0); + metrics.failed.set(0); + } } /// Returns an iterator-like struct that yields peers which extra @@ -100,6 +150,9 @@ impl ExtraRequests { Ok(true) => { // this is a new root so we add it to the current `pending_requests` self.pending_requests.push_back((request.0, request.1)); + if let Some(metrics) = &self.metrics { + metrics.pending.inc(); + } }, Err(fork_tree::Error::Revert) => { // we have finalized further than the given request, presumably @@ -117,6 +170,10 @@ impl ExtraRequests { pub(crate) fn peer_disconnected(&mut self, who: &PeerId) { if let Some(request) = self.active_requests.remove(who) { self.pending_requests.push_front(request); + if let Some(metrics) = &self.metrics { + metrics.active.dec(); + metrics.pending.inc(); + } } } @@ -130,13 +187,21 @@ impl ExtraRequests { // currently enforced by the outer network protocol before passing on // messages to chain sync. if let Some(request) = self.active_requests.remove(&who) { + if let Some(metrics) = &self.metrics { + metrics.active.dec(); + } + if let Some(r) = resp { trace!(target: LOG_TARGET, "Queuing import of {} from {:?} for {:?}", self.request_type_name, who, request, ); - self.importing_requests.insert(request); + if self.importing_requests.insert(request) { + if let Some(metrics) = &self.metrics { + metrics.importing.inc(); + } + } return Some((who, request.0, request.1, r)) } else { trace!(target: LOG_TARGET, @@ -146,6 +211,10 @@ impl ExtraRequests { } self.failed_requests.entry(request).or_default().push((who, Instant::now())); self.pending_requests.push_front(request); + if let Some(metrics) = &self.metrics { + metrics.failed.set(self.failed_requests.len().try_into().unwrap_or(u64::MAX)); + metrics.pending.inc(); + } } else { trace!(target: LOG_TARGET, "No active {} request to {:?}", @@ -194,6 +263,11 @@ impl ExtraRequests { self.pending_requests.retain(|(h, n)| roots.contains(&(h, n, &()))); self.active_requests.retain(|_, (h, n)| roots.contains(&(h, n, &()))); self.failed_requests.retain(|(h, n), _| roots.contains(&(h, n, &()))); + if let Some(metrics) = &self.metrics { + metrics.pending.set(self.pending_requests.len().try_into().unwrap_or(u64::MAX)); + metrics.active.set(self.active_requests.len().try_into().unwrap_or(u64::MAX)); + metrics.failed.set(self.failed_requests.len().try_into().unwrap_or(u64::MAX)); + } Ok(()) } @@ -210,12 +284,18 @@ impl ExtraRequests { if !self.importing_requests.remove(&request) { return false } + if let Some(metrics) = &self.metrics { + metrics.importing.dec(); + } let (finalized_hash, finalized_number) = match result { Ok(req) => (req.0, req.1), Err(_) => { if reschedule_on_failure { self.pending_requests.push_front(request); + if let Some(metrics) = &self.metrics { + metrics.pending.inc(); + } } return true }, @@ -233,6 +313,11 @@ impl ExtraRequests { self.active_requests.clear(); self.pending_requests.clear(); self.pending_requests.extend(self.tree.roots().map(|(&h, &n, _)| (h, n))); + if let Some(metrics) = &self.metrics { + metrics.failed.set(0); + metrics.active.set(0); + metrics.pending.set(self.pending_requests.len().try_into().unwrap_or(u64::MAX)); + } self.best_seen_finalized_number = finalized_number; true @@ -249,16 +334,6 @@ impl ExtraRequests { pub(crate) fn pending_requests(&self) -> impl Iterator> { self.pending_requests.iter() } - - /// Get some key metrics. - pub(crate) fn metrics(&self) -> Metrics { - Metrics { - pending_requests: self.pending_requests.len().try_into().unwrap_or(std::u32::MAX), - active_requests: self.active_requests.len().try_into().unwrap_or(std::u32::MAX), - failed_requests: self.failed_requests.len().try_into().unwrap_or(std::u32::MAX), - importing_requests: self.importing_requests.len().try_into().unwrap_or(std::u32::MAX), - } - } } /// Matches peers with pending extra requests. @@ -301,8 +376,17 @@ impl<'a, B: BlockT> Matcher<'a, B> { for requests in self.extras.failed_requests.values_mut() { requests.retain(|(_, instant)| instant.elapsed() < EXTRA_RETRY_WAIT); } + if let Some(metrics) = &self.extras.metrics { + metrics + .failed + .set(self.extras.failed_requests.len().try_into().unwrap_or(u64::MAX)); + } while let Some(request) = self.extras.pending_requests.pop_front() { + if let Some(metrics) = &self.extras.metrics { + metrics.pending.dec(); + } + for (peer, sync) in peers.iter().filter(|(_, sync)| sync.state == PeerSyncState::Available) { @@ -326,6 +410,9 @@ impl<'a, B: BlockT> Matcher<'a, B> { continue } self.extras.active_requests.insert(*peer, request); + if let Some(metrics) = &self.extras.metrics { + metrics.active.inc(); + } trace!(target: LOG_TARGET, "Sending {} request to {:?} for {:?}", @@ -336,6 +423,9 @@ impl<'a, B: BlockT> Matcher<'a, B> { } self.extras.pending_requests.push_back(request); + if let Some(metrics) = &self.extras.metrics { + metrics.pending.inc(); + } self.remaining -= 1; if self.remaining == 0 { @@ -359,7 +449,7 @@ mod tests { #[test] fn requests_are_processed_in_order() { fn property(mut peers: ArbitraryPeers) { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); let num_peers_available = peers.0.values().filter(|s| s.state == PeerSyncState::Available).count(); @@ -385,7 +475,7 @@ mod tests { #[test] fn new_roots_schedule_new_request() { fn property(data: Vec) { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); for (i, number) in data.into_iter().enumerate() { let hash = [i as u8; 32].into(); let pending = requests.pending_requests.len(); @@ -402,7 +492,7 @@ mod tests { #[test] fn disconnecting_implies_rescheduling() { fn property(mut peers: ArbitraryPeers) -> bool { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); let num_peers_available = peers.0.values().filter(|s| s.state == PeerSyncState::Available).count(); @@ -438,7 +528,7 @@ mod tests { #[test] fn no_response_reschedules() { fn property(mut peers: ArbitraryPeers) { - let mut requests = ExtraRequests::::new("test"); + let mut requests = ExtraRequests::::new("test", None); let num_peers_available = peers.0.values().filter(|s| s.state == PeerSyncState::Available).count(); @@ -480,7 +570,7 @@ mod tests { fn request_is_rescheduled_when_earlier_block_is_finalized() { sp_tracing::try_init_simple(); - let mut finality_proofs = ExtraRequests::::new("test"); + let mut finality_proofs = ExtraRequests::::new("test", None); let hash4 = [4; 32].into(); let hash5 = [5; 32].into(); @@ -521,7 +611,7 @@ mod tests { #[test] fn ancestor_roots_are_finalized_when_finality_notification_is_missed() { - let mut finality_proofs = ExtraRequests::::new("test"); + let mut finality_proofs = ExtraRequests::::new("test", None); let hash4 = [4; 32].into(); let hash5 = [5; 32].into(); diff --git a/substrate/client/network/sync/src/lib.rs b/substrate/client/network/sync/src/lib.rs index 9f6c0f45d089ce415f9e8c0ae792de1d7a327407..ca7280edba5f5df5ec9be586f2048a5638604ae4 100644 --- a/substrate/client/network/sync/src/lib.rs +++ b/substrate/client/network/sync/src/lib.rs @@ -19,7 +19,7 @@ //! Blockchain syncing implementation in Substrate. pub use service::syncing_service::SyncingService; -pub use strategy::warp::{WarpSyncParams, WarpSyncPhase, WarpSyncProgress}; +pub use strategy::warp::{WarpSyncConfig, WarpSyncPhase, WarpSyncProgress}; pub use types::{SyncEvent, SyncEventStream, SyncState, SyncStatus, SyncStatusProvider}; mod block_announce_validator; diff --git a/substrate/client/network/sync/src/service/syncing_service.rs b/substrate/client/network/sync/src/service/syncing_service.rs index f4bc58afd4fda16a825acba25d64fa319460a25b..08a2b36118a9d4d7d1b780c62a848a789562babf 100644 --- a/substrate/client/network/sync/src/service/syncing_service.rs +++ b/substrate/client/network/sync/src/service/syncing_service.rs @@ -50,10 +50,6 @@ pub enum ToServiceCommand { EventStream(TracingUnboundedSender), Status(oneshot::Sender>), NumActivePeers(oneshot::Sender), - SyncState(oneshot::Sender>), - BestSeenBlock(oneshot::Sender>>), - NumSyncPeers(oneshot::Sender), - NumQueuedBlocks(oneshot::Sender), NumDownloadedBlocks(oneshot::Sender), NumSyncRequests(oneshot::Sender), PeersInfo(oneshot::Sender)>>), @@ -83,6 +79,11 @@ impl SyncingService { Self { tx, num_connected, is_major_syncing } } + /// Get the number of peers known to `SyncingEngine` (both full and light). + pub fn num_connected_peers(&self) -> usize { + self.num_connected.load(Ordering::Relaxed) + } + /// Get the number of active peers. pub async fn num_active_peers(&self) -> Result { let (tx, rx) = oneshot::channel(); @@ -91,30 +92,6 @@ impl SyncingService { rx.await } - /// Get best seen block. - pub async fn best_seen_block(&self) -> Result>, oneshot::Canceled> { - let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::BestSeenBlock(tx)); - - rx.await - } - - /// Get the number of sync peers. - pub async fn num_sync_peers(&self) -> Result { - let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::NumSyncPeers(tx)); - - rx.await - } - - /// Get the number of queued blocks. - pub async fn num_queued_blocks(&self) -> Result { - let (tx, rx) = oneshot::channel(); - let _ = self.tx.unbounded_send(ToServiceCommand::NumQueuedBlocks(tx)); - - rx.await - } - /// Get the number of downloaded blocks. pub async fn num_downloaded_blocks(&self) -> Result { let (tx, rx) = oneshot::channel(); @@ -149,11 +126,11 @@ impl SyncingService { /// Get sync status /// /// Returns an error if `SyncingEngine` has terminated. - pub async fn status(&self) -> Result, ()> { + pub async fn status(&self) -> Result, oneshot::Canceled> { let (tx, rx) = oneshot::channel(); let _ = self.tx.unbounded_send(ToServiceCommand::Status(tx)); - rx.await.map_err(|_| ()) + rx.await } } diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index 72f84d1c286ea4d63b385f7897294523959661b2..f8d6976bbaa0de9e36398f0d205b0607a13176ee 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; @@ -29,8 +29,8 @@ use crate::{ types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncStatus}, LOG_TARGET, }; -use chain_sync::{ChainSync, ChainSyncAction, ChainSyncMode}; -use log::{debug, error, info, warn}; +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}; @@ -59,6 +59,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 { @@ -104,7 +206,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, } @@ -140,26 +242,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 +255,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 +270,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.clone(), - std::iter::empty(), - )?; - Ok(Self { - config, - client, - warp: None, - state: None, - chain_sync: Some(chain_sync), - peer_best_blocks: Default::default(), - }) - } - } - - /// Notify that a new peer has connected. - pub fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { + 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 +278,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 +286,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 +319,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 +359,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 +370,7 @@ where } } - /// Process state response. - pub fn on_state_response( + fn on_state_response( &mut self, peer_id: PeerId, key: StrategyKey, @@ -352,7 +381,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 +392,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 +410,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 +424,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 +447,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,61 +465,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()) } - /// Report Prometheus metrics - pub fn report_metrics(&self) { - if let Some(ref chain_sync) = self.chain_sync { - chain_sync.report_metrics(); - } - } - - /// Let `WarpSync` know about target block header - pub fn set_warp_sync_target_block_header( - &mut self, - target_header: B::Header, - ) -> Result<(), ()> { - match self.config.mode { - SyncMode::Warp => match self.warp { - Some(ref mut warp) => { - warp.set_target_block(target_header); - Ok(()) - }, - None => { - // As mode is set to warp sync, but no warp sync strategy is active, this means - // that warp sync has already finished / was skipped. - warn!( - target: LOG_TARGET, - "Discarding warp sync target, as warp sync was seemingly skipped due \ - to node being (partially) synced.", - ); - Ok(()) - }, - }, - _ => { - error!( - target: LOG_TARGET, - "Cannot set warp sync target block: not in warp sync mode." - ); - debug_assert!(false); - Err(()) - }, - } - } - - /// Get actions that should be performed by the owner on the strategy's behalf - #[must_use] - pub fn actions(&mut self) -> 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 { @@ -505,7 +483,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") }; @@ -516,6 +494,56 @@ where Ok(actions) } +} + +impl PolkadotSyncingStrategy +where + B: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + 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(), + }) + } + } /// Proceed with the next strategy if the active one finished. pub fn proceed_to_next(&mut self) -> Result<(), ClientError> { @@ -552,7 +580,7 @@ where self.client.clone(), self.config.max_parallel_downloads, self.config.max_blocks_per_request, - self.config.metrics_registry.clone(), + self.config.metrics_registry.as_ref(), self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { (*peer_id, *best_hash, *best_number) }), @@ -580,7 +608,7 @@ where self.client.clone(), self.config.max_parallel_downloads, self.config.max_blocks_per_request, - self.config.metrics_registry.clone(), + self.config.metrics_registry.as_ref(), self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { (*peer_id, *best_hash, *best_number) }), @@ -600,63 +628,3 @@ where } } } - -#[cfg(test)] -mod test { - use super::*; - use futures::executor::block_on; - use sc_block_builder::BlockBuilderBuilder; - use substrate_test_runtime_client::{ - ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClientBuilder, - TestClientBuilderExt, - }; - - /// Regression test for crash when starting already synced parachain node with `--sync=warp`. - /// We must remove this after setting of warp sync target block is moved to initialization of - /// `SyncingEngine` (issue https://github.com/paritytech/polkadot-sdk/issues/3537). - #[test] - fn set_target_block_finished_warp_sync() { - // Populate database with finalized state. - let mut client = Arc::new(TestClientBuilder::new().build()); - let block = BlockBuilderBuilder::new(&*client) - .on_parent_block(client.chain_info().best_hash) - .with_parent_block_number(client.chain_info().best_number) - .build() - .unwrap() - .build() - .unwrap() - .block; - block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let just = (*b"TEST", Vec::new()); - client.finalize_block(block.hash(), Some(just)).unwrap(); - let target_block = BlockBuilderBuilder::new(&*client) - .on_parent_block(client.chain_info().best_hash) - .with_parent_block_number(client.chain_info().best_number) - .build() - .unwrap() - .build() - .unwrap() - .block; - - // Initialize syncing strategy. - let config = SyncingConfig { - mode: SyncMode::Warp, - max_parallel_downloads: 3, - max_blocks_per_request: 64, - metrics_registry: None, - }; - let mut strategy = - SyncingStrategy::new(config, client, Some(WarpSyncConfig::WaitForTarget)).unwrap(); - - // Warp sync instantly finishes as we have finalized state in DB. - let actions = strategy.actions().unwrap(); - assert_eq!(actions.len(), 1); - assert!(matches!(actions[0], SyncingAction::Finished)); - assert!(strategy.warp.is_none()); - - // Try setting the target block. We mustn't crash. - strategy - .set_warp_sync_target_block_header(target_block.header().clone()) - .unwrap(); - } -} diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 52870d5ba15147cdb40aec9f1c51a338337c3125..a8ba5558d1bcb772739b977a21e5fd3aad840a6c 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, @@ -43,8 +44,8 @@ use crate::{ use codec::Encode; use log::{debug, error, info, trace, warn}; -use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; -use sc_client_api::{BlockBackend, ProofProvider}; +use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; +use sc_client_api::{blockchain::BlockGap, BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock, @@ -128,7 +129,6 @@ mod rep { struct Metrics { queued_blocks: Gauge, fork_targets: Gauge, - justifications: GaugeVec, } impl Metrics { @@ -143,16 +143,6 @@ impl Metrics { let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?; register(g, r)? }, - justifications: { - let g = GaugeVec::new( - Opts::new( - "substrate_sync_extra_justifications", - "Number of extra justifications requests", - ), - &["status"], - )?; - register(g, r)? - }, }) } } @@ -208,28 +198,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 { @@ -244,50 +212,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 { @@ -357,7 +281,59 @@ 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, + /// 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 @@ -368,120 +344,608 @@ 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, - 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"), - 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, - num_connected_peers: 0u32, - queued_blocks: self.queue_blocks.len() as u32, - state_sync: self.state_sync.as_ref().map(|s| s.progress()), - warp_sync: warp_sync_progress, + // 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, 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, + 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, + 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] @@ -509,7 +973,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 @@ -533,7 +997,7 @@ where state: PeerSyncState::Available, }, ); - return Ok(None) + return Ok(None); } // If we are at genesis, just start downloading. @@ -602,85 +1066,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(|| ForkTarget { number, peers: Default::default(), parent_hash: None }) - .peers - .extend(peers); - } - /// Submit a block response for processing. #[must_use] fn on_block_data( @@ -754,14 +1139,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 @@ -802,14 +1187,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() { @@ -843,7 +1228,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()) @@ -854,11 +1239,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. @@ -883,16 +1269,22 @@ where ); self.fork_targets .entry(peer.best_hash) - .or_insert_with(|| ForkTarget { - number: peer.best_number, - parent_hash: None, - peers: Default::default(), + .or_insert_with(|| { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.inc(); + } + + ForkTarget { + number: peer.best_number, + parent_hash: None, + peers: Default::default(), + } }) .peers .insert(*peer_id); } peer.state = PeerSyncState::Available; - return Ok(()) + return Ok(()); } }, PeerSyncState::Available | @@ -925,7 +1317,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,256 +1339,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)) - }; - - 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(|| 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) - } + return Ok(()); + }; - if let Some(state) = self.peers.remove(peer_id) { - if !state.state.is_available() { - if let Some(bad_peer) = - self.disconnected_peers.on_disconnect_during_request(*peer_id) - { - self.actions.push(ChainSyncAction::DropPeer(bad_peer)); + self.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)); } - } - } - 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() - }); + 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(()); + } } - } - - /// Report prometheus metrics. - pub fn report_metrics(&self) { - if let Some(metrics) = &self.metrics { - metrics - .fork_targets - .set(self.fork_targets.len().try_into().unwrap_or(std::u64::MAX)); - metrics - .queued_blocks - .set(self.queue_blocks.len().try_into().unwrap_or(std::u64::MAX)); - let justifications_metrics = self.extra_justifications.metrics(); - metrics - .justifications - .with_label_values(&["pending"]) - .set(justifications_metrics.pending_requests.into()); - metrics - .justifications - .with_label_values(&["active"]) - .set(justifications_metrics.active_requests.into()); - metrics - .justifications - .with_label_values(&["failed"]) - .set(justifications_metrics.failed_requests.into()); - metrics - .justifications - .with_label_values(&["importing"]) - .set(justifications_metrics.importing_requests.into()); - } + Ok(()) } /// Returns the median seen block number. @@ -1264,8 +1454,13 @@ where self.on_block_queued(h, n) } self.queue_blocks.extend(new_blocks.iter().map(|b| b.hash)); + if let Some(metrics) = &self.metrics { + metrics + .queued_blocks + .set(self.queue_blocks.len().try_into().unwrap_or(u64::MAX)); + } - self.actions.push(ChainSyncAction::ImportBlocks { origin, blocks: new_blocks }) + self.actions.push(SyncingAction::ImportBlocks { origin, blocks: new_blocks }) } fn update_peer_common_number(&mut self, peer_id: &PeerId, new_common: NumberFor) { @@ -1280,6 +1475,9 @@ where /// through all peers to update our view of their state as well. fn on_block_queued(&mut self, hash: &B::Hash, number: NumberFor) { if self.fork_targets.remove(hash).is_some() { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.dec(); + } trace!(target: LOG_TARGET, "Completed fork sync {hash:?}"); } if let Some(gap_sync) = &mut self.gap_sync { @@ -1294,7 +1492,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 }; @@ -1341,7 +1539,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(_) => { @@ -1394,7 +1595,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(), @@ -1414,7 +1615,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) } @@ -1457,53 +1658,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; @@ -1534,12 +1688,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(); @@ -1549,12 +1703,13 @@ where std::cmp::min(self.best_queued_number, self.client.info().finalized_number); let best_queued = self.best_queued_number; let client = &self.client; - let queue = &self.queue_blocks; + let queue_blocks = &self.queue_blocks; let allowed_requests = self.allowed_requests.take(); let max_parallel = if is_major_syncing { 1 } else { self.max_parallel_downloads }; let max_blocks_per_request = self.max_blocks_per_request; let gap_sync = &mut self.gap_sync; let disconnected_peers = &mut self.disconnected_peers; + let metrics = self.metrics.as_ref(); self.peers .iter_mut() .filter_map(move |(&id, peer)| { @@ -1562,7 +1717,7 @@ where !allowed_requests.contains(&id) || !disconnected_peers.is_peer_available(&id) { - return None + return None; } // If our best queued is more than `MAX_BLOCKS_TO_LOOK_BACKWARDS` blocks away from @@ -1574,7 +1729,7 @@ where MAX_BLOCKS_TO_LOOK_BACKWARDS.into() && best_queued < peer.best_number && peer.common_number < last_finalized && - queue.len() <= MAJOR_SYNC_BLOCKS.into() + queue_blocks.len() <= MAJOR_SYNC_BLOCKS.into() { trace!( target: LOG_TARGET, @@ -1617,13 +1772,14 @@ where last_finalized, attrs, |hash| { - if queue.contains(hash) { + if queue_blocks.contains(hash) { BlockStatus::Queued } else { client.block_status(*hash).unwrap_or(BlockStatus::Unknown) } }, max_blocks_per_request, + metrics, ) { trace!(target: LOG_TARGET, "Downloading fork {hash:?} from {id}"); peer.state = PeerSyncState::DownloadingStale(hash); @@ -1659,17 +1815,17 @@ where /// Get a state request scheduled by sync to be sent out (if any). fn state_request(&mut self) -> Option<(PeerId, OpaqueStateRequest)> { if self.allowed_requests.is_empty() { - return None + return None; } if self.state_sync.is_some() && self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { // Only one pending state request is allowed. - return None + return None; } if let Some(sync) = &self.state_sync { if sync.is_complete() { - return None + return None; } for (id, peer) in self.peers.iter_mut() { @@ -1681,7 +1837,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)))); } } } @@ -1720,7 +1876,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 { @@ -1739,7 +1895,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(()), @@ -1750,170 +1906,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 { - self.queue_blocks.remove(hash); - 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() } } @@ -1971,7 +1996,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; @@ -1990,7 +2015,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; @@ -2021,7 +2046,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, @@ -2094,14 +2119,15 @@ fn peer_gap_block_request( /// Get pending fork sync targets for a peer. fn fork_sync_request( id: &PeerId, - targets: &mut HashMap>, + fork_targets: &mut HashMap>, best_num: NumberFor, finalized: NumberFor, attributes: BlockAttributes, check_block: impl Fn(&B::Hash) -> BlockStatus, max_blocks_per_request: u32, + metrics: Option<&Metrics>, ) -> Option<(B::Hash, BlockRequest)> { - targets.retain(|hash, r| { + fork_targets.retain(|hash, r| { if r.number <= finalized { trace!( target: LOG_TARGET, @@ -2109,7 +2135,7 @@ fn fork_sync_request( hash, r.number, ); - return false + return false; } if check_block(hash) != BlockStatus::Unknown { trace!( @@ -2118,13 +2144,16 @@ fn fork_sync_request( hash, r.number, ); - return false + return false; } true }); - for (hash, r) in targets { + if let Some(metrics) = metrics { + metrics.fork_targets.set(fork_targets.len().try_into().unwrap_or(u64::MAX)); + } + for (hash, r) in fork_targets { if !r.peers.contains(&id) { - continue + 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. @@ -2151,7 +2180,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); } @@ -2170,7 +2199,7 @@ where T: HeaderMetadata + ?Sized, { if base == block { - return Ok(false) + return Ok(false); } let ancestor = sp_blockchain::lowest_common_ancestor(client, *block, *base)?; @@ -2197,7 +2226,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 = @@ -2217,7 +2246,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) && @@ -2228,7 +2257,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()) @@ -2238,7 +2267,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)); } } @@ -2253,7 +2282,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) { @@ -2271,7 +2300,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 cd955113542be598e18c94057d3ad056c95a6002..59436f387db6a38bffdd939e30cf498f1d736b77 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync/test.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync/test.rs @@ -91,7 +91,7 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { #[test] fn restart_doesnt_affect_peers_downloading_finality_data() { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); // we request max 8 blocks to always initiate block requests to both peers for the test to be // deterministic @@ -103,7 +103,7 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { let peer_id2 = PeerId::random(); let peer_id3 = PeerId::random(); - let mut new_blocks = |n| { + let new_blocks = |n| { for _ in 0..n { let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) @@ -128,10 +128,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 +162,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 }, @@ -242,12 +242,12 @@ fn get_block_request( } /// Build and import a new best block. -fn build_block(client: &mut Arc, at: Option, fork: bool) -> Block { +fn build_block(client: &TestClient, at: Option, fork: bool) -> Block { let at = at.unwrap_or_else(|| client.info().best_hash); - let mut block_builder = BlockBuilderBuilder::new(&**client) + let mut block_builder = BlockBuilderBuilder::new(client) .on_parent_block(at) - .fetch_parent_block_number(&**client) + .fetch_parent_block_number(client) .unwrap() .build() .unwrap(); @@ -282,13 +282,13 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { sp_tracing::try_init_simple(); let blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); (0..MAX_DOWNLOAD_AHEAD * 2) - .map(|_| build_block(&mut client, None, false)) + .map(|_| build_block(&client, None, false)) .collect::>() }; - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let info = client.info(); let mut sync = @@ -329,7 +329,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; @@ -415,13 +415,13 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { fn can_sync_huge_fork() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 4) - .map(|_| build_block(&mut client, None, false)) + .map(|_| build_block(&client, None, false)) .collect::>(); let fork_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let fork_blocks = blocks[..MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2] .into_iter() .inspect(|b| block_on(client.import(BlockOrigin::Own, (*b).clone())).unwrap()) @@ -431,8 +431,7 @@ fn can_sync_huge_fork() { fork_blocks .into_iter() .chain( - (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1) - .map(|_| build_block(&mut client, None, true)), + (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1).map(|_| build_block(&client, None, true)), ) .collect::>() }; @@ -477,7 +476,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:?}"), } }; @@ -509,7 +508,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; @@ -550,13 +549,13 @@ fn can_sync_huge_fork() { fn syncs_fork_without_duplicate_requests() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 4) - .map(|_| build_block(&mut client, None, false)) + .map(|_| build_block(&client, None, false)) .collect::>(); let fork_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let fork_blocks = blocks[..MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2] .into_iter() .inspect(|b| block_on(client.import(BlockOrigin::Own, (*b).clone())).unwrap()) @@ -566,8 +565,7 @@ fn syncs_fork_without_duplicate_requests() { fork_blocks .into_iter() .chain( - (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1) - .map(|_| build_block(&mut client, None, true)), + (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 2 + 1).map(|_| build_block(&client, None, true)), ) .collect::>() }; @@ -612,7 +610,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:?}"), } }; @@ -648,7 +646,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; @@ -708,8 +706,8 @@ fn syncs_fork_without_duplicate_requests() { #[test] fn removes_target_fork_on_disconnect() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::>(); + let client = Arc::new(TestClientBuilder::new().build()); + let blocks = (0..3).map(|_| build_block(&client, None, false)).collect::>(); let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) @@ -733,8 +731,8 @@ fn removes_target_fork_on_disconnect() { #[test] fn can_import_response_with_missing_blocks() { sp_tracing::try_init_simple(); - let mut client2 = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..4).map(|_| build_block(&mut client2, None, false)).collect::>(); + let client2 = TestClientBuilder::new().build(); + let blocks = (0..4).map(|_| build_block(&client2, None, false)).collect::>(); let empty_client = Arc::new(TestClientBuilder::new().build()); @@ -770,14 +768,14 @@ fn ancestor_search_repeat() { #[test] fn sync_restart_removes_block_but_not_justification_requests() { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = Arc::new(TestClientBuilder::new().build()); let mut sync = ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) .unwrap(); let peers = vec![PeerId::random(), PeerId::random()]; - let mut new_blocks = |n| { + let new_blocks = |n| { for _ in 0..n { let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) @@ -841,10 +839,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); @@ -854,7 +852,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, } })); @@ -880,11 +878,11 @@ fn sync_restart_removes_block_but_not_justification_requests() { fn request_across_forks() { sp_tracing::try_init_simple(); - let mut client = Arc::new(TestClientBuilder::new().build()); - let blocks = (0..100).map(|_| build_block(&mut client, None, false)).collect::>(); + let client = Arc::new(TestClientBuilder::new().build()); + let blocks = (0..100).map(|_| build_block(&client, None, false)).collect::>(); let fork_a_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let mut fork_blocks = blocks[..] .into_iter() .inspect(|b| { @@ -894,13 +892,13 @@ fn request_across_forks() { .cloned() .collect::>(); for _ in 0..10 { - fork_blocks.push(build_block(&mut client, None, false)); + fork_blocks.push(build_block(&client, None, false)); } fork_blocks }; let fork_b_blocks = { - let mut client = Arc::new(TestClientBuilder::new().build()); + let client = TestClientBuilder::new().build(); let mut fork_blocks = blocks[..] .into_iter() .inspect(|b| { @@ -910,7 +908,7 @@ fn request_across_forks() { .cloned() .collect::>(); for _ in 0..10 { - fork_blocks.push(build_block(&mut client, None, true)); + fork_blocks.push(build_block(&client, None, true)); } fork_blocks }; @@ -945,7 +943,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()); @@ -990,7 +988,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 ff229863a68b38f3ea8f268ea3815f25fe3d7037..6f06f238fe3a610adedfd992523ef62a2e79d68f 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -340,7 +340,6 @@ impl StateStrategy { }, best_seen_block: Some(self.state_sync.target_number()), num_peers: self.peers.len().saturated_into(), - num_connected_peers: self.peers.len().saturated_into(), queued_blocks: 0, state_sync: Some(self.state_sync.progress()), warp_sync: None, diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 00855578695d85e529cb8acae7e33fc533a8a2c0..99405c2e5f08bd5e7845c97e66db6cfed2c972da 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -26,7 +26,6 @@ use crate::{ LOG_TARGET, }; use codec::{Decode, Encode}; -use futures::channel::oneshot; use log::{debug, error, trace}; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, Direction, FromBlock, @@ -104,8 +103,6 @@ mod rep { pub enum WarpSyncPhase { /// Waiting for peers to connect. AwaitingPeers { required_peers: usize }, - /// Waiting for target block to be received. - AwaitingTargetBlock, /// Downloading and verifying grandpa warp proofs. DownloadingWarpProofs, /// Downloading target block. @@ -125,7 +122,6 @@ impl fmt::Display for WarpSyncPhase { match self { Self::AwaitingPeers { required_peers } => write!(f, "Waiting for {required_peers} peers to be connected"), - Self::AwaitingTargetBlock => write!(f, "Waiting for target block to be received"), Self::DownloadingWarpProofs => write!(f, "Downloading finality proofs"), Self::DownloadingTargetBlock => write!(f, "Downloading target block"), Self::DownloadingState => write!(f, "Downloading state"), @@ -145,37 +141,14 @@ pub struct WarpSyncProgress { pub total_bytes: u64, } -/// The different types of warp syncing, passed to `build_network`. -pub enum WarpSyncParams { - /// Standard warp sync for the chain. - WithProvider(Arc>), - /// Skip downloading proofs and wait for a header of the state that should be downloaded. - /// - /// It is expected that the header provider ensures that the header is trusted. - WaitForTarget(oneshot::Receiver<::Header>), -} - /// Warp sync configuration as accepted by [`WarpSync`]. pub enum WarpSyncConfig { /// Standard warp sync for the chain. WithProvider(Arc>), - /// Skip downloading proofs and wait for a header of the state that should be downloaded. + /// Skip downloading proofs and use provided header of the state that should be downloaded. /// /// It is expected that the header provider ensures that the header is trusted. - WaitForTarget, -} - -impl WarpSyncParams { - /// Split `WarpSyncParams` into `WarpSyncConfig` and warp sync target block header receiver. - pub fn split( - self, - ) -> (WarpSyncConfig, Option::Header>>) { - match self { - WarpSyncParams::WithProvider(provider) => - (WarpSyncConfig::WithProvider(provider), None), - WarpSyncParams::WaitForTarget(rx) => (WarpSyncConfig::WaitForTarget, Some(rx)), - } - } + WithTarget(::Header), } /// Warp sync phase used by warp sync state machine. @@ -189,9 +162,6 @@ enum Phase { last_hash: B::Hash, warp_sync_provider: Arc>, }, - /// Waiting for target block to be set externally if we skip warp proofs downloading, - /// and start straight from the target block (used by parachains warp sync). - PendingTargetBlock, /// Downloading target block. TargetBlock(B::Header), /// Warp sync is complete. @@ -274,7 +244,7 @@ where let phase = match warp_sync_config { WarpSyncConfig::WithProvider(warp_sync_provider) => Phase::WaitingForPeers { warp_sync_provider }, - WarpSyncConfig::WaitForTarget => Phase::PendingTargetBlock, + WarpSyncConfig::WithTarget(target_header) => Phase::TargetBlock(target_header), }; Self { @@ -289,20 +259,6 @@ where } } - /// Set target block externally in case we skip warp proof downloading. - pub fn set_target_block(&mut self, header: B::Header) { - let Phase::PendingTargetBlock = self.phase else { - error!( - target: LOG_TARGET, - "Attempt to set warp sync target block in invalid phase.", - ); - debug_assert!(false); - return - }; - - self.phase = Phase::TargetBlock(header); - } - /// Notify that a new peer has connected. pub fn add_peer(&mut self, peer_id: PeerId, _best_hash: B::Hash, best_number: NumberFor) { self.peers.insert(peer_id, Peer { best_number, state: PeerState::Available }); @@ -592,10 +548,6 @@ where phase: WarpSyncPhase::DownloadingTargetBlock, total_bytes: self.total_proof_bytes, }, - Phase::PendingTargetBlock { .. } => WarpSyncProgress { - phase: WarpSyncPhase::AwaitingTargetBlock, - total_bytes: self.total_proof_bytes, - }, Phase::Complete => WarpSyncProgress { phase: WarpSyncPhase::Complete, total_bytes: self.total_proof_bytes + self.total_state_bytes, @@ -614,19 +566,16 @@ where state: match &self.phase { Phase::WaitingForPeers { .. } => SyncState::Downloading { target: Zero::zero() }, Phase::WarpProof { .. } => SyncState::Downloading { target: Zero::zero() }, - Phase::PendingTargetBlock => SyncState::Downloading { target: Zero::zero() }, Phase::TargetBlock(header) => SyncState::Downloading { target: *header.number() }, Phase::Complete => SyncState::Idle, }, best_seen_block: match &self.phase { Phase::WaitingForPeers { .. } => None, Phase::WarpProof { .. } => None, - Phase::PendingTargetBlock => None, Phase::TargetBlock(header) => Some(*header.number()), Phase::Complete => None, }, num_peers: self.peers.len().saturated_into(), - num_connected_peers: self.peers.len().saturated_into(), queued_blocks: 0, state_sync: None, warp_sync: Some(self.progress()), @@ -759,7 +708,13 @@ mod test { #[test] fn warp_sync_to_target_for_db_with_finalized_state_is_noop() { let client = mock_client_with_state(); - let config = WarpSyncConfig::WaitForTarget; + let config = WarpSyncConfig::WithTarget(::Header::new( + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + )); let mut warp_sync = WarpSync::new(Arc::new(client), config); // Warp sync instantly finishes @@ -785,7 +740,13 @@ mod test { #[test] fn warp_sync_to_target_for_empty_db_doesnt_finish_instantly() { let client = mock_client_without_state(); - let config = WarpSyncConfig::WaitForTarget; + let config = WarpSyncConfig::WithTarget(::Header::new( + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + )); let mut warp_sync = WarpSync::new(Arc::new(client), config); // No actions are emitted. @@ -936,7 +897,13 @@ mod test { } // Manually set to another phase. - warp_sync.phase = Phase::PendingTargetBlock; + warp_sync.phase = Phase::TargetBlock(::Header::new( + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + )); // No request is made. assert!(warp_sync.warp_proof_request().is_none()); @@ -1193,7 +1160,7 @@ mod test { .unwrap() .block; let target_header = target_block.header().clone(); - let config = WarpSyncConfig::WaitForTarget; + let config = WarpSyncConfig::WithTarget(target_header); let mut warp_sync = WarpSync::new(client, config); // Make sure we have enough peers to make a request. @@ -1201,10 +1168,6 @@ mod test { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); } - // No actions generated so far. - assert_eq!(warp_sync.actions().count(), 0); - - warp_sync.set_target_block(target_header); assert!(matches!(warp_sync.phase, Phase::TargetBlock(_))); let (_peer_id, request) = warp_sync.target_block_request().unwrap(); diff --git a/substrate/client/network/sync/src/types.rs b/substrate/client/network/sync/src/types.rs index e8b8c89003609e009f75b1e06bdaa87d900d24a2..c3403fe1e5f7561c41fa7e8460c302712282df77 100644 --- a/substrate/client/network/sync/src/types.rs +++ b/substrate/client/network/sync/src/types.rs @@ -39,7 +39,7 @@ pub struct PeerInfo { } /// Info about a peer's known state (both full and light). -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct ExtendedPeerInfo { /// Roles pub roles: Roles, @@ -49,6 +49,17 @@ pub struct ExtendedPeerInfo { pub best_number: NumberFor, } +impl Clone for ExtendedPeerInfo +where + B: BlockT, +{ + fn clone(&self) -> Self { + Self { roles: self.roles, best_hash: self.best_hash, best_number: self.best_number } + } +} + +impl Copy for ExtendedPeerInfo where B: BlockT {} + /// Reported sync state. #[derive(Clone, Eq, PartialEq, Debug)] pub enum SyncState { @@ -76,8 +87,6 @@ pub struct SyncStatus { pub best_seen_block: Option>, /// Number of peers participating in syncing. pub num_peers: u32, - /// Number of peers known to `SyncingEngine` (both full and light). - pub num_connected_peers: u32, /// Number of blocks queued for import pub queued_blocks: u32, /// State sync status in progress, if any. diff --git a/substrate/client/network/test/src/block_import.rs b/substrate/client/network/test/src/block_import.rs index 690a579e0272bce6fc0123deb66e528635afb9a2..8c120d9de662890d78cf0498557098b059c7bd96 100644 --- a/substrate/client/network/test/src/block_import.rs +++ b/substrate/client/network/test/src/block_import.rs @@ -32,7 +32,7 @@ use substrate_test_runtime_client::{ }; fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock) { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let block = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().best_hash) .with_parent_block_number(client.chain_info().best_number) @@ -78,7 +78,7 @@ fn import_single_good_block_works() { &mut substrate_test_runtime_client::new(), BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Ok(BlockImportStatus::ImportedUnknown(ref num, ref aux, ref org)) if *num == number && *aux == expected_aux && *org == Some(peer_id.into()) => {}, @@ -93,7 +93,7 @@ fn import_single_good_known_block_is_ignored() { &mut client, BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Ok(BlockImportStatus::ImportedKnown(ref n, _)) if *n == number => {}, _ => panic!(), @@ -108,7 +108,7 @@ fn import_single_good_block_without_header_fails() { &mut substrate_test_runtime_client::new(), BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Err(BlockImportError::IncompleteHeader(ref org)) if *org == Some(peer_id.into()) => {}, _ => panic!(), diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index 6ac20842a33f82295de7680973f9af34554de93c..f84f353fb4a024ac57d080bf72c09ef2c31f15f5 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -34,7 +34,7 @@ use std::{ time::Duration, }; -use futures::{channel::oneshot, future::BoxFuture, pin_mut, prelude::*}; +use futures::{future::BoxFuture, pin_mut, prelude::*}; use libp2p::PeerId; use log::trace; use parking_lot::Mutex; @@ -67,7 +67,7 @@ use sc_network_sync::{ service::{network::NetworkServiceProvider, syncing_service::SyncingService}, state_request_handler::StateRequestHandler, strategy::warp::{ - AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncParams, WarpSyncProvider, + AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncConfig, WarpSyncProvider, }, warp_request_handler, }; @@ -217,7 +217,7 @@ impl BlockImport for PeersClient { } async fn import_block( - &mut self, + &self, block: BlockImportParams, ) -> Result { self.client.import_block(block).await @@ -266,7 +266,7 @@ where /// Returns the number of peers we're connected to. pub async fn num_peers(&self) -> usize { - self.sync_service.status().await.unwrap().num_connected_peers as usize + self.sync_service.num_connected_peers() } /// Returns the number of downloaded blocks. @@ -607,7 +607,7 @@ where } async fn import_block( - &mut self, + &self, block: BlockImportParams, ) -> Result { self.inner.import_block(block).await @@ -701,7 +701,7 @@ pub struct FullPeerConfig { /// Enable transaction indexing. pub storage_chain: bool, /// Optional target block header to sync to - pub target_block: Option<::Header>, + pub target_header: Option<::Header>, /// Force genesis even in case of warp & light state sync. pub force_genesis: bool, } @@ -865,13 +865,9 @@ pub trait TestNetFactory: Default + Sized + Send { let warp_sync = Arc::new(TestWarpSyncProvider(client.clone())); - let warp_sync_params = match config.target_block { - Some(target_block) => { - let (sender, receiver) = oneshot::channel::<::Header>(); - let _ = sender.send(target_block); - WarpSyncParams::WaitForTarget(receiver) - }, - _ => WarpSyncParams::WithProvider(warp_sync.clone()), + let warp_sync_config = match config.target_header { + Some(target_header) => WarpSyncConfig::WithTarget(target_header), + _ => WarpSyncConfig::WithProvider(warp_sync.clone()), }; let warp_protocol_config = { @@ -919,7 +915,7 @@ pub trait TestNetFactory: Default + Sized + Send { protocol_id.clone(), &fork_id, block_announce_validator, - Some(warp_sync_params), + Some(warp_sync_config), chain_sync_network_handle, import_queue.service(), block_relay_params.downloader, @@ -1020,7 +1016,7 @@ pub trait TestNetFactory: Default + Sized + Send { for peer in peers { if peer.sync_service.is_major_syncing() || - peer.sync_service.num_queued_blocks().await.unwrap() != 0 + peer.sync_service.status().await.unwrap().queued_blocks != 0 { return false } @@ -1040,7 +1036,7 @@ pub trait TestNetFactory: Default + Sized + Send { async fn is_idle(&mut self) -> bool { let peers = self.peers_mut(); for peer in peers { - if peer.sync_service.num_queued_blocks().await.unwrap() != 0 { + if peer.sync_service.status().await.unwrap().queued_blocks != 0 { return false } if peer.sync_service.num_sync_requests().await.unwrap() != 0 { @@ -1098,9 +1094,7 @@ pub trait TestNetFactory: Default + Sized + Send { 'outer: loop { for sync_service in &sync_services { - if sync_service.status().await.unwrap().num_connected_peers as usize != - num_peers - 1 - { + if sync_service.num_connected_peers() != num_peers - 1 { futures::future::poll_fn::<(), _>(|cx| { self.poll(cx); Poll::Ready(()) diff --git a/substrate/client/network/test/src/sync.rs b/substrate/client/network/test/src/sync.rs index f1c1b7414303202346a35aaf335d162997b504c3..4244c49bf7fb38a42339102836bc0c206d4d48de 100644 --- a/substrate/client/network/test/src/sync.rs +++ b/substrate/client/network/test/src/sync.rs @@ -1049,7 +1049,7 @@ async fn syncs_all_forks_from_single_peer() { }) .await; - if net.peer(1).sync_service().best_seen_block().await.unwrap() == Some(12) { + if net.peer(1).sync_service().status().await.unwrap().best_seen_block == Some(12) { break } } @@ -1298,7 +1298,7 @@ async fn warp_sync_to_target_block() { net.add_full_peer_with_config(FullPeerConfig { sync_mode: SyncMode::Warp, - target_block: Some(target_block), + target_header: Some(target_block), ..Default::default() }); diff --git a/substrate/client/network/transactions/src/lib.rs b/substrate/client/network/transactions/src/lib.rs index 31ad0781035e5bd0662a617dcb31502048bdea81..a241041968fd2c83eaabbc3a225aa4f675e75471 100644 --- a/substrate/client/network/transactions/src/lib.rs +++ b/substrate/client/network/transactions/src/lib.rs @@ -522,8 +522,14 @@ where return } - debug!(target: LOG_TARGET, "Propagating transactions"); let transactions = self.transaction_pool.transactions(); + + if transactions.is_empty() { + return + } + + debug!(target: LOG_TARGET, "Propagating transactions"); + let propagated_to = self.do_propagate_transactions(&transactions); self.transaction_pool.on_broadcasted(propagated_to); } diff --git a/substrate/client/offchain/src/lib.rs b/substrate/client/offchain/src/lib.rs index 48d3b8f1393a430b8f07031775e4d83b063400b2..7cee64e6ce7ef466f173f1e56fbd093bf8bbe0cd 100644 --- a/substrate/client/offchain/src/lib.rs +++ b/substrate/client/offchain/src/lib.rs @@ -481,7 +481,7 @@ mod tests { let (client, backend) = substrate_test_runtime_client::TestClientBuilder::new() .enable_offchain_indexing_api() .build_with_backend(); - let mut client = Arc::new(client); + let client = Arc::new(client); let offchain_db = backend.offchain_storage().unwrap(); let key = &b"hello"[..]; diff --git a/substrate/client/rpc-api/src/author/mod.rs b/substrate/client/rpc-api/src/author/mod.rs index cfc56f4130aba9527e9bb2b748f38314d6d35644..c39d6c68355ab3184bed4d9e81084c3fc84917ed 100644 --- a/substrate/client/rpc-api/src/author/mod.rs +++ b/substrate/client/rpc-api/src/author/mod.rs @@ -34,11 +34,11 @@ pub trait AuthorApi { async fn submit_extrinsic(&self, extrinsic: Bytes) -> Result; /// Insert a key into the keystore. - #[method(name = "author_insertKey")] + #[method(name = "author_insertKey", with_extensions)] fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<(), Error>; /// Generate new session keys and returns the corresponding public keys. - #[method(name = "author_rotateKeys")] + #[method(name = "author_rotateKeys", with_extensions)] fn rotate_keys(&self) -> Result; /// Checks if the keystore has private keys for the given session public keys. @@ -46,13 +46,13 @@ pub trait AuthorApi { /// `session_keys` is the SCALE encoded session keys object from the runtime. /// /// Returns `true` iff all private keys could be found. - #[method(name = "author_hasSessionKeys")] + #[method(name = "author_hasSessionKeys", with_extensions)] fn has_session_keys(&self, session_keys: Bytes) -> Result; /// Checks if the keystore has private keys for the given public key and key type. /// /// Returns `true` if a private key could be found. - #[method(name = "author_hasKey")] + #[method(name = "author_hasKey", with_extensions)] fn has_key(&self, public_key: Bytes, key_type: String) -> Result; /// Returns all pending extrinsics, potentially grouped by sender. @@ -60,7 +60,7 @@ pub trait AuthorApi { fn pending_extrinsics(&self) -> Result, Error>; /// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting. - #[method(name = "author_removeExtrinsic")] + #[method(name = "author_removeExtrinsic", with_extensions)] fn remove_extrinsic( &self, bytes_or_hash: Vec>, diff --git a/substrate/client/rpc-api/src/dev/mod.rs b/substrate/client/rpc-api/src/dev/mod.rs index 5bee6df73ba93e34d63f4533b57a2be64f056212..5c6208ff5d50e7c84cd061823a63025ea48bd7ed 100644 --- a/substrate/client/rpc-api/src/dev/mod.rs +++ b/substrate/client/rpc-api/src/dev/mod.rs @@ -59,6 +59,6 @@ pub trait DevApi { /// This function requires the specified block and its parent to be available /// at the queried node. If either the specified block or the parent is pruned, /// this function will return `None`. - #[method(name = "dev_getBlockStats")] + #[method(name = "dev_getBlockStats", with_extensions)] fn block_stats(&self, block_hash: Hash) -> Result, Error>; } diff --git a/substrate/client/rpc-api/src/lib.rs b/substrate/client/rpc-api/src/lib.rs index 451ebdf7fc00404bbd58abffef5a298cd9d6f310..cc580fc0e7cabea05ed1bb4627b47ea30037a6dd 100644 --- a/substrate/client/rpc-api/src/lib.rs +++ b/substrate/client/rpc-api/src/lib.rs @@ -25,7 +25,7 @@ mod error; mod policy; -pub use policy::{DenyUnsafe, UnsafeRpcError}; +pub use policy::{check_if_safe, DenyUnsafe, UnsafeRpcError}; pub mod author; pub mod chain; diff --git a/substrate/client/rpc-api/src/offchain/mod.rs b/substrate/client/rpc-api/src/offchain/mod.rs index 469e22d2b3faedf299c16e4809d8ddb046d8981d..4dd5b066d49fe90063d14a276a5ec2bf9731b8f9 100644 --- a/substrate/client/rpc-api/src/offchain/mod.rs +++ b/substrate/client/rpc-api/src/offchain/mod.rs @@ -28,10 +28,10 @@ use sp_core::{offchain::StorageKind, Bytes}; #[rpc(client, server)] pub trait OffchainApi { /// Set offchain local storage under given key and prefix. - #[method(name = "offchain_localStorageSet")] + #[method(name = "offchain_localStorageSet", with_extensions)] fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error>; /// Get offchain local storage under given key and prefix. - #[method(name = "offchain_localStorageGet")] + #[method(name = "offchain_localStorageGet", with_extensions)] fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result, Error>; } diff --git a/substrate/client/rpc-api/src/policy.rs b/substrate/client/rpc-api/src/policy.rs index c0847de89d2c613f904e3ff6e6db39cdc2c878fd..7e0657db8d1784ebc1b217c20e877dbe3a780623 100644 --- a/substrate/client/rpc-api/src/policy.rs +++ b/substrate/client/rpc-api/src/policy.rs @@ -23,6 +23,15 @@ use jsonrpsee::types::{error::ErrorCode, ErrorObject, ErrorObjectOwned}; +/// Checks if the RPC call is safe to be called externally. +pub fn check_if_safe(ext: &jsonrpsee::Extensions) -> Result<(), UnsafeRpcError> { + match ext.get::().map(|deny_unsafe| deny_unsafe.check_if_safe()) { + Some(Ok(())) => Ok(()), + Some(Err(e)) => Err(e), + None => unreachable!("DenyUnsafe extension is always set by the substrate rpc server; qed"), + } +} + /// Signifies whether a potentially unsafe RPC should be denied. #[derive(Clone, Copy, Debug)] pub enum DenyUnsafe { diff --git a/substrate/client/rpc-api/src/state/mod.rs b/substrate/client/rpc-api/src/state/mod.rs index e38e383c4c1523b42beef5f50e7852cc81290fb5..ee62387b6bd4b58d13379d94e01548d5adde9e35 100644 --- a/substrate/client/rpc-api/src/state/mod.rs +++ b/substrate/client/rpc-api/src/state/mod.rs @@ -48,7 +48,7 @@ pub trait StateApi { ) -> Result, Error>; /// Returns the keys with prefix, leave empty to get all the keys - #[method(name = "state_getPairs", blocking)] + #[method(name = "state_getPairs", blocking, with_extensions)] fn storage_pairs( &self, prefix: StorageKey, @@ -76,7 +76,7 @@ pub trait StateApi { fn storage_hash(&self, key: StorageKey, hash: Option) -> Result, Error>; /// Returns the size of a storage entry at a block's state. - #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])] + #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"], with_extensions)] async fn storage_size(&self, key: StorageKey, hash: Option) -> Result, Error>; @@ -95,7 +95,7 @@ pub trait StateApi { /// Subsequent values in the vector represent changes to the previous state (diffs). /// WARNING: The time complexity of this query is O(|keys|*dist(block, hash)), and the /// memory complexity is O(dist(block, hash)) -- use with caution. - #[method(name = "state_queryStorage", blocking)] + #[method(name = "state_queryStorage", blocking, with_extensions)] fn query_storage( &self, keys: Vec, @@ -136,6 +136,7 @@ pub trait StateApi { name = "state_subscribeStorage" => "state_storage", unsubscribe = "state_unsubscribeStorage", item = StorageChangeSet, + with_extensions, )] fn subscribe_storage(&self, keys: Option>); @@ -291,7 +292,7 @@ pub trait StateApi { /// /// If you are having issues with maximum payload size you can use the flag /// `-ltracing=trace` to get some logging during tracing. - #[method(name = "state_traceBlock", blocking)] + #[method(name = "state_traceBlock", blocking, with_extensions)] fn trace_block( &self, block: Hash, diff --git a/substrate/client/rpc-api/src/statement/mod.rs b/substrate/client/rpc-api/src/statement/mod.rs index 39ec52cbea01321c813d197f22dc406f42c23538..eee58f506e162540b60ed383a554eb766a433156 100644 --- a/substrate/client/rpc-api/src/statement/mod.rs +++ b/substrate/client/rpc-api/src/statement/mod.rs @@ -27,7 +27,7 @@ pub mod error; #[rpc(client, server)] pub trait StatementApi { /// Return all statements, SCALE-encoded. - #[method(name = "statement_dump")] + #[method(name = "statement_dump", with_extensions)] fn dump(&self) -> RpcResult>; /// Return the data of all known statements which include all topics and have no `DecryptionKey` diff --git a/substrate/client/rpc-api/src/system/mod.rs b/substrate/client/rpc-api/src/system/mod.rs index c38fa8f3d817606ce699b31b4f16c2a6e0c7e775..2d6d48e1ddb4d7e1acea39731faeafcfcec52023 100644 --- a/substrate/client/rpc-api/src/system/mod.rs +++ b/substrate/client/rpc-api/src/system/mod.rs @@ -69,7 +69,7 @@ pub trait SystemApi { async fn system_local_listen_addresses(&self) -> Result, Error>; /// Returns currently connected peers - #[method(name = "system_peers")] + #[method(name = "system_peers", with_extensions)] async fn system_peers(&self) -> Result>, Error>; /// Returns current state of the network. @@ -78,7 +78,7 @@ pub trait SystemApi { /// as its format might change at any time. // TODO: the future of this call is uncertain: https://github.com/paritytech/substrate/issues/1890 // https://github.com/paritytech/substrate/issues/5541 - #[method(name = "system_unstable_networkState")] + #[method(name = "system_unstable_networkState", with_extensions)] async fn system_network_state(&self) -> Result; /// Adds a reserved peer. Returns the empty string or an error. The string @@ -86,12 +86,12 @@ pub trait SystemApi { /// /// `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV` /// is an example of a valid, passing multiaddr with PeerId attached. - #[method(name = "system_addReservedPeer")] + #[method(name = "system_addReservedPeer", with_extensions)] async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error>; /// Remove a reserved peer. Returns the empty string or an error. The string /// should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`. - #[method(name = "system_removeReservedPeer")] + #[method(name = "system_removeReservedPeer", with_extensions)] async fn system_remove_reserved_peer(&self, peer_id: String) -> Result<(), Error>; /// Returns the list of reserved peers @@ -112,10 +112,10 @@ pub trait SystemApi { /// The syntax is identical to the CLI `=`: /// /// `sync=debug,state=trace` - #[method(name = "system_addLogFilter")] + #[method(name = "system_addLogFilter", with_extensions)] fn system_add_log_filter(&self, directives: String) -> Result<(), Error>; /// Resets the log filter to Substrate defaults - #[method(name = "system_resetLogFilter")] + #[method(name = "system_resetLogFilter", with_extensions)] fn system_reset_log_filter(&self) -> Result<(), Error>; } diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 7e3d3bf55b749973a3e34b071c38e0c9902db5e8..b39c3fdda67c0a8de74a0b805635e78697152847 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -16,21 +16,20 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +dyn-clone = { workspace = true } forwarded-header-value = { workspace = true } futures = { workspace = true } governor = { workspace = true } http = { workspace = true } http-body-util = { workspace = true } +hyper = { workspace = true } ip_network = { workspace = true } jsonrpsee = { features = ["server"], workspace = true } log = { workspace = true, default-features = true } prometheus-endpoint = { workspace = true, default-features = true } +sc-rpc-api = { workspace = true } serde = { workspace = true } serde_json = { workspace = true, default-features = true } tokio = { features = ["parking_lot"], workspace = true, default-features = true } tower = { workspace = true, features = ["util"] } tower-http = { workspace = true, features = ["cors"] } - -# Dependencies outside the polkadot-sdk workspace -# which requires hyper v1 -hyper = "1.3" diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index 0bae16b113dffeee893a82922c5b5db34a8c5a6a..0472a0a2f63cdf4c45e74ee10fa6b2888c026c78 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -23,246 +23,287 @@ pub mod middleware; pub mod utils; -use std::{error::Error as StdError, net::SocketAddr, num::NonZeroU32, sync::Arc, time::Duration}; +use std::{error::Error as StdError, net::SocketAddr, time::Duration}; use jsonrpsee::{ core::BoxError, server::{ - serve_with_graceful_shutdown, stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder, + serve_with_graceful_shutdown, stop_channel, ws, PingConfig, ServerHandle, StopHandle, }, Methods, RpcModule, }; use middleware::NodeHealthProxyLayer; -use tokio::net::TcpListener; use tower::Service; -use utils::{build_rpc_api, format_cors, get_proxy_ip, host_filtering, try_into_cors}; +use utils::{ + build_rpc_api, deny_unsafe, format_listen_addrs, get_proxy_ip, ListenAddrError, RpcSettings, +}; pub use ip_network::IpNetwork; pub use jsonrpsee::{ - core::{ - id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, - traits::IdProvider, - }, + core::id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, server::{middleware::rpc::RpcServiceBuilder, BatchRequestConfig}, }; pub use middleware::{Metrics, MiddlewareLayer, RpcMetrics}; +pub use utils::{RpcEndpoint, RpcMethods}; const MEGABYTE: u32 = 1024 * 1024; -/// Type alias for the JSON-RPC server. -pub type Server = jsonrpsee::server::ServerHandle; +/// 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: + jsonrpsee::core::traits::IdProvider + dyn_clone::DynClone +{ +} + +dyn_clone::clone_trait_object!(SubscriptionIdProvider); /// RPC server configuration. #[derive(Debug)] -pub struct Config<'a, M: Send + Sync + 'static> { - /// Socket addresses. - pub addrs: [SocketAddr; 2], - /// CORS. - pub cors: Option<&'a Vec>, - /// Maximum connections. - pub max_connections: u32, - /// Maximum subscriptions per connection. - pub max_subs_per_conn: u32, - /// Maximum rpc request payload size. - pub max_payload_in_mb: u32, - /// Maximum rpc response payload size. - pub max_payload_out_mb: u32, +pub struct Config { + /// RPC interfaces to start. + pub endpoints: Vec, /// Metrics. pub metrics: Option, - /// Message buffer size - pub message_buffer_capacity: u32, /// RPC API. pub rpc_api: RpcModule, /// Subscription ID provider. - pub id_provider: Option>, + pub id_provider: Option>, /// Tokio runtime handle. pub tokio_handle: tokio::runtime::Handle, - /// Batch request config. - pub batch_config: BatchRequestConfig, - /// Rate limit calls per minute. - pub rate_limit: Option, - /// Disable rate limit for certain ips. - pub rate_limit_whitelisted_ips: Vec, - /// Trust proxy headers for rate limiting. - pub rate_limit_trust_proxy_headers: bool, } #[derive(Debug, Clone)] -struct PerConnection { +struct PerConnection { methods: Methods, stop_handle: StopHandle, metrics: Option, tokio_handle: tokio::runtime::Handle, - service_builder: TowerServiceBuilder, - rate_limit_whitelisted_ips: Arc>, } /// Start RPC server listening on given address. -pub async fn start_server( - config: Config<'_, M>, -) -> Result> +pub async fn start_server(config: Config) -> Result> where M: Send + Sync, { - let Config { - addrs, - batch_config, - cors, - max_payload_in_mb, - max_payload_out_mb, - max_connections, - max_subs_per_conn, - metrics, - message_buffer_capacity, - id_provider, - tokio_handle, - rpc_api, - rate_limit, - rate_limit_whitelisted_ips, - rate_limit_trust_proxy_headers, - } = config; - - let listener = TcpListener::bind(addrs.as_slice()).await?; - let local_addr = listener.local_addr().ok(); - let host_filter = host_filtering(cors.is_some(), local_addr); - - let http_middleware = tower::ServiceBuilder::new() - .option_layer(host_filter) - // Proxy `GET /health, /health/readiness` requests to the internal `system_health` method. - .layer(NodeHealthProxyLayer::default()) - .layer(try_into_cors(cors)?); - - let mut builder = jsonrpsee::server::Server::builder() - .max_request_body_size(max_payload_in_mb.saturating_mul(MEGABYTE)) - .max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE)) - .max_connections(max_connections) - .max_subscriptions_per_connection(max_subs_per_conn) - .enable_ws_ping( - PingConfig::new() - .ping_interval(Duration::from_secs(30)) - .inactive_limit(Duration::from_secs(60)) - .max_failures(3), - ) - .set_http_middleware(http_middleware) - .set_message_buffer_capacity(message_buffer_capacity) - .set_batch_request_config(batch_config) - .custom_tokio_runtime(tokio_handle.clone()); - - if let Some(provider) = id_provider { - builder = builder.set_id_provider(provider); - } else { - builder = builder.set_id_provider(RandomStringIdProvider::new(16)); - }; + let Config { endpoints, metrics, tokio_handle, rpc_api, id_provider } = config; let (stop_handle, server_handle) = stop_channel(); let cfg = PerConnection { methods: build_rpc_api(rpc_api).into(), - service_builder: builder.to_service_builder(), metrics, tokio_handle: tokio_handle.clone(), stop_handle, - rate_limit_whitelisted_ips: Arc::new(rate_limit_whitelisted_ips), }; - tokio_handle.spawn(async move { - loop { - let (sock, remote_addr) = tokio::select! { - res = listener.accept() => { - match res { - Ok(s) => s, - Err(e) => { - log::debug!(target: "rpc", "Failed to accept ipv4 connection: {:?}", e); - continue; + let mut local_addrs = Vec::new(); + + for endpoint in endpoints { + let allowed_to_fail = endpoint.is_optional; + let local_addr = endpoint.listen_addr; + + let mut listener = match endpoint.bind().await { + Ok(l) => l, + Err(e) if allowed_to_fail => { + log::debug!(target: "rpc", "JSON-RPC server failed to bind optional address: {:?}, error: {:?}", local_addr, e); + continue; + }, + Err(e) => return Err(e), + }; + let local_addr = listener.local_addr(); + local_addrs.push(local_addr); + let cfg = cfg.clone(); + + let mut id_provider2 = id_provider.clone(); + + tokio_handle.spawn(async move { + loop { + let (sock, remote_addr, rpc_cfg) = tokio::select! { + res = listener.accept() => { + match res { + Ok(s) => s, + Err(e) => { + log::debug!(target: "rpc", "Failed to accept connection: {:?}", e); + continue; + } } } - } - _ = cfg.stop_handle.clone().shutdown() => break, - }; - - let ip = remote_addr.ip(); - let cfg2 = cfg.clone(); - let svc = tower::service_fn(move |req: http::Request| { - let PerConnection { - methods, - service_builder, - metrics, - tokio_handle, - stop_handle, - rate_limit_whitelisted_ips, - } = cfg2.clone(); - - let proxy_ip = - if rate_limit_trust_proxy_headers { get_proxy_ip(&req) } else { None }; - - let rate_limit_cfg = if rate_limit_whitelisted_ips - .iter() - .any(|ips| ips.contains(proxy_ip.unwrap_or(ip))) - { - log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is trusted, disabling rate-limit", proxy_ip); - None - } else { - if !rate_limit_whitelisted_ips.is_empty() { - log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is not trusted, rate-limit enabled", proxy_ip); - } - rate_limit + _ = cfg.stop_handle.clone().shutdown() => break, }; - let is_websocket = ws::is_upgrade_request(&req); - let transport_label = if is_websocket { "ws" } else { "http" }; - - let middleware_layer = match (metrics, rate_limit_cfg) { - (None, None) => None, - (Some(metrics), None) => Some( - MiddlewareLayer::new().with_metrics(Metrics::new(metrics, transport_label)), - ), - (None, Some(rate_limit)) => - Some(MiddlewareLayer::new().with_rate_limit_per_minute(rate_limit)), - (Some(metrics), Some(rate_limit)) => Some( - MiddlewareLayer::new() - .with_metrics(Metrics::new(metrics, transport_label)) - .with_rate_limit_per_minute(rate_limit), - ), + let RpcSettings { + batch_config, + max_connections, + max_payload_in_mb, + max_payload_out_mb, + max_buffer_capacity_per_connection, + max_subscriptions_per_connection, + rpc_methods, + rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips, + host_filter, + cors, + rate_limit, + } = rpc_cfg; + + let http_middleware = tower::ServiceBuilder::new() + .option_layer(host_filter) + // Proxy `GET /health, /health/readiness` requests to the internal + // `system_health` method. + .layer(NodeHealthProxyLayer::default()) + .layer(cors); + + let mut builder = jsonrpsee::server::Server::builder() + .max_request_body_size(max_payload_in_mb.saturating_mul(MEGABYTE)) + .max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE)) + .max_connections(max_connections) + .max_subscriptions_per_connection(max_subscriptions_per_connection) + .enable_ws_ping( + PingConfig::new() + .ping_interval(Duration::from_secs(30)) + .inactive_limit(Duration::from_secs(60)) + .max_failures(3), + ) + .set_http_middleware(http_middleware) + .set_message_buffer_capacity(max_buffer_capacity_per_connection) + .set_batch_request_config(batch_config) + .custom_tokio_runtime(cfg.tokio_handle.clone()) + .set_id_provider(RandomStringIdProvider::new(16)); + + if let Some(provider) = id_provider2.take() { + builder = builder.set_id_provider(provider); + } else { + builder = builder.set_id_provider(RandomStringIdProvider::new(16)); }; - let rpc_middleware = RpcServiceBuilder::new() - .rpc_logger(1024) - .option_layer(middleware_layer.clone()); - let mut svc = - service_builder.set_rpc_middleware(rpc_middleware).build(methods, stop_handle); - - async move { - if is_websocket { - let on_disconnect = svc.on_session_closed(); - - // Spawn a task to handle when the connection is closed. - tokio_handle.spawn(async move { - let now = std::time::Instant::now(); - middleware_layer.as_ref().map(|m| m.ws_connect()); - on_disconnect.await; - middleware_layer.as_ref().map(|m| m.ws_disconnect(now)); - }); - } - - // https://github.com/rust-lang/rust/issues/102211 the error type can't be inferred - // to be `Box` so we need to convert it to - // a concrete type as workaround. - svc.call(req).await.map_err(|e| BoxError::from(e)) - } - }); - - cfg.tokio_handle.spawn(serve_with_graceful_shutdown( - sock, - svc, - cfg.stop_handle.clone().shutdown(), - )); - } - }); - - log::info!( - "Running JSON-RPC server: addr={}, allowed origins={}", - local_addr.map_or_else(|| "unknown".to_string(), |a| a.to_string()), - format_cors(cors) - ); - - Ok(server_handle) + let service_builder = builder.to_service_builder(); + let deny_unsafe = deny_unsafe(&local_addr, &rpc_methods); + + let ip = remote_addr.ip(); + let cfg2 = cfg.clone(); + let service_builder2 = service_builder.clone(); + + let svc = + tower::service_fn(move |mut req: http::Request| { + req.extensions_mut().insert(deny_unsafe); + + let PerConnection { methods, metrics, tokio_handle, stop_handle } = + cfg2.clone(); + let service_builder = service_builder2.clone(); + + let proxy_ip = + if rate_limit_trust_proxy_headers { get_proxy_ip(&req) } else { None }; + + let rate_limit_cfg = if rate_limit_whitelisted_ips + .iter() + .any(|ips| ips.contains(proxy_ip.unwrap_or(ip))) + { + log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is trusted, disabling rate-limit", proxy_ip); + None + } else { + if !rate_limit_whitelisted_ips.is_empty() { + log::debug!(target: "rpc", "ip={ip}, proxy_ip={:?} is not trusted, rate-limit enabled", proxy_ip); + } + rate_limit + }; + + let is_websocket = ws::is_upgrade_request(&req); + let transport_label = if is_websocket { "ws" } else { "http" }; + + let middleware_layer = match (metrics, rate_limit_cfg) { + (None, None) => None, + (Some(metrics), None) => Some( + MiddlewareLayer::new() + .with_metrics(Metrics::new(metrics, transport_label)), + ), + (None, Some(rate_limit)) => + Some(MiddlewareLayer::new().with_rate_limit_per_minute(rate_limit)), + (Some(metrics), Some(rate_limit)) => Some( + MiddlewareLayer::new() + .with_metrics(Metrics::new(metrics, transport_label)) + .with_rate_limit_per_minute(rate_limit), + ), + }; + + let rpc_middleware = + RpcServiceBuilder::new().option_layer(middleware_layer.clone()); + let mut svc = service_builder + .set_rpc_middleware(rpc_middleware) + .build(methods, stop_handle); + + async move { + if is_websocket { + let on_disconnect = svc.on_session_closed(); + + // Spawn a task to handle when the connection is closed. + tokio_handle.spawn(async move { + let now = std::time::Instant::now(); + middleware_layer.as_ref().map(|m| m.ws_connect()); + on_disconnect.await; + middleware_layer.as_ref().map(|m| m.ws_disconnect(now)); + }); + } + + // https://github.com/rust-lang/rust/issues/102211 the error type can't be inferred + // to be `Box` so we need to + // convert it to a concrete type as workaround. + svc.call(req).await.map_err(|e| BoxError::from(e)) + } + }); + + cfg.tokio_handle.spawn(serve_with_graceful_shutdown( + sock, + svc, + cfg.stop_handle.clone().shutdown(), + )); + } + }); + } + + if local_addrs.is_empty() { + return Err(Box::new(ListenAddrError)); + } + + // The previous logging format was before + // `Running JSON-RPC server: addr=127.0.0.1:9944, allowed origins=["*"]` + // + // The new format is `Running JSON-RPC server: addr=` + // with the exception that for a single address it will be `Running JSON-RPC server: addr=addr,` + // with a trailing comma. + // + // This is to make it work with old scripts/utils that parse the logs. + log::info!("Running JSON-RPC server: addr={}", format_listen_addrs(&local_addrs)); + + Ok(Server::new(server_handle, local_addrs)) } diff --git a/substrate/client/rpc-servers/src/utils.rs b/substrate/client/rpc-servers/src/utils.rs index d9d943c7c1fb31fe09ed532cd219a537ebf01b52..d9b2db7af13351d248d72155cb6eb4fce4ee3866 100644 --- a/substrate/client/rpc-servers/src/utils.rs +++ b/substrate/client/rpc-servers/src/utils.rs @@ -18,29 +18,190 @@ //! Substrate RPC server utils. +use crate::BatchRequestConfig; use std::{ error::Error as StdError, net::{IpAddr, SocketAddr}, + num::NonZeroU32, str::FromStr, }; use forwarded_header_value::ForwardedHeaderValue; use http::header::{HeaderName, HeaderValue}; +use ip_network::IpNetwork; use jsonrpsee::{server::middleware::http::HostFilterLayer, RpcModule}; +use sc_rpc_api::DenyUnsafe; use tower_http::cors::{AllowOrigin, CorsLayer}; const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for"); const X_REAL_IP: HeaderName = HeaderName::from_static("x-real-ip"); const FORWARDED: HeaderName = HeaderName::from_static("forwarded"); -pub(crate) fn host_filtering(enabled: bool, addr: Option) -> Option { - // If the local_addr failed, fallback to wildcard. - let port = addr.map_or("*".to_string(), |p| p.port().to_string()); +#[derive(Debug)] +pub(crate) struct ListenAddrError; +impl std::error::Error for ListenAddrError {} + +impl std::fmt::Display for ListenAddrError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "No listen address was successfully bound") + } +} + +/// Available RPC methods. +#[derive(Debug, Copy, Clone)] +pub enum RpcMethods { + /// Allow only a safe subset of RPC methods. + Safe, + /// Expose every RPC method (even potentially unsafe ones). + Unsafe, + /// Automatically determine the RPC methods based on the connection. + Auto, +} + +impl Default for RpcMethods { + fn default() -> Self { + RpcMethods::Auto + } +} + +impl FromStr for RpcMethods { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "safe" => Ok(RpcMethods::Safe), + "unsafe" => Ok(RpcMethods::Unsafe), + "auto" => Ok(RpcMethods::Auto), + invalid => Err(format!("Invalid rpc methods {invalid}")), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RpcSettings { + pub(crate) batch_config: BatchRequestConfig, + pub(crate) max_connections: u32, + pub(crate) max_payload_in_mb: u32, + pub(crate) max_payload_out_mb: u32, + pub(crate) max_subscriptions_per_connection: u32, + pub(crate) max_buffer_capacity_per_connection: u32, + pub(crate) rpc_methods: RpcMethods, + pub(crate) rate_limit: Option, + pub(crate) rate_limit_trust_proxy_headers: bool, + pub(crate) rate_limit_whitelisted_ips: Vec, + pub(crate) cors: CorsLayer, + pub(crate) host_filter: Option, +} + +/// Represent a single RPC endpoint with its configuration. +#[derive(Debug, Clone)] +pub struct RpcEndpoint { + /// Listen address. + pub listen_addr: SocketAddr, + /// Batch request configuration. + pub batch_config: BatchRequestConfig, + /// Maximum number of connections. + pub max_connections: u32, + /// Maximum inbound payload size in MB. + pub max_payload_in_mb: u32, + /// Maximum outbound payload size in MB. + pub max_payload_out_mb: u32, + /// Maximum number of subscriptions per connection. + pub max_subscriptions_per_connection: u32, + /// Maximum buffer capacity per connection. + pub max_buffer_capacity_per_connection: u32, + /// Rate limit per minute. + pub rate_limit: Option, + /// Whether to trust proxy headers for rate limiting. + pub rate_limit_trust_proxy_headers: bool, + /// Whitelisted IPs for rate limiting. + pub rate_limit_whitelisted_ips: Vec, + /// CORS. + pub cors: Option>, + /// RPC methods to expose. + pub rpc_methods: RpcMethods, + /// Whether it's an optional listening address i.e, it's ignored if it fails to bind. + /// For example substrate tries to bind both ipv4 and ipv6 addresses but some platforms + /// may not support ipv6. + pub is_optional: bool, + /// Whether to retry with a random port if the provided port is already in use. + pub retry_random_port: bool, +} + +impl RpcEndpoint { + /// Binds to the listen address. + pub(crate) async fn bind(self) -> Result> { + let listener = match tokio::net::TcpListener::bind(self.listen_addr).await { + Ok(listener) => listener, + Err(_) if self.retry_random_port => { + let mut addr = self.listen_addr; + addr.set_port(0); + + tokio::net::TcpListener::bind(addr).await? + }, + Err(e) => return Err(e.into()), + }; + let local_addr = listener.local_addr()?; + let host_filter = host_filtering(self.cors.is_some(), local_addr); + let cors = try_into_cors(self.cors)?; + + Ok(Listener { + listener, + local_addr, + cfg: RpcSettings { + batch_config: self.batch_config, + max_connections: self.max_connections, + max_payload_in_mb: self.max_payload_in_mb, + max_payload_out_mb: self.max_payload_out_mb, + max_subscriptions_per_connection: self.max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.max_buffer_capacity_per_connection, + rpc_methods: self.rpc_methods, + rate_limit: self.rate_limit, + rate_limit_trust_proxy_headers: self.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rate_limit_whitelisted_ips, + host_filter, + cors, + }, + }) + } +} + +/// TCP socket server with RPC settings. +pub(crate) struct Listener { + listener: tokio::net::TcpListener, + local_addr: SocketAddr, + cfg: RpcSettings, +} + +impl Listener { + /// Accepts a new connection. + pub(crate) async fn accept( + &mut self, + ) -> std::io::Result<(tokio::net::TcpStream, SocketAddr, RpcSettings)> { + let (sock, remote_addr) = self.listener.accept().await?; + Ok((sock, remote_addr, self.cfg.clone())) + } + + /// Returns the local address the listener is bound to. + pub fn local_addr(&self) -> SocketAddr { + self.local_addr + } +} + +pub(crate) fn host_filtering(enabled: bool, addr: SocketAddr) -> Option { if enabled { // NOTE: The listening addresses are whitelisted by default. - let hosts = - [format!("localhost:{port}"), format!("127.0.0.1:{port}"), format!("[::1]:{port}")]; + + let mut hosts = Vec::new(); + + if addr.is_ipv4() { + hosts.push(format!("localhost:{}", addr.port())); + hosts.push(format!("127.0.0.1:{}", addr.port())); + } else { + hosts.push(format!("[::1]:{}", addr.port())); + } + Some(HostFilterLayer::new(hosts).expect("Valid hosts; qed")) } else { None @@ -65,13 +226,15 @@ pub(crate) fn build_rpc_api(mut rpc_api: RpcModule) } pub(crate) fn try_into_cors( - maybe_cors: Option<&Vec>, + maybe_cors: Option>, ) -> Result> { if let Some(cors) = maybe_cors { let mut list = Vec::new(); + for origin in cors { - list.push(HeaderValue::from_str(origin)?); + list.push(HeaderValue::from_str(&origin)?) } + Ok(CorsLayer::new().allow_origin(AllowOrigin::list(list))) } else { // allow all cors @@ -79,14 +242,6 @@ pub(crate) fn try_into_cors( } } -pub(crate) fn format_cors(maybe_cors: Option<&Vec>) -> String { - if let Some(cors) = maybe_cors { - format!("{:?}", cors) - } else { - format!("{:?}", ["*"]) - } -} - /// Extracts the IP addr from the HTTP request. /// /// It is extracted in the following order: @@ -126,6 +281,34 @@ pub(crate) fn get_proxy_ip(req: &http::Request) -> Option { None } +/// Get the `deny_unsafe` setting based on the address and the RPC methods exposed by the interface. +pub fn deny_unsafe(addr: &SocketAddr, methods: &RpcMethods) -> DenyUnsafe { + match (addr.ip().is_loopback(), methods) { + (_, RpcMethods::Unsafe) | (true, RpcMethods::Auto) => DenyUnsafe::No, + _ => DenyUnsafe::Yes, + } +} + +pub(crate) fn format_listen_addrs(addr: &[SocketAddr]) -> String { + let mut s = String::new(); + + let mut it = addr.iter().peekable(); + + while let Some(addr) = it.next() { + s.push_str(&addr.to_string()); + + if it.peek().is_some() { + s.push(','); + } + } + + if addr.len() == 1 { + s.push(','); + } + + s +} + #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 1ec7895e30886f7d74fa972d3b99b940aa1a051d..ae21895de38d19c1deb25f0103bf8a899d6fd72a 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { features = ["client-core", "macros", "server-core"], workspace = true } +jsonrpsee = { workspace = true, features = ["client-core", "macros", "server-core"] } # Internal chain structures for "chain_spec". sc-chain-spec = { workspace = true, default-features = true } # Pool for submitting extrinsics required by "transaction" @@ -45,7 +45,7 @@ rand = { workspace = true, default-features = true } schnellru = { workspace = true } [dev-dependencies] -jsonrpsee = { features = ["server", "ws-client"], workspace = true } +jsonrpsee = { workspace = true, features = ["server", "ws-client"] } serde_json = { workspace = true, default-features = true } tokio = { features = ["macros"], workspace = true, default-features = true } substrate-test-runtime-client = { workspace = true } diff --git a/substrate/client/rpc-spec-v2/src/archive/tests.rs b/substrate/client/rpc-spec-v2/src/archive/tests.rs index de71ed82a128845d98ef5b6f96e0896de1a71ef1..078016f5b3e210311c80c26fc6afaf587a1780dd 100644 --- a/substrate/client/rpc-spec-v2/src/archive/tests.rs +++ b/substrate/client/rpc-spec-v2/src/archive/tests.rs @@ -96,7 +96,7 @@ async fn archive_genesis() { #[tokio::test] async fn archive_body() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); // Invalid block hash. let invalid_hash = hex_string(&INVALID_HASH); @@ -130,7 +130,7 @@ async fn archive_body() { #[tokio::test] async fn archive_header() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); // Invalid block hash. let invalid_hash = hex_string(&INVALID_HASH); @@ -176,7 +176,7 @@ async fn archive_finalized_height() { #[tokio::test] async fn archive_hash_by_height() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); // Genesis height. let hashes: Vec = api.call("archive_unstable_hashByHeight", [0]).await.unwrap(); @@ -282,7 +282,7 @@ async fn archive_hash_by_height() { #[tokio::test] async fn archive_call() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); let invalid_hash = hex_string(&INVALID_HASH); // Invalid parameter (non-hex). @@ -341,7 +341,7 @@ async fn archive_call() { #[tokio::test] async fn archive_storage_hashes_values() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -431,7 +431,7 @@ async fn archive_storage_hashes_values() { #[tokio::test] async fn archive_storage_closest_merkle_value() { - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, MAX_QUERIED_LIMIT); /// The core of this test. /// @@ -592,7 +592,7 @@ async fn archive_storage_closest_merkle_value() { #[tokio::test] async fn archive_storage_paginate_iterations() { // 1 iteration allowed before pagination kicks in. - let (mut client, api) = setup_api(1, MAX_QUERIED_LIMIT); + let (client, api) = setup_api(1, MAX_QUERIED_LIMIT); // Import a new block with storage changes. let mut builder = BlockBuilderBuilder::new(&*client) @@ -787,7 +787,7 @@ async fn archive_storage_paginate_iterations() { #[tokio::test] async fn archive_storage_discarded_items() { // One query at a time - let (mut client, api) = setup_api(MAX_PAGINATION_LIMIT, 1); + let (client, api) = setup_api(MAX_PAGINATION_LIMIT, 1); // Import a new block with storage changes. let mut builder = BlockBuilderBuilder::new(&*client) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index 1d28d207124872e0a5c0ff9801775f5c1a5e3181..ebb72ed3d156b1ee39ab818865f5d9f4e34f8234 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -428,9 +428,12 @@ where /// Generates new block events from the given finalized hashes. /// /// It may be possible that the `Finalized` event fired before the `NewBlock` - /// event. In that case, for each finalized hash that was not reported yet - /// generate the `NewBlock` event. For the final finalized hash we must also - /// generate one `BestBlock` event. + /// event. Only in that case we generate: + /// - `NewBlock` event for all finalized hashes. + /// - `BestBlock` event for the last finalized hash. + /// + /// This function returns an empty list if all finalized hashes were already reported + /// and are pinned. fn generate_finalized_events( &mut self, finalized_block_hashes: &[Block::Hash], @@ -454,34 +457,33 @@ where } // Generate `NewBlock` events for all blocks beside the last block in the list - if i + 1 != finalized_block_hashes.len() { + let is_last = i + 1 == finalized_block_hashes.len(); + if !is_last { // Generate only the `NewBlock` event for this block. events.extend(self.generate_import_events(*hash, *parent, false)); - } else { + continue; + } + + if let Some(best_block_hash) = self.current_best_block { + let ancestor = + sp_blockchain::lowest_common_ancestor(&*self.client, *hash, best_block_hash)?; + // If we end up here and the `best_block` is a descendent of the finalized block // (last block in the list), it means that there were skipped notifications. - // Otherwise `pin_block` would had returned `true`. + // Otherwise `pin_block` would had returned `false`. // // When the node falls out of sync and then syncs up to the tip of the chain, it can // happen that we skip notifications. Then it is better to terminate the connection // instead of trying to send notifications for all missed blocks. - if let Some(best_block_hash) = self.current_best_block { - let ancestor = sp_blockchain::lowest_common_ancestor( - &*self.client, - *hash, - best_block_hash, - )?; - - if ancestor.hash == *hash { - return Err(SubscriptionManagementError::Custom( - "A descendent of the finalized block was already reported".into(), - )) - } + if ancestor.hash == *hash { + return Err(SubscriptionManagementError::Custom( + "A descendent of the finalized block was already reported".into(), + )) } - - // Let's generate the `NewBlock` and `NewBestBlock` events for the block. - events.extend(self.generate_import_events(*hash, *parent, true)) } + + // Let's generate the `NewBlock` and `NewBestBlock` events for the block. + events.extend(self.generate_import_events(*hash, *parent, true)) } Ok(events) @@ -549,39 +551,32 @@ where }); if let Some(current_best_block) = self.current_best_block { - // The best reported block is in the pruned list. Report a new best block. + // We need to generate a new best block if the best block is in the pruned list. let is_in_pruned_list = pruned_block_hashes.iter().any(|hash| *hash == current_best_block); - // The block is not the last finalized block. - // - // It can be either: - // - a descendant of the last finalized block - // - a block on a fork that will be pruned in the future. - // - // In those cases, we emit a new best block. - let is_not_last_finalized = current_best_block != last_finalized; - - if is_in_pruned_list || is_not_last_finalized { - // We need to generate a best block event. - let best_block_hash = self.client.info().best_hash; - - // Defensive check against state missmatch. - if best_block_hash == current_best_block { - // The client doest not have any new information about the best block. - // The information from `.info()` is updated from the DB as the last - // step of the finalization and it should be up to date. - // If the info is outdated, there is nothing the RPC can do for now. - debug!( - target: LOG_TARGET, - "[follow][id={:?}] Client does not contain different best block", - self.sub_id, - ); - } else { - // The RPC needs to also submit a new best block changed before the - // finalized event. - self.current_best_block = Some(best_block_hash); - events - .push(FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash })); + if is_in_pruned_list { + self.current_best_block = Some(last_finalized); + events.push(FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: last_finalized, + })); + } else { + // The pruning logic ensures that when the finalized block is announced, + // all blocks on forks that have the common ancestor lower or equal + // to the finalized block are reported. + // + // However, we double check if the best block is a descendant of the last finalized + // block to ensure we don't miss any events. + let ancestor = sp_blockchain::lowest_common_ancestor( + &*self.client, + last_finalized, + current_best_block, + )?; + let is_descendant = ancestor.hash == last_finalized; + if !is_descendant { + self.current_best_block = Some(last_finalized); + events.push(FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: last_finalized, + })); } } } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs index d4d616f54dc88c9909cb8bfadc720da01afd2030..14325b4fbb9807d25b7292aa143cbdb2840d0753 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs @@ -890,7 +890,7 @@ mod tests { } fn produce_blocks( - mut client: Arc>>, + client: Arc>>, num_blocks: usize, ) -> Vec<::Hash> { let mut blocks = Vec::with_capacity(num_blocks); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs index ab5be1f24e5d85b1be51a0ad1b3d42c3dbcbf730..073ee34a79f3f9ef260c49f95d77ad1b2cd953e5 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs @@ -69,7 +69,7 @@ impl ChainHeadMockClient { } } - pub async fn trigger_finality_stream(&self, header: Header) { + pub async fn trigger_finality_stream(&self, header: Header, stale_heads: Vec) { // Ensure the client called the `finality_notification_stream`. while self.finality_sinks.lock().is_empty() { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; @@ -77,11 +77,8 @@ impl ChainHeadMockClient { // Build the notification. let (sink, _stream) = tracing_unbounded("test_sink", 100_000); - let summary = FinalizeSummary { - header: header.clone(), - finalized: vec![header.hash()], - stale_heads: vec![], - }; + let summary = + FinalizeSummary { header: header.clone(), finalized: vec![header.hash()], stale_heads }; let notification = FinalityNotification::from_summary(summary, sink); for sink in self.finality_sinks.lock().iter_mut() { diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index b195e05b6649abb524c973b350b52833a34fd794..30a01b93b315120efbb0f88f623cce5eef55ae69 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -137,7 +137,7 @@ async fn setup_api() -> ( CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -186,7 +186,7 @@ async fn setup_api() -> ( } async fn import_block( - mut client: Arc>, + client: Arc>, parent_hash: ::Hash, parent_number: u64, ) -> Block { @@ -203,7 +203,7 @@ async fn import_block( } async fn import_best_block_with_tx( - mut client: Arc>, + client: Arc>, parent_hash: ::Hash, parent_number: u64, tx: Transfer, @@ -245,7 +245,7 @@ macro_rules! check_new_and_best_block_events { async fn follow_subscription_produces_blocks() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -316,7 +316,7 @@ async fn follow_subscription_produces_blocks() { async fn follow_with_runtime() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -346,7 +346,7 @@ async fn follow_with_runtime() { [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ - [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); @@ -469,7 +469,7 @@ async fn get_header() { #[tokio::test] async fn get_body() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let block_hash = format!("{:?}", block.header.hash()); let invalid_hash = hex_string(&INVALID_HASH); @@ -626,7 +626,7 @@ async fn call_runtime() { async fn call_runtime_without_flag() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -691,7 +691,7 @@ async fn call_runtime_without_flag() { #[tokio::test] async fn get_storage_hash() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let block_hash = format!("{:?}", block.header.hash()); let invalid_hash = hex_string(&INVALID_HASH); let key = hex_string(&KEY); @@ -835,7 +835,7 @@ async fn get_storage_hash() { #[tokio::test] async fn get_storage_multi_query_iter() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let key = hex_string(&KEY); // Import a new block with storage changes. @@ -959,7 +959,7 @@ async fn get_storage_multi_query_iter() { #[tokio::test] async fn get_storage_value() { - let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; + let (client, api, mut block_sub, sub_id, block) = setup_api().await; let block_hash = format!("{:?}", block.hash()); let invalid_hash = hex_string(&INVALID_HASH); let key = hex_string(&KEY); @@ -1287,7 +1287,7 @@ async fn unique_operation_ids() { async fn separate_operation_ids_for_subscriptions() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1375,7 +1375,7 @@ async fn separate_operation_ids_for_subscriptions() { async fn follow_generates_initial_blocks() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1533,7 +1533,7 @@ async fn follow_generates_initial_blocks() { async fn follow_exceeding_pinned_blocks() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1612,7 +1612,7 @@ async fn follow_exceeding_pinned_blocks() { async fn follow_with_unpin() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1720,7 +1720,7 @@ async fn follow_with_unpin() { async fn unpin_duplicate_hashes() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1825,7 +1825,7 @@ async fn unpin_duplicate_hashes() { async fn follow_with_multiple_unpin_hashes() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -1972,7 +1972,7 @@ async fn follow_with_multiple_unpin_hashes() { async fn follow_prune_best_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -2160,7 +2160,7 @@ async fn follow_prune_best_block() { async fn follow_forks_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -2322,7 +2322,7 @@ async fn follow_forks_pruned_block() { async fn follow_report_multiple_pruned_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let api = ChainHead::new( client.clone(), @@ -2559,7 +2559,7 @@ async fn pin_block_references() { ) .unwrap(); - let mut client = Arc::new( + let client = Arc::new( new_in_mem::<_, Block, _, RuntimeApi>( backend.clone(), executor, @@ -2705,7 +2705,7 @@ async fn pin_block_references() { async fn follow_finalized_before_new_block() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); @@ -2743,7 +2743,7 @@ async fn follow_finalized_before_new_block() { // expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events. // Trigger the Finalized notification before the NewBlock one. - run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone())).await; + run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone(), vec![])).await; // Initialized must always be reported first. let finalized_hash = client.info().finalized_hash; @@ -2823,7 +2823,7 @@ async fn ensure_operation_limits_works() { CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead with maximum 1 ongoing operations. let api = ChainHead::new( @@ -2930,7 +2930,7 @@ async fn check_continue_operation() { CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead with maximum 1 item before asking for pagination. let api = ChainHead::new( @@ -3115,7 +3115,7 @@ async fn stop_storage_operation() { CHILD_VALUE.to_vec(), ); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead with maximum 1 item before asking for pagination. let api = ChainHead::new( @@ -3221,7 +3221,7 @@ async fn stop_storage_operation() { #[tokio::test] async fn storage_closest_merkle_value() { - let (mut client, api, mut sub, sub_id, block) = setup_api().await; + let (client, api, mut sub, sub_id, block) = setup_api().await; /// The core of this test. /// @@ -3414,7 +3414,7 @@ async fn storage_closest_merkle_value() { async fn chain_head_stop_all_subscriptions() { let builder = TestClientBuilder::new(); let backend = builder.backend(); - let mut client = Arc::new(builder.build()); + let client = Arc::new(builder.build()); // Configure the chainHead to stop all subscriptions on lagging distance of 5 blocks. let api = ChainHead::new( @@ -3833,3 +3833,222 @@ async fn follow_unique_pruned_blocks() { }); assert_eq!(event, expected); } + +#[tokio::test] +async fn follow_report_best_block_of_a_known_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let api = ChainHead::new( + client_mock.clone(), + backend, + Arc::new(TaskExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_lagging_distance: MAX_LAGGING_DISTANCE, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + }, + ) + .into_rpc(); + + let finalized_hash = client.info().finalized_hash; + let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap(); + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], + finalized_block_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> block 1 -> block 2 + // ^^^ best block reported + // + // -> block 1 -> block 2_f -> block 3 (best) + // ^^^ finalized + + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + let block_2_f = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_2_f_hash = block_2_f.hash(); + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); + + // Import block 2 as best on the fork. + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); + // This push is required as otherwise block 3 has the same hash as block 2 and won't get + // imported + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_2 = block_builder.build().unwrap().block; + let block_2_hash = block_2.header.hash(); + client.import_as_best(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + run_with_timeout(client_mock.trigger_import_stream(block_1.header.clone())).await; + run_with_timeout(client_mock.trigger_import_stream(block_2_f.header.clone())).await; + run_with_timeout(client_mock.trigger_import_stream(block_2.header.clone())).await; + + // Check block 1. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_1_hash), + parent_block_hash: format!("{:?}", finalized_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_1_hash), + }); + assert_eq!(event, expected); + + // Check block 2. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_f_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_f_hash), + }); + assert_eq!(event, expected); + + // Check block 2, that we imported as custom best. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_hash), + }); + assert_eq!(event, expected); + + // Craft block 3 and import it later to simulate a race condition. + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_f_hash) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_3_hash = block_3.hash(); + + // Set best block info to block 3, that is not announced yet. + // + // This simulates the following edge-case: + // - The client imports a new block as best block. + // - The finality stream is triggered before the block is announced. + // + // This generated in the past a `BestBlock` event for the block that was not announced + // by `NewBlock` events. + // + // This happened because the chainHead was using the `client.info()` without verifying + // if the block was announced or not. This was fixed by using the latest finalized + // block instead as fallback. For more info see: https://github.com/paritytech/polkadot-sdk/issues/5512. + client_mock.set_best_block(block_3_hash, 3); + + // Finalize the block 2 from the fork. + client.finalize_block(block_2_f_hash, None).unwrap(); + run_with_timeout( + client_mock.trigger_finality_stream(block_2_f.header.clone(), vec![block_2_hash]), + ) + .await; + + // Block 2f is now the best block, not the block 3 that is not announced yet. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_f_hash), + }); + assert_eq!(event, expected); + // Block 2 must be reported as pruned, even if it was the previous best. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![ + // Note: the client mock is only reporting one block at a time. + // format!("{:?}", block_1_hash), + format!("{:?}", block_2_f_hash), + ], + pruned_block_hashes: vec![format!("{:?}", block_2_hash)], + }); + assert_eq!(event, expected); + + // Block 3 is now imported as best. + client.import_as_best(BlockOrigin::Own, block_3.clone()).await.unwrap(); + run_with_timeout(client_mock.trigger_import_stream(block_3.header.clone())).await; + + // Check block 3. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_3_hash), + parent_block_hash: format!("{:?}", block_2_f_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_3_hash), + }); + assert_eq!(event, expected); + + // Pruned hash can be unpinned. + let sub_id = sub.subscription_id(); + let sub_id = serde_json::to_string(&sub_id).unwrap(); + let hash = format!("{:?}", block_2_hash); + let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &hash]).await.unwrap(); + + // Finalize the block 3. + client.finalize_block(block_3_hash, None).unwrap(); + run_with_timeout(client_mock.trigger_finality_stream(block_3.header.clone(), vec![])).await; + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![format!("{:?}", block_3_hash)], + pruned_block_hashes: vec![], + }); + assert_eq!(event, expected); +} diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index 4a8f4b3ec630a624aece7962d02a863096bf74fa..6fe28a3873e9ac62c086858c4c71b0ccd8bf2589 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -43,7 +43,6 @@ sp-statement-store = { workspace = true, default-features = true } tokio = { workspace = true, default-features = true } [dev-dependencies] -sp-tracing = { workspace = true } assert_matches = { workspace = true } sc-block-builder = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } @@ -55,7 +54,6 @@ tokio = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } substrate-test-runtime-client = { workspace = true } pretty_assertions = { workspace = true } -tracing-subscriber = { features = ["env-filter"], workspace = true } [features] test-helpers = [] diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs index 2fc21a238bc996fe055f4baed5ca468ba950e57f..731f4df2f6f3eebfc5d3fa93bc1e46f76904e8b8 100644 --- a/substrate/client/rpc/src/author/mod.rs +++ b/substrate/client/rpc/src/author/mod.rs @@ -30,8 +30,8 @@ use crate::{ use codec::{Decode, Encode}; use futures::TryFutureExt; -use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink}; -use sc_rpc_api::DenyUnsafe; +use jsonrpsee::{core::async_trait, types::ErrorObject, Extensions, PendingSubscriptionSink}; +use sc_rpc_api::check_if_safe; use sc_transaction_pool_api::{ error::IntoPoolError, BlockHash, InPoolTransaction, TransactionFor, TransactionPool, TransactionSource, TxHash, @@ -55,8 +55,6 @@ pub struct Author { pool: Arc

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

, keystore: KeystorePtr, - deny_unsafe: DenyUnsafe, executor: SubscriptionTaskExecutor, ) -> Self { - Author { client, pool, keystore, deny_unsafe, executor } + Author { client, pool, keystore, executor } } } @@ -104,8 +101,14 @@ where }) } - fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()> { - self.deny_unsafe.check_if_safe()?; + fn insert_key( + &self, + ext: &Extensions, + key_type: String, + suri: String, + public: Bytes, + ) -> Result<()> { + check_if_safe(ext)?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; self.keystore @@ -114,8 +117,8 @@ where Ok(()) } - fn rotate_keys(&self) -> Result { - self.deny_unsafe.check_if_safe()?; + fn rotate_keys(&self, ext: &Extensions) -> Result { + check_if_safe(ext)?; let best_block_hash = self.client.info().best_hash; let mut runtime_api = self.client.runtime_api(); @@ -128,8 +131,8 @@ where .map_err(|api_err| Error::Client(Box::new(api_err)).into()) } - fn has_session_keys(&self, session_keys: Bytes) -> Result { - self.deny_unsafe.check_if_safe()?; + fn has_session_keys(&self, ext: &Extensions, session_keys: Bytes) -> Result { + check_if_safe(ext)?; let best_block_hash = self.client.info().best_hash; let keys = self @@ -142,8 +145,8 @@ where Ok(self.keystore.has_keys(&keys)) } - fn has_key(&self, public_key: Bytes, key_type: String) -> Result { - self.deny_unsafe.check_if_safe()?; + fn has_key(&self, ext: &Extensions, public_key: Bytes, key_type: String) -> Result { + check_if_safe(ext)?; let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; Ok(self.keystore.has_keys(&[(public_key.to_vec(), key_type)])) @@ -155,9 +158,10 @@ where fn remove_extrinsic( &self, + ext: &Extensions, bytes_or_hash: Vec>>, ) -> Result>> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; let hashes = bytes_or_hash .into_iter() .map(|x| match x { diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs index 6bcb3e7863c08d4cb4da1be529ffa83072f57962..bde60960eaf45a416195a8558bf759ed59cd3f2f 100644 --- a/substrate/client/rpc/src/author/tests.rs +++ b/substrate/client/rpc/src/author/tests.rs @@ -22,6 +22,7 @@ use crate::testing::{test_executor, timeout_secs}; use assert_matches::assert_matches; use codec::Encode; use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError, RpcModule}; +use sc_rpc_api::DenyUnsafe; use sc_transaction_pool::{BasicPool, FullChainApi}; use sc_transaction_pool_api::TransactionStatus; use sp_core::{ @@ -72,26 +73,27 @@ impl Default for TestSetup { } impl TestSetup { - fn author(&self) -> Author> { - Author { + fn to_rpc(&self) -> RpcModule>> { + let mut module = Author { client: self.client.clone(), pool: self.pool.clone(), keystore: self.keystore.clone(), - deny_unsafe: DenyUnsafe::No, executor: test_executor(), } + .into_rpc(); + module.extensions_mut().insert(DenyUnsafe::No); + module } fn into_rpc() -> RpcModule>> { - Self::default().author().into_rpc() + Self::default().to_rpc() } } #[tokio::test] async fn author_submit_transaction_should_not_cause_error() { - sp_tracing::init_for_tests(); - let author = TestSetup::default().author(); - let api = author.into_rpc(); + let api = TestSetup::into_rpc(); + let xt: Bytes = uxt(AccountKeyring::Alice, 1).encode().into(); let extrinsic_hash: H256 = blake2_256(&xt).into(); let response: H256 = api.call("author_submitExtrinsic", [xt.clone()]).await.unwrap(); @@ -179,7 +181,7 @@ async fn author_should_return_pending_extrinsics() { async fn author_should_remove_extrinsics() { const METHOD: &'static str = "author_removeExtrinsic"; let setup = TestSetup::default(); - let api = setup.author().into_rpc(); + let api = setup.to_rpc(); // Submit three extrinsics, then remove two of them (will cause the third to be removed as well, // having a higher nonce) @@ -214,7 +216,7 @@ async fn author_should_remove_extrinsics() { #[tokio::test] async fn author_should_insert_key() { let setup = TestSetup::default(); - let api = setup.author().into_rpc(); + let api = setup.to_rpc(); let suri = "//Alice"; let keypair = ed25519::Pair::from_string(suri, None).expect("generates keypair"); let params: (String, String, Bytes) = ( @@ -231,7 +233,7 @@ async fn author_should_insert_key() { #[tokio::test] async fn author_should_rotate_keys() { let setup = TestSetup::default(); - let api = setup.author().into_rpc(); + let api = setup.to_rpc(); let new_pubkeys: Bytes = api.call("author_rotateKeys", EmptyParams::new()).await.unwrap(); let session_keys = @@ -244,7 +246,6 @@ async fn author_should_rotate_keys() { #[tokio::test] async fn author_has_session_keys() { - // Setup let api = TestSetup::into_rpc(); // Add a valid session key @@ -255,7 +256,7 @@ async fn author_has_session_keys() { // Add a session key in a different keystore let non_existent_pubkeys: Bytes = { - let api2 = TestSetup::default().author().into_rpc(); + let api2 = TestSetup::into_rpc(); api2.call("author_rotateKeys", EmptyParams::new()) .await .expect("Rotates the keys") @@ -279,8 +280,6 @@ async fn author_has_session_keys() { #[tokio::test] async fn author_has_key() { - sp_tracing::init_for_tests(); - let api = TestSetup::into_rpc(); let suri = "//Alice"; let alice_keypair = ed25519::Pair::from_string(suri, None).expect("Generates keypair"); diff --git a/substrate/client/rpc/src/chain/tests.rs b/substrate/client/rpc/src/chain/tests.rs index afb81f709f7d38b60ddc40c5cee6c2acaf642af5..9d78aa88091d768efa91856e6746136bae1943c5 100644 --- a/substrate/client/rpc/src/chain/tests.rs +++ b/substrate/client/rpc/src/chain/tests.rs @@ -72,7 +72,7 @@ async fn should_return_header() { #[tokio::test] async fn should_return_a_block() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); let block = BlockBuilderBuilder::new(&*client) @@ -137,7 +137,7 @@ async fn should_return_a_block() { #[tokio::test] async fn should_return_block_hash() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); let res: ListOrValue> = @@ -204,7 +204,7 @@ async fn should_return_block_hash() { #[tokio::test] async fn should_return_finalized_hash() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); let res: H256 = api.call("chain_getFinalizedHead", EmptyParams::new()).await.unwrap(); @@ -248,7 +248,7 @@ async fn should_notify_about_finalized_block() { } async fn test_head_subscription(method: &str) { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let mut sub = { let api = new_full(client.clone(), test_executor()).into_rpc(); diff --git a/substrate/client/rpc/src/dev/mod.rs b/substrate/client/rpc/src/dev/mod.rs index 424ef72b694e23210cd21df20b78c0aae2433010..3ccda760c3f5965d40c1a9dbbeb615d2dea93538 100644 --- a/substrate/client/rpc/src/dev/mod.rs +++ b/substrate/client/rpc/src/dev/mod.rs @@ -22,8 +22,9 @@ #[cfg(test)] mod tests; +use jsonrpsee::Extensions; use sc_client_api::{BlockBackend, HeaderBackend}; -use sc_rpc_api::{dev::error::Error, DenyUnsafe}; +use sc_rpc_api::{check_if_safe, dev::error::Error}; use sp_api::{ApiExt, Core, ProvideRuntimeApi}; use sp_core::Encode; use sp_runtime::{ @@ -42,14 +43,13 @@ type HasherOf = <::Header as Header>::Hashing; /// The Dev API. All methods are unsafe. pub struct Dev { client: Arc, - deny_unsafe: DenyUnsafe, _phantom: PhantomData, } impl Dev { /// Create a new Dev API. - pub fn new(client: Arc, deny_unsafe: DenyUnsafe) -> Self { - Self { client, deny_unsafe, _phantom: PhantomData::default() } + pub fn new(client: Arc) -> Self { + Self { client, _phantom: PhantomData::default() } } } @@ -64,8 +64,12 @@ where + 'static, Client::Api: Core, { - fn block_stats(&self, hash: Block::Hash) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + fn block_stats( + &self, + ext: &Extensions, + hash: Block::Hash, + ) -> Result, Error> { + check_if_safe(ext)?; let block = { let block = self.client.block(hash).map_err(|e| Error::BlockQueryError(Box::new(e)))?; diff --git a/substrate/client/rpc/src/dev/tests.rs b/substrate/client/rpc/src/dev/tests.rs index e8f9ba4990d255789cda8c92c17f71cca924399f..ff691af7de414133d0f0be8f404997ff9fc9f5a6 100644 --- a/substrate/client/rpc/src/dev/tests.rs +++ b/substrate/client/rpc/src/dev/tests.rs @@ -17,6 +17,7 @@ // along with this program. If not, see . use super::*; +use crate::DenyUnsafe; use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; @@ -24,8 +25,9 @@ use substrate_test_runtime_client::{prelude::*, runtime::Block}; #[tokio::test] async fn block_stats_work() { - let mut client = Arc::new(substrate_test_runtime_client::new()); - let api = >::new(client.clone(), DenyUnsafe::No).into_rpc(); + let client = Arc::new(substrate_test_runtime_client::new()); + let mut api = >::new(client.clone()).into_rpc(); + api.extensions_mut().insert(DenyUnsafe::No); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -76,8 +78,9 @@ async fn block_stats_work() { #[tokio::test] async fn deny_unsafe_works() { - let mut client = Arc::new(substrate_test_runtime_client::new()); - let api = >::new(client.clone(), DenyUnsafe::Yes).into_rpc(); + let client = Arc::new(substrate_test_runtime_client::new()); + let mut api = >::new(client.clone()).into_rpc(); + api.extensions_mut().insert(DenyUnsafe::Yes); let block = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().genesis_hash) @@ -101,6 +104,6 @@ async fn deny_unsafe_works() { assert_eq!( resp, - r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"# + r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"RPC call is unsafe to be called externally"}}"# ); } diff --git a/substrate/client/rpc/src/lib.rs b/substrate/client/rpc/src/lib.rs index b40d0341e3212e6955d4d3f37db16f9c444626a3..33e4ac2f4c89a086a231c42407a7d733f52d51cf 100644 --- a/substrate/client/rpc/src/lib.rs +++ b/substrate/client/rpc/src/lib.rs @@ -22,12 +22,9 @@ #![warn(missing_docs)] -pub use jsonrpsee::core::{ - id_providers::{ - RandomIntegerIdProvider as RandomIntegerSubscriptionId, - RandomStringIdProvider as RandomStringSubscriptionId, - }, - traits::IdProvider as RpcSubscriptionIdProvider, +pub use jsonrpsee::core::id_providers::{ + RandomIntegerIdProvider as RandomIntegerSubscriptionId, + RandomStringIdProvider as RandomStringSubscriptionId, }; pub use sc_rpc_api::DenyUnsafe; diff --git a/substrate/client/rpc/src/offchain/mod.rs b/substrate/client/rpc/src/offchain/mod.rs index 66167386605305d2fa93fa877ab8f2c831aa7172..af6bc1ba58c8fdce53679465f204332d6a59f66b 100644 --- a/substrate/client/rpc/src/offchain/mod.rs +++ b/substrate/client/rpc/src/offchain/mod.rs @@ -22,11 +22,11 @@ mod tests; use self::error::Error; -use jsonrpsee::core::async_trait; +use jsonrpsee::{core::async_trait, Extensions}; use parking_lot::RwLock; +use sc_rpc_api::check_if_safe; /// Re-export the API for backward compatibility. pub use sc_rpc_api::offchain::*; -use sc_rpc_api::DenyUnsafe; use sp_core::{ offchain::{OffchainStorage, StorageKind}, Bytes, @@ -38,20 +38,25 @@ use std::sync::Arc; pub struct Offchain { /// Offchain storage storage: Arc>, - deny_unsafe: DenyUnsafe, } impl Offchain { /// Create new instance of Offchain API. - pub fn new(storage: T, deny_unsafe: DenyUnsafe) -> Self { - Offchain { storage: Arc::new(RwLock::new(storage)), deny_unsafe } + pub fn new(storage: T) -> Self { + Offchain { storage: Arc::new(RwLock::new(storage)) } } } #[async_trait] impl OffchainApiServer for Offchain { - fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + fn set_local_storage( + &self, + ext: &Extensions, + kind: StorageKind, + key: Bytes, + value: Bytes, + ) -> Result<(), Error> { + check_if_safe(ext)?; let prefix = match kind { StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, @@ -61,8 +66,13 @@ impl OffchainApiServer for Offchain { Ok(()) } - fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + fn get_local_storage( + &self, + ext: &Extensions, + kind: StorageKind, + key: Bytes, + ) -> Result, Error> { + check_if_safe(ext)?; let prefix = match kind { StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX, diff --git a/substrate/client/rpc/src/offchain/tests.rs b/substrate/client/rpc/src/offchain/tests.rs index 7758fac7fabdbb8168d18f66fe93c9bf801f7bba..41f22c2dc964227cb2989dd10a8322e49ef54103 100644 --- a/substrate/client/rpc/src/offchain/tests.rs +++ b/substrate/client/rpc/src/offchain/tests.rs @@ -17,22 +17,25 @@ // along with this program. If not, see . use super::*; +use crate::testing::{allow_unsafe, deny_unsafe}; use assert_matches::assert_matches; use sp_core::{offchain::storage::InMemOffchainStorage, Bytes}; #[test] fn local_storage_should_work() { let storage = InMemOffchainStorage::default(); - let offchain = Offchain::new(storage, DenyUnsafe::No); + let offchain = Offchain::new(storage); let key = Bytes(b"offchain_storage".to_vec()); let value = Bytes(b"offchain_value".to_vec()); + let ext = allow_unsafe(); + assert_matches!( - offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()), + offchain.set_local_storage(&ext, StorageKind::PERSISTENT, key.clone(), value.clone()), Ok(()) ); assert_matches!( - offchain.get_local_storage(StorageKind::PERSISTENT, key), + offchain.get_local_storage(&ext, StorageKind::PERSISTENT, key), Ok(Some(ref v)) if *v == value ); } @@ -40,18 +43,20 @@ fn local_storage_should_work() { #[test] fn offchain_calls_considered_unsafe() { let storage = InMemOffchainStorage::default(); - let offchain = Offchain::new(storage, DenyUnsafe::Yes); + let offchain = Offchain::new(storage); let key = Bytes(b"offchain_storage".to_vec()); let value = Bytes(b"offchain_value".to_vec()); + let ext = deny_unsafe(); + assert_matches!( - offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()), + offchain.set_local_storage(&ext, StorageKind::PERSISTENT, key.clone(), value.clone()), Err(Error::UnsafeRpcCalled(e)) => { assert_eq!(e.to_string(), "RPC call is unsafe to be called externally") } ); assert_matches!( - offchain.get_local_storage(StorageKind::PERSISTENT, key), + offchain.get_local_storage(&ext, StorageKind::PERSISTENT, key), Err(Error::UnsafeRpcCalled(e)) => { assert_eq!(e.to_string(), "RPC call is unsafe to be called externally") } diff --git a/substrate/client/rpc/src/state/mod.rs b/substrate/client/rpc/src/state/mod.rs index c9a41e25eda8bec3d9b2e7868248699bb2ae984c..d8989b3e1bee73eab92b03adb0c5f667ece14093 100644 --- a/substrate/client/rpc/src/state/mod.rs +++ b/substrate/client/rpc/src/state/mod.rs @@ -25,11 +25,11 @@ mod utils; mod tests; use crate::SubscriptionTaskExecutor; -use jsonrpsee::{core::async_trait, PendingSubscriptionSink}; +use jsonrpsee::{core::async_trait, Extensions, PendingSubscriptionSink}; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider, }; -use sc_rpc_api::DenyUnsafe; +use sc_rpc_api::{check_if_safe, DenyUnsafe}; use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi}; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_core::{ @@ -164,7 +164,6 @@ where pub fn new_full( client: Arc, executor: SubscriptionTaskExecutor, - deny_unsafe: DenyUnsafe, ) -> (State, ChildState) where Block: BlockT + 'static, @@ -187,14 +186,12 @@ where let child_backend = Box::new(self::state_full::FullState::new(client.clone(), executor.clone())); let backend = Box::new(self::state_full::FullState::new(client, executor)); - (State { backend, deny_unsafe }, ChildState { backend: child_backend }) + (State { backend }, ChildState { backend: child_backend }) } /// State API with subscriptions support. pub struct State { backend: Box>, - /// Whether to deny unsafe calls - deny_unsafe: DenyUnsafe, } #[async_trait] @@ -222,10 +219,11 @@ where fn storage_pairs( &self, + ext: &Extensions, key_prefix: StorageKey, block: Option, ) -> Result, Error> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; self.backend.storage_pairs(block, key_prefix).map_err(Into::into) } @@ -262,13 +260,15 @@ where async fn storage_size( &self, + ext: &Extensions, key: StorageKey, block: Option, ) -> Result, Error> { - self.backend - .storage_size(block, key, self.deny_unsafe) - .await - .map_err(Into::into) + let deny_unsafe = ext + .get::() + .cloned() + .expect("DenyUnsafe extension is always set by the substrate rpc server; qed"); + self.backend.storage_size(block, key, deny_unsafe).await.map_err(Into::into) } fn metadata(&self, block: Option) -> Result { @@ -281,11 +281,12 @@ where fn query_storage( &self, + ext: &Extensions, keys: Vec, from: Block::Hash, to: Option, ) -> Result>, Error> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; self.backend.query_storage(from, to, keys).map_err(Into::into) } @@ -312,12 +313,13 @@ where /// Note: requires runtimes compiled with wasm tracing support, `--features with-tracing`. fn trace_block( &self, + ext: &Extensions, block: Block::Hash, targets: Option, storage_keys: Option, methods: Option, ) -> Result { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; self.backend .trace_block(block, targets, storage_keys, methods) .map_err(Into::into) @@ -327,8 +329,17 @@ where self.backend.subscribe_runtime_version(pending) } - fn subscribe_storage(&self, pending: PendingSubscriptionSink, keys: Option>) { - self.backend.subscribe_storage(pending, keys, self.deny_unsafe) + fn subscribe_storage( + &self, + pending: PendingSubscriptionSink, + ext: &Extensions, + keys: Option>, + ) { + let deny_unsafe = ext + .get::() + .cloned() + .expect("DenyUnsafe extension is always set by the substrate rpc server; qed"); + self.backend.subscribe_storage(pending, keys, deny_unsafe) } } diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index dd866e671c5095dca9b5851111340a4e32f71e67..6b711f2425e95891ad2fd775528e7bafaef451ee 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -18,12 +18,11 @@ use self::error::Error; use super::*; -use crate::testing::{test_executor, timeout_secs}; +use crate::testing::{allow_unsafe, test_executor, timeout_secs}; use assert_matches::assert_matches; use futures::executor; use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError}; use sc_block_builder::BlockBuilderBuilder; -use sc_rpc_api::DenyUnsafe; use sp_consensus::BlockOrigin; use sp_core::{hash::H256, storage::ChildInfo}; use std::sync::Arc; @@ -39,14 +38,6 @@ fn prefixed_storage_key() -> PrefixedStorageKey { child_info.prefixed_storage_key() } -fn init_logger() { - use tracing_subscriber::{EnvFilter, FmtSubscriber}; - - let _ = FmtSubscriber::builder() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); -} - #[tokio::test] async fn should_return_storage() { const KEY: &[u8] = b":mock"; @@ -62,8 +53,9 @@ async fn should_return_storage() { .add_extra_storage(b":map:acc2".to_vec(), vec![1, 2, 3]) .build(); let genesis_hash = client.genesis_hash(); - let (client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No); + let (client, child) = new_full(Arc::new(client), test_executor()); let key = StorageKey(KEY.to_vec()); + let ext = allow_unsafe(); assert_eq!( client @@ -78,11 +70,15 @@ async fn should_return_storage() { Ok(true) ); assert_eq!( - client.storage_size(key.clone(), None).await.unwrap().unwrap() as usize, + client.storage_size(&ext, key.clone(), None).await.unwrap().unwrap() as usize, VALUE.len(), ); assert_eq!( - client.storage_size(StorageKey(b":map".to_vec()), None).await.unwrap().unwrap() as usize, + client + .storage_size(&ext, StorageKey(b":map".to_vec()), None) + .await + .unwrap() + .unwrap() as usize, 2 + 3, ); assert_eq!( @@ -110,7 +106,7 @@ async fn should_return_storage_entries() { .add_extra_child_storage(&child_info, KEY2.to_vec(), CHILD_VALUE2.to_vec()) .build(); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No); + let (_client, child) = new_full(Arc::new(client), test_executor()); let keys = &[StorageKey(KEY1.to_vec()), StorageKey(KEY2.to_vec())]; assert_eq!( @@ -141,7 +137,7 @@ async fn should_return_child_storage() { .build(), ); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No); + let (_client, child) = new_full(client, test_executor()); let child_key = prefixed_storage_key(); let key = StorageKey(b"key".to_vec()); @@ -172,7 +168,7 @@ async fn should_return_child_storage_entries() { .build(), ); let genesis_hash = client.genesis_hash(); - let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No); + let (_client, child) = new_full(client, test_executor()); let child_key = prefixed_storage_key(); let keys = vec![StorageKey(b"key1".to_vec()), StorageKey(b"key2".to_vec())]; @@ -203,7 +199,7 @@ async fn should_return_child_storage_entries() { async fn should_call_contract() { let client = Arc::new(substrate_test_runtime_client::new()); let genesis_hash = client.genesis_hash(); - let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No); + let (client, _child) = new_full(client, test_executor()); assert_matches!( client.call("balanceOf".into(), Bytes(vec![1, 2, 3]), Some(genesis_hash).into()), @@ -213,13 +209,12 @@ async fn should_call_contract() { #[tokio::test] async fn should_notify_about_storage_changes() { - init_logger(); - let mut sub = { - let mut client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let client = Arc::new(substrate_test_runtime_client::new()); + let (api, _child) = new_full(client.clone(), test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::No); - let api_rpc = api.into_rpc(); let sub = api_rpc .subscribe_unbounded("state_subscribeStorage", EmptyParams::new()) .await @@ -253,11 +248,9 @@ async fn should_notify_about_storage_changes() { #[tokio::test] async fn should_send_initial_storage_changes_and_notifications() { - init_logger(); - let mut sub = { - let mut client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let client = Arc::new(substrate_test_runtime_client::new()); + let (api, _child) = new_full(client.clone(), test_executor()); let alice_balance_key = [ sp_crypto_hashing::twox_128(b"System"), @@ -270,7 +263,9 @@ async fn should_send_initial_storage_changes_and_notifications() { .cloned() .collect::>(); - let api_rpc = api.into_rpc(); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::No); + let sub = api_rpc .subscribe_unbounded( "state_subscribeStorage", @@ -304,10 +299,10 @@ async fn should_send_initial_storage_changes_and_notifications() { #[tokio::test] async fn should_query_storage() { - async fn run_tests(mut client: Arc) { - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + async fn run_tests(client: Arc) { + let (api, _child) = new_full(client.clone(), test_executor()); - let mut add_block = |index| { + let add_block = |index| { let mut builder = BlockBuilderBuilder::new(&*client) .on_parent_block(client.chain_info().best_hash) .with_parent_block_number(client.chain_info().best_number) @@ -377,14 +372,16 @@ async fn should_query_storage() { }, ]; + let ext = allow_unsafe(); + // Query changes only up to block1 let keys = (1..6).map(|k| StorageKey(vec![k])).collect::>(); - let result = api.query_storage(keys.clone(), genesis_hash, Some(block1_hash).into()); + let result = api.query_storage(&ext, keys.clone(), genesis_hash, Some(block1_hash).into()); assert_eq!(result.unwrap(), expected); // Query all changes - let result = api.query_storage(keys.clone(), genesis_hash, None.into()); + let result = api.query_storage(&ext, keys.clone(), genesis_hash, None.into()); expected.push(StorageChangeSet { block: block2_hash, @@ -397,13 +394,13 @@ async fn should_query_storage() { assert_eq!(result.unwrap(), expected); // Query changes up to block2. - let result = api.query_storage(keys.clone(), genesis_hash, Some(block2_hash)); + let result = api.query_storage(&ext, keys.clone(), genesis_hash, Some(block2_hash)); assert_eq!(result.unwrap(), expected); // Inverted range. assert_matches!( - api.query_storage(keys.clone(), block1_hash, Some(genesis_hash)), + api.query_storage(&ext, keys.clone(), block1_hash, Some(genesis_hash)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("1 ({:?})", block1_hash) && to == format!("0 ({:?})", genesis_hash) && details == "from number > to number".to_owned() ); @@ -412,7 +409,7 @@ async fn should_query_storage() { // Invalid second hash. assert_matches!( - api.query_storage(keys.clone(), genesis_hash, Some(random_hash1)), + api.query_storage(&ext, keys.clone(), genesis_hash, Some(random_hash1)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", genesis_hash) && to == format!("{:?}", Some(random_hash1)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -421,7 +418,7 @@ async fn should_query_storage() { // Invalid first hash with Some other hash. assert_matches!( - api.query_storage(keys.clone(), random_hash1, Some(genesis_hash)), + api.query_storage(&ext, keys.clone(), random_hash1, Some(genesis_hash)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(genesis_hash)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -430,7 +427,7 @@ async fn should_query_storage() { // Invalid first hash with None. assert_matches!( - api.query_storage(keys.clone(), random_hash1, None), + api.query_storage(&ext, keys.clone(), random_hash1, None), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(block2_hash)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -439,7 +436,7 @@ async fn should_query_storage() { // Both hashes invalid. assert_matches!( - api.query_storage(keys.clone(), random_hash1, Some(random_hash2)), + api.query_storage(&ext, keys.clone(), random_hash1, Some(random_hash2)), Err(Error::InvalidBlockRange { from, to, details }) if from == format!("{:?}", random_hash1) && to == format!("{:?}", Some(random_hash2)) && details == format!( "UnknownBlock: Header was not found in the database: {:?}", random_hash1 @@ -471,7 +468,7 @@ async fn should_query_storage() { #[tokio::test] async fn should_return_runtime_version() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client.clone(), test_executor()); // it is basically json-encoded substrate_test_runtime_client::runtime::VERSION let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\ @@ -479,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(); @@ -493,9 +491,10 @@ async fn should_return_runtime_version() { async fn should_notify_on_runtime_version_initially() { let mut sub = { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::No); + let (api, _child) = new_full(client, test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::No); - let api_rpc = api.into_rpc(); let sub = api_rpc .subscribe_unbounded("state_subscribeRuntimeVersion", EmptyParams::new()) .await @@ -518,12 +517,11 @@ fn should_deserialize_storage_key() { #[tokio::test] async fn wildcard_storage_subscriptions_are_rpc_unsafe() { - init_logger(); - let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes); + let (api, _child) = new_full(client, test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::Yes); - let api_rpc = api.into_rpc(); let err = api_rpc.subscribe_unbounded("state_subscribeStorage", EmptyParams::new()).await; assert_matches!(err, Err(RpcError::JsonRpc(e)) if e.message() == "RPC call is unsafe to be called externally"); } @@ -531,8 +529,9 @@ async fn wildcard_storage_subscriptions_are_rpc_unsafe() { #[tokio::test] async fn concrete_storage_subscriptions_are_rpc_safe() { let client = Arc::new(substrate_test_runtime_client::new()); - let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes); - let api_rpc = api.into_rpc(); + let (api, _child) = new_full(client, test_executor()); + let mut api_rpc = api.into_rpc(); + api_rpc.extensions_mut().insert(DenyUnsafe::Yes); let key = StorageKey(STORAGE_KEY.to_vec()); let sub = api_rpc.subscribe_unbounded("state_subscribeStorage", [[key]]).await; diff --git a/substrate/client/rpc/src/statement/mod.rs b/substrate/client/rpc/src/statement/mod.rs index e99135aec38c6fb61dc667535b6fe9e0f458f6e3..81aa50f73430b4e06b6b5133eaf1dde9e639e143 100644 --- a/substrate/client/rpc/src/statement/mod.rs +++ b/substrate/client/rpc/src/statement/mod.rs @@ -19,10 +19,12 @@ //! Substrate statement store API. use codec::{Decode, Encode}; -use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + Extensions, +}; /// Re-export the API for backward compatibility. pub use sc_rpc_api::statement::{error::Error, StatementApiServer}; -use sc_rpc_api::DenyUnsafe; use sp_core::Bytes; use sp_statement_store::{StatementSource, SubmitResult}; use std::sync::Arc; @@ -30,23 +32,19 @@ use std::sync::Arc; /// Statement store API pub struct StatementStore { store: Arc, - deny_unsafe: DenyUnsafe, } impl StatementStore { /// Create new instance of Offchain API. - pub fn new( - store: Arc, - deny_unsafe: DenyUnsafe, - ) -> Self { - StatementStore { store, deny_unsafe } + pub fn new(store: Arc) -> Self { + StatementStore { store } } } #[async_trait] impl StatementApiServer for StatementStore { - fn dump(&self) -> RpcResult> { - self.deny_unsafe.check_if_safe()?; + fn dump(&self, ext: &Extensions) -> RpcResult> { + sc_rpc_api::check_if_safe(ext)?; let statements = self.store.statements().map_err(|e| Error::StatementStore(e.to_string()))?; diff --git a/substrate/client/rpc/src/system/mod.rs b/substrate/client/rpc/src/system/mod.rs index 8c7510c64cb5199a1ef9ac6ecf7bfbc9a570d368..e0752749bcbd3c305b772f9cd84998ad355e2e40 100644 --- a/substrate/client/rpc/src/system/mod.rs +++ b/substrate/client/rpc/src/system/mod.rs @@ -22,8 +22,11 @@ mod tests; use futures::channel::oneshot; -use jsonrpsee::core::{async_trait, JsonValue}; -use sc_rpc_api::DenyUnsafe; +use jsonrpsee::{ + core::{async_trait, JsonValue}, + Extensions, +}; +use sc_rpc_api::check_if_safe; use sc_tracing::logging; use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::{self, Header as HeaderT}; @@ -35,7 +38,6 @@ pub use sc_rpc_api::system::*; pub struct System { info: SystemInfo, send_back: TracingUnboundedSender>, - deny_unsafe: DenyUnsafe, } /// Request to be processed. @@ -68,12 +70,8 @@ impl System { /// /// The `send_back` will be used to transmit some of the requests. The user is responsible for /// reading from that channel and answering the requests. - pub fn new( - info: SystemInfo, - send_back: TracingUnboundedSender>, - deny_unsafe: DenyUnsafe, - ) -> Self { - System { info, send_back, deny_unsafe } + pub fn new(info: SystemInfo, send_back: TracingUnboundedSender>) -> Self { + System { info, send_back } } } @@ -119,22 +117,23 @@ impl SystemApiServer::Number> async fn system_peers( &self, + ext: &Extensions, ) -> Result::Number>>, Error> { - self.deny_unsafe.check_if_safe()?; + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::Peers(tx)); rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_network_state(&self) -> Result { - self.deny_unsafe.check_if_safe()?; + async fn system_network_state(&self, ext: &Extensions) -> Result { + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkState(tx)); rx.await.map_err(|e| Error::Internal(e.to_string())) } - async fn system_add_reserved_peer(&self, peer: String) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + async fn system_add_reserved_peer(&self, ext: &Extensions, peer: String) -> Result<(), Error> { + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkAddReservedPeer(peer, tx)); match rx.await { @@ -144,8 +143,12 @@ impl SystemApiServer::Number> } } - async fn system_remove_reserved_peer(&self, peer: String) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + async fn system_remove_reserved_peer( + &self, + ext: &Extensions, + peer: String, + ) -> Result<(), Error> { + check_if_safe(ext)?; let (tx, rx) = oneshot::channel(); let _ = self.send_back.unbounded_send(Request::NetworkRemoveReservedPeer(peer, tx)); match rx.await { @@ -173,15 +176,15 @@ impl SystemApiServer::Number> rx.await.map_err(|e| Error::Internal(e.to_string())) } - fn system_add_log_filter(&self, directives: String) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + fn system_add_log_filter(&self, ext: &Extensions, directives: String) -> Result<(), Error> { + check_if_safe(ext)?; logging::add_directives(&directives); logging::reload_filter().map_err(|e| Error::Internal(e)) } - fn system_reset_log_filter(&self) -> Result<(), Error> { - self.deny_unsafe.check_if_safe()?; + fn system_reset_log_filter(&self, ext: &Extensions) -> Result<(), Error> { + check_if_safe(ext)?; logging::reset_log_filter().map_err(|e| Error::Internal(e)) } } diff --git a/substrate/client/rpc/src/system/tests.rs b/substrate/client/rpc/src/system/tests.rs index 03967c63523c79610009b0edb4ac4e6618b99040..bfef0e429ceba05415ff276723917348bf7dcbbf 100644 --- a/substrate/client/rpc/src/system/tests.rs +++ b/substrate/client/rpc/src/system/tests.rs @@ -17,6 +17,7 @@ // along with this program. If not, see . use super::{helpers::SyncState, *}; +use crate::DenyUnsafe; use assert_matches::assert_matches; use futures::prelude::*; use jsonrpsee::{core::EmptyServerParams as EmptyParams, MethodsError as RpcError, RpcModule}; @@ -127,7 +128,7 @@ fn api>>(sync: T) -> RpcModule> { future::ready(()) })) }); - System::new( + let mut module = System::new( SystemInfo { impl_name: "testclient".into(), impl_version: "0.2.0".into(), @@ -136,9 +137,11 @@ fn api>>(sync: T) -> RpcModule> { chain_type: Default::default(), }, tx, - sc_rpc_api::DenyUnsafe::No, ) - .into_rpc() + .into_rpc(); + + module.extensions_mut().insert(DenyUnsafe::No); + module } #[tokio::test] diff --git a/substrate/client/rpc/src/testing.rs b/substrate/client/rpc/src/testing.rs index e04f80a7b9e61d8fc0614e284f4dbab8a7f7dbac..990f81ba551e6fbb1b921e9297fc6cd1857104bd 100644 --- a/substrate/client/rpc/src/testing.rs +++ b/substrate/client/rpc/src/testing.rs @@ -20,6 +20,9 @@ use std::{future::Future, sync::Arc}; +use jsonrpsee::Extensions; +use sc_rpc_api::DenyUnsafe; + /// A task executor that can be used for running RPC tests. /// /// Warning: the tokio runtime must be initialized before calling this. @@ -70,3 +73,17 @@ pub fn test_executor() -> Arc { pub fn timeout_secs>(s: u64, f: F) -> tokio::time::Timeout { tokio::time::timeout(std::time::Duration::from_secs(s), f) } + +/// Helper to create an extension that denies unsafe calls. +pub fn deny_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::Yes); + ext +} + +/// Helper to create an extension that allows unsafe calls. +pub fn allow_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::No); + ext +} diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index d7fb7918481cd80c02a6bee05524fbe223261e0d..28a76847ac0611ee75012b6b3ee15adeea9c6e61 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, @@ -43,6 +43,7 @@ use sc_executor::{ use sc_keystore::LocalKeystore; use sc_network::{ config::{FullNetworkConfiguration, SyncMode}, + multiaddr::Protocol, service::{ traits::{PeerStore, RequestResponseConfig}, NotificationMetrics, @@ -55,7 +56,7 @@ use sc_network_sync::{ block_relay_protocol::BlockRelayParams, block_request_handler::BlockRequestHandler, engine::SyncingEngine, service::network::NetworkServiceProvider, state_request_handler::StateRequestHandler, - warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, WarpSyncParams, + warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, WarpSyncConfig, }; use sc_rpc::{ author::AuthorApiServer, @@ -212,10 +213,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 @@ -248,10 +246,7 @@ where offchain_worker_enabled: config.offchain_worker.enabled, offchain_indexing_api: config.offchain_worker.indexing_enabled, wasm_runtime_overrides: config.wasm_runtime_overrides.clone(), - no_genesis: matches!( - config.network.sync_mode, - SyncMode::LightState { .. } | SyncMode::Warp { .. } - ), + no_genesis: config.no_genesis(), wasm_runtime_substitutes, enable_import_proof_recording, }, @@ -271,11 +266,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 _ }); @@ -364,8 +359,7 @@ pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> { /// A shared transaction pool. pub transaction_pool: Arc, /// Builds additional [`RpcModule`]s that should be added to the server - pub rpc_builder: - Box Result, Error>>, + pub rpc_builder: Box Result, Error>>, /// A shared network instance. pub network: Arc, /// A Sender for RPC requests. @@ -438,7 +432,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()?; @@ -467,7 +471,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, @@ -491,25 +501,51 @@ 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 = |deny_unsafe: DenyUnsafe| { + let gen_rpc_module = || { gen_rpc_module( - deny_unsafe, task_manager.spawn_handle(), client.clone(), 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 = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?; - let rpc_handlers = RpcHandlers::new(Arc::new(gen_rpc_module(sc_rpc::DenyUnsafe::No)?.into())); + let rpc_server_handle = start_rpc_servers( + &config.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), listen_addrs); // Spawn informant task spawn_handle.spawn( @@ -518,9 +554,9 @@ where sc_informant::build(client.clone(), network, sync_service.clone()), ); - task_manager.keep_alive((config.base_path, rpc)); + task_manager.keep_alive((config.base_path, rpc_server_handle)); - Ok(rpc_handlers) + Ok(in_memory_rpc_handle) } /// Returns a future that forwards imported transactions to the transaction networking protocol. @@ -554,7 +590,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, @@ -567,16 +607,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()) @@ -593,15 +633,18 @@ where /// Generate RPC module using provided configuration pub fn gen_rpc_module( - deny_unsafe: DenyUnsafe, spawn_handle: SpawnTaskHandle, client: Arc, 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(DenyUnsafe, SubscriptionTaskExecutor) -> Result, Error>), + rpc_builder: &(dyn Fn(SubscriptionTaskExecutor) -> Result, Error>), ) -> Result, Error> where TBl: BlockT, @@ -624,11 +667,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(()); @@ -636,8 +679,7 @@ where let (chain, state, child_state) = { let chain = sc_rpc::chain::new_full(client.clone(), task_executor.clone()).into_rpc(); - let (state, child_state) = - sc_rpc::state::new_full(client.clone(), task_executor.clone(), deny_unsafe); + let (state, child_state) = sc_rpc::state::new_full(client.clone(), task_executor.clone()); let state = state.into_rpc(); let child_state = child_state.into_rpc(); @@ -674,8 +716,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( @@ -691,9 +733,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(); @@ -701,15 +743,14 @@ where client.clone(), transaction_pool, keystore, - deny_unsafe, task_executor.clone(), ) .into_rpc(); - let system = sc_rpc::system::System::new(system_info, system_rpc_tx, deny_unsafe).into_rpc(); + let system = sc_rpc::system::System::new(system_info, system_rpc_tx).into_rpc(); if let Some(storage) = backend.offchain_storage() { - let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe).into_rpc(); + let offchain = sc_rpc::offchain::Offchain::new(storage).into_rpc(); rpc_api.merge(offchain).map_err(|e| Error::Application(e.into()))?; } @@ -729,7 +770,7 @@ where rpc_api.merge(state).map_err(|e| Error::Application(e.into()))?; rpc_api.merge(child_state).map_err(|e| Error::Application(e.into()))?; // Additional [`RpcModule`]s defined in the node to fit the specific blockchain - let extra_rpcs = rpc_builder(deny_unsafe, task_executor.clone())?; + let extra_rpcs = rpc_builder(task_executor.clone())?; rpc_api.merge(extra_rpcs).map_err(|e| Error::Application(e.into()))?; Ok(rpc_api) @@ -759,8 +800,8 @@ pub struct BuildNetworkParams< /// A block announce validator builder. pub block_announce_validator_builder: Option) -> Box + Send> + Send>>, - /// Optional warp sync params. - pub warp_sync_params: Option>, + /// Optional warp sync config. + pub warp_sync_config: Option>, /// User specified block relay params. If not specified, the default /// block request handler will be used. pub block_relay: Option>, @@ -804,12 +845,12 @@ where spawn_handle, import_queue, block_announce_validator_builder, - warp_sync_params, + warp_sync_config, block_relay, metrics, } = params; - if warp_sync_params.is_none() && config.network.sync_mode.is_warp() { + if warp_sync_config.is_none() && config.network.sync_mode.is_warp() { return Err("Warp sync enabled, but no warp sync provider configured.".into()) } @@ -872,8 +913,8 @@ where (protocol_config, config_name) }; - let (warp_sync_protocol_config, warp_request_protocol_name) = match warp_sync_params.as_ref() { - Some(WarpSyncParams::WithProvider(warp_with_provider)) => { + let (warp_sync_protocol_config, warp_request_protocol_name) = match warp_sync_config.as_ref() { + Some(WarpSyncConfig::WithProvider(warp_with_provider)) => { // Allow both outgoing and incoming requests. let (handler, protocol_config) = WarpSyncRequestHandler::new::<_, TNet>( protocol_id.clone(), @@ -942,7 +983,7 @@ where protocol_id.clone(), &config.chain_spec.fork_id().map(ToOwned::to_owned), block_announce_validator, - warp_sync_params, + warp_sync_config, chain_sync_network_handle, import_queue.service(), block_downloader, @@ -955,7 +996,7 @@ where let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); let network_params = sc_network::config::Params::::Hash, TNet> { - role: config.role.clone(), + role: config.role, executor: { let spawn_handle = Clone::clone(&spawn_handle); Box::new(move |fut| { @@ -1001,7 +1042,7 @@ where "system-rpc-handler", Some("networking"), build_system_rpc_future::<_, _, ::Hash>( - config.role.clone(), + config.role, network_mut.network_service(), sync_service.clone(), client.clone(), diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index a2c9212f7b9c9ce80860aa9ca9f3b9e8b0a844d5..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. @@ -1754,7 +1756,7 @@ where /// If you are not sure that there are no BlockImport objects provided by the consensus /// algorithm, don't use this function. async fn import_block( - &mut self, + &self, mut import_block: BlockImportParams, ) -> Result { let span = tracing::span!(tracing::Level::DEBUG, "import_block"); @@ -1854,19 +1856,19 @@ where { type Error = ConsensusError; - async fn import_block( - &mut self, - import_block: BlockImportParams, - ) -> Result { - (&*self).import_block(import_block).await - } - async fn check_block( &self, block: BlockCheckParams, ) -> Result { (&self).check_block(block).await } + + async fn import_block( + &self, + import_block: BlockImportParams, + ) -> Result { + (&self).import_block(import_block).await + } } impl Finalizer for Client diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index f8d8511f2718b9a0786c6bc119977de85b86feaa..6f65c2e2d81b12172637a240d38f9da4d7f00b48 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -33,7 +33,9 @@ pub use sc_network::{ }, Multiaddr, }; -pub use sc_rpc_server::IpNetwork; +pub use sc_rpc_server::{ + IpNetwork, RpcEndpoint, RpcMethods, SubscriptionIdProvider as RpcSubscriptionIdProvider, +}; pub use sc_telemetry::TelemetryEndpoints; pub use sc_transaction_pool::Options as TransactionPoolOptions; use sp_core::crypto::SecretString; @@ -76,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 binding address. - 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. @@ -135,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. @@ -255,24 +221,6 @@ impl Configuration { } } -/// Available RPC methods. -#[derive(Debug, Copy, Clone)] -pub enum RpcMethods { - /// Expose every RPC method only when RPC is listening on `localhost`, - /// otherwise serve only safe RPC methods. - Auto, - /// Allow only a safe subset of RPC methods. - Safe, - /// Expose every RPC method (even potentially unsafe ones). - Unsafe, -} - -impl Default for RpcMethods { - fn default() -> RpcMethods { - RpcMethods::Auto - } -} - #[static_init::dynamic(drop, lazy)] static mut BASE_PATH_TEMP: Option = None; @@ -339,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 89d563001cd6dd117593b8edf53963e3bd6d8062..babb76f022f049abb55ff1117a7a1c2c9b100fd8 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -34,7 +34,11 @@ mod client; mod metrics; mod task_manager; -use std::{collections::HashMap, net::SocketAddr}; +use crate::config::Multiaddr; +use std::{ + collections::HashMap, + net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, +}; use codec::{Decode, Encode}; use futures::{pin_mut, FutureExt, StreamExt}; @@ -47,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; @@ -80,31 +85,40 @@ 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::WarpSyncParams; +pub use sc_network_sync::WarpSyncConfig; #[doc(hidden)] pub use sc_network_transactions::config::{TransactionImport, TransactionImportFuture}; -pub use sc_rpc::{ - RandomIntegerSubscriptionId, RandomStringSubscriptionId, RpcSubscriptionIdProvider, -}; +pub use sc_rpc::{RandomIntegerSubscriptionId, RandomStringSubscriptionId}; pub use sc_tracing::TracingReceiver; pub use sc_transaction_pool::Options as TransactionPoolOptions; pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, TransactionPool}; #[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. @@ -126,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[..] } } @@ -341,7 +360,7 @@ pub async fn build_system_rpc_future< sc_rpc::system::Request::SyncState(sender) => { use sc_rpc::system::SyncState; - match sync_service.best_seen_block().await { + match sync_service.status().await.map(|status| status.best_seen_block) { Ok(best_seen_block) => { let best_number = client.info().best_number; let _ = sender.send(SyncState { @@ -359,63 +378,71 @@ 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> + rpc_id_provider: Option>, +) -> Result where - R: Fn(sc_rpc::DenyUnsafe) -> Result, Error>, + R: Fn() -> Result, Error>, { - fn deny_unsafe(addr: SocketAddr, methods: &RpcMethods) -> sc_rpc::DenyUnsafe { - let is_exposed_addr = !addr.ip().is_loopback(); - match (is_exposed_addr, methods) { - | (_, RpcMethods::Unsafe) | (false, RpcMethods::Auto) => sc_rpc::DenyUnsafe::No, - _ => sc_rpc::DenyUnsafe::Yes, - } - } - - // if binding the specified port failed then a random port is assigned by the OS. - let backup_port = |mut addr: SocketAddr| { - addr.set_port(0); - addr + let endpoints: Vec = if let Some(endpoints) = + rpc_configuration.addr.as_ref() + { + endpoints.clone() + } else { + 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: rpc_configuration.batch_config, + cors: rpc_configuration.cors.clone(), + listen_addr: ipv4, + 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: rpc_configuration.batch_config, + cors: rpc_configuration.cors.clone(), + listen_addr: ipv6, + 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 addr = config.rpc_addr.unwrap_or_else(|| ([127, 0, 0, 1], config.rpc_port).into()); - let backup_addr = backup_port(addr); - let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; + let metrics = sc_rpc_server::RpcMetrics::new(registry)?; + let rpc_api = gen_rpc_module()?; let server_config = sc_rpc_server::Config { - addrs: [addr, backup_addr], - batch_config: config.rpc_batch_config, - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subs_per_conn: config.rpc_max_subs_per_conn, - message_buffer_capacity: config.rpc_message_buffer_capacity, - rpc_api: gen_rpc_module(deny_unsafe(addr, &config.rpc_methods))?, + endpoints, + rpc_api, metrics, id_provider: rpc_id_provider, - cors: config.rpc_cors.as_ref(), - tokio_handle: config.tokio_handle.clone(), - rate_limit: config.rpc_rate_limit, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, + tokio_handle: tokio_handle.clone(), }; // TODO: https://github.com/paritytech/substrate/issues/13773 @@ -423,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 bd48fae634445eafe61b591e47a6883ba57056e2..13e63962fe8fecba1f571e149f6d52cdf749965b 100644 --- a/substrate/client/service/test/src/client/mod.rs +++ b/substrate/client/service/test/src/client/mod.rs @@ -238,7 +238,7 @@ fn client_initializes_from_genesis_ok() { #[test] fn block_builder_works_with_no_transactions() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let block = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) @@ -256,7 +256,7 @@ fn block_builder_works_with_no_transactions() { #[test] fn block_builder_works_with_transactions() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let mut builder = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) @@ -316,7 +316,7 @@ fn block_builder_works_with_transactions() { #[test] fn block_builder_does_not_include_invalid() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let mut builder = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) .with_parent_block_number(0) @@ -390,7 +390,7 @@ fn best_containing_with_genesis_block() { fn uncles_with_only_ancestors() { // block tree: // G -> A1 -> A2 - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -424,7 +424,7 @@ fn uncles_with_multiple_forks() { // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -584,7 +584,7 @@ fn finality_target_on_longest_chain_with_single_chain_3_blocks() { // block tree: // G -> A1 -> A2 - let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -625,7 +625,7 @@ fn finality_target_on_longest_chain_with_multiple_forks() { // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 - let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) @@ -877,7 +877,7 @@ fn finality_target_on_longest_chain_with_max_depth_higher_than_best() { // block tree: // G -> A1 -> A2 - let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); let chain = client.chain_info(); // G -> A1 @@ -914,7 +914,7 @@ fn finality_target_with_best_not_on_longest_chain() { // -> B2 -> (B3) -> B4 // ^best - let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let (client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); let chain = client.chain_info(); // G -> A1 @@ -1045,7 +1045,7 @@ fn finality_target_with_best_not_on_longest_chain() { fn import_with_justification() { // block tree: // G -> A1 -> A2 -> A3 - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let mut finality_notifications = client.finality_notification_stream(); @@ -1099,7 +1099,7 @@ fn import_with_justification() { #[test] fn importing_diverged_finalized_block_should_trigger_reorg() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 -> A2 // \ @@ -1160,7 +1160,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() { #[test] fn finalizing_diverged_block_should_trigger_reorg() { - let (mut client, select_chain) = TestClientBuilder::new().build_with_longest_chain(); + let (client, select_chain) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 -> A2 // \ @@ -1257,7 +1257,7 @@ fn finalizing_diverged_block_should_trigger_reorg() { #[test] fn finality_notifications_content() { sp_tracing::try_init_simple(); - let (mut client, _select_chain) = TestClientBuilder::new().build_with_longest_chain(); + let (client, _select_chain) = TestClientBuilder::new().build_with_longest_chain(); // -> D3 -> D4 // G -> A1 -> A2 -> A3 @@ -1410,7 +1410,7 @@ fn get_hash_by_block_number_doesnt_panic() { #[test] fn state_reverted_on_reorg() { sp_tracing::try_init_simple(); - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); let current_balance = |client: &substrate_test_runtime_client::TestClient| { client @@ -1492,7 +1492,7 @@ fn doesnt_import_blocks_that_revert_finality() { .unwrap(), ); - let mut client = TestClientBuilder::with_backend(backend).build(); + let client = TestClientBuilder::with_backend(backend).build(); let mut finality_notifications = client.finality_notification_stream(); @@ -1619,7 +1619,7 @@ fn respects_block_rules() { known_bad: &mut HashSet, fork_rules: &mut Vec<(u64, H256)>, ) { - let mut client = if record_only { + let client = if record_only { TestClientBuilder::new().build() } else { TestClientBuilder::new() @@ -1771,7 +1771,7 @@ fn returns_status_for_pruned_blocks() { .unwrap(), ); - let mut client = TestClientBuilder::with_backend(backend).build(); + let client = TestClientBuilder::with_backend(backend).build(); let a1 = BlockBuilderBuilder::new(&client) .on_parent_block(client.chain_info().genesis_hash) @@ -2160,7 +2160,7 @@ fn cleans_up_closed_notification_sinks_on_block_import() { /// Test that ensures that we always send an import notification for re-orgs. #[test] fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifications() { - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); let mut notification_stream = futures::executor::block_on_stream(client.import_notification_stream()); @@ -2232,7 +2232,7 @@ fn use_dalek_ext_works() { sp_core::ed25519::Signature::default() } - let mut client = TestClientBuilder::new().build(); + let client = TestClientBuilder::new().build(); client.execution_extensions().set_extensions_factory( sc_client_api::execution_extensions::ExtensionBeforeBlock::::new( @@ -2263,7 +2263,7 @@ fn use_dalek_ext_works() { #[test] fn finalize_after_best_block_updates_best() { - let mut client = substrate_test_runtime_client::new(); + let client = substrate_test_runtime_client::new(); // G -> A1 let a1 = BlockBuilderBuilder::new(&client) diff --git a/substrate/client/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/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/tests/pool.rs b/substrate/client/transaction-pool/tests/pool.rs index 49bd2203c12b48454844e1bc3ce09c2c446fc9b6..6d70b6ce67eca71a6b010e6a8ab46d7473343630 100644 --- a/substrate/client/transaction-pool/tests/pool.rs +++ b/substrate/client/transaction-pool/tests/pool.rs @@ -980,7 +980,7 @@ fn ready_set_should_eventually_resolve_when_block_update_arrives() { #[test] fn import_notification_to_pool_maintain_works() { - let mut client = Arc::new(substrate_test_runtime_client::new()); + let client = Arc::new(substrate_test_runtime_client::new()); let best_hash = client.info().best_hash; let finalized_hash = client.info().finalized_hash; diff --git a/substrate/docs/SECURITY.md b/substrate/docs/SECURITY.md index 0d2064863d85c885448f2caea9a113e663d61598..f52da03271326efc7d8f0363195fd162fd99ed17 100644 --- a/substrate/docs/SECURITY.md +++ b/substrate/docs/SECURITY.md @@ -8,7 +8,7 @@ required to address security issues. ## Reporting a Vulnerability Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report -might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io. +might be eligible for the Parity Bug Bounty Program, your email should be sent to bugbounty@parity.io. Your report should include the following: 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/Cargo.toml b/substrate/frame/assets/Cargo.toml index fcb151298f09a50dfb0a527a1dba664296e27975..e20b576d0836ec959ea3a050c4b269659b724214 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -30,7 +30,6 @@ frame-benchmarking = { optional = true, workspace = true } sp-core = { workspace = true } [dev-dependencies] -sp-std = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 6f8ad0c2939378eef14338ffba3a27d0e15b5a3c..e909932bfc8207db8e075aa11c950429b7d4c260 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -801,8 +801,6 @@ pub mod pallet { /// /// - `id`: The identifier of the asset to be destroyed. This must identify an existing /// asset. - /// - /// The asset class must be frozen before calling `start_destroy`. #[pallet::call_index(2)] pub fn start_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index 912cb3e27cd5bdf7e31adf33bf4d5e117d00682d..4e4052b2b566e6978a4281d72e1a7199136c6c89 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -25,12 +25,12 @@ use frame_election_provider_support::{ }; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, KeyOwnerProofSystem, OnInitialize}, + traits::{ConstU128, ConstU32, ConstU64, OnInitialize}, }; use pallet_session::historical as pallet_session_historical; use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature}; use sp_core::{ - crypto::{KeyTypeId, Pair, VrfSecret}, + crypto::{Pair, VrfSecret}, U256, }; use sp_io; @@ -182,7 +182,7 @@ impl Config for Test { type WeightInfo = (); type MaxAuthorities = ConstU32<10>; type MaxNominators = ConstU32<100>; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index 5740f8081c078bf9e0e6614d4fd26f23c57da989..c825300218d462a8465df2e80afda4e02c951cf3 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -31,6 +31,14 @@ const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; +fn minimum_balance, I: 'static>() -> T::Balance { + if cfg!(feature = "insecure_zero_ed") { + 100u32.into() + } else { + T::ExistentialDeposit::get() + } +} + #[instance_benchmarks] mod benchmarks { use super::*; @@ -40,7 +48,7 @@ mod benchmarks { // * Transfer will create the recipient account. #[benchmark] fn transfer_allow_death() { - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let caller = whitelisted_caller(); // Give some multiple of the existential deposit @@ -75,7 +83,7 @@ mod benchmarks { as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); // Give the recipient account existential deposit (thus their account already exists). - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); @@ -98,7 +106,7 @@ mod benchmarks { // Give the sender account max funds, thus a transfer will not kill account. let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); #[extrinsic_call] @@ -115,7 +123,7 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give the user some initial balance. - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); @@ -132,7 +140,7 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give the user some initial balance. - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); @@ -147,7 +155,7 @@ mod benchmarks { // * Transfer will create the recipient account. #[benchmark] fn force_transfer() { - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let source: T::AccountId = account("source", 0, SEED); let source_lookup = T::Lookup::unlookup(source.clone()); @@ -175,7 +183,7 @@ mod benchmarks { #[benchmark(extra)] fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let caller = whitelisted_caller(); // Give some multiple of the existential deposit @@ -214,7 +222,7 @@ mod benchmarks { let recipient_lookup = T::Lookup::unlookup(recipient.clone()); // Give some multiple of the existential deposit - let existential_deposit = T::ExistentialDeposit::get(); + let existential_deposit: T::Balance = minimum_balance::(); let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&caller, balance); @@ -231,7 +239,7 @@ mod benchmarks { let user_lookup = T::Lookup::unlookup(user.clone()); // Give some multiple of the existential deposit - let ed = T::ExistentialDeposit::get(); + let ed = minimum_balance::(); let balance = ed + ed; let _ = as Currency<_>>::make_free_balance_be(&user, balance); @@ -257,8 +265,8 @@ mod benchmarks { .map(|i| -> T::AccountId { let user = account("old_user", i, SEED); let account = AccountData { - free: T::ExistentialDeposit::get(), - reserved: T::ExistentialDeposit::get(), + free: minimum_balance::(), + reserved: minimum_balance::(), frozen: Zero::zero(), flags: ExtraFlags::old_logic(), }; diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index 1a09303a6590d54b3bb94ffb1d870d5c1e687b24..4a8b213c645e0fc37a80edc911d7eebb68ed9232 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -25,7 +25,7 @@ use frame_support::traits::{ Preservation::{Expendable, Preserve, Protect}, Restriction::Free, }, - Consideration, Footprint, LinearStoragePrice, + Consideration, Footprint, LinearStoragePrice, MaybeConsideration, }; use fungible::{ FreezeConsideration, HoldConsideration, Inspect, InspectFreeze, InspectHold, @@ -518,24 +518,28 @@ fn freeze_consideration_works() { let who = 4; // freeze amount taken somewhere outside of our (Consideration) scope. let extend_freeze = 15; + + 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().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, extend_freeze)); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4 + extend_freeze); - let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 8 + extend_freeze); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10 + extend_freeze); let _ = ticket.drop(&who).unwrap(); @@ -560,24 +564,28 @@ fn hold_consideration_works() { let who = 4; // hold amount taken somewhere outside of our (Consideration) scope. let extend_hold = 15; + + 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().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().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); assert_ok!(Balances::hold(&TestId::Foo, &who, extend_hold)); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4 + extend_hold); - let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 8 + extend_hold); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10 + extend_hold); let _ = ticket.drop(&who).unwrap(); @@ -600,21 +608,22 @@ fn lone_freeze_consideration_works() { >; let who = 4; + let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, 5)); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 15); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); let _ = ticket.drop(&who).unwrap(); @@ -637,21 +646,22 @@ fn lone_hold_consideration_works() { >; let who = 4; + let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); assert_ok!(Balances::hold(&TestId::Foo, &who, 5)); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 15); - let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); - assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), zero_ticket); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); let _ = ticket.drop(&who).unwrap(); diff --git a/substrate/frame/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/Cargo.toml b/substrate/frame/beefy-mmr/Cargo.toml index 672a7a68bc7e0fea4946ffdb6961015b8e89f445..d67ac20ee922bc299956d8201a1552b2651c9527 100644 --- a/substrate/frame/beefy-mmr/Cargo.toml +++ b/substrate/frame/beefy-mmr/Cargo.toml @@ -18,6 +18,7 @@ log = { workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { optional = true, workspace = true, default-features = true } binary-merkle-tree = { workspace = true } +frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-beefy = { workspace = true } @@ -40,6 +41,7 @@ std = [ "array-bytes", "binary-merkle-tree/std", "codec/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "log/std", @@ -65,6 +67,7 @@ try-runtime = [ "sp-runtime/try-runtime", ] runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-mmr/runtime-benchmarks", diff --git a/substrate/frame/beefy-mmr/src/benchmarking.rs b/substrate/frame/beefy-mmr/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..135f95eabb998ba2709c232940b608966760c6c4 --- /dev/null +++ b/substrate/frame/beefy-mmr/src/benchmarking.rs @@ -0,0 +1,129 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Beefy pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as BeefyMmr; +use codec::Encode; +use frame_benchmarking::v2::*; +use frame_support::traits::Hooks; +use frame_system::{Config as SystemConfig, Pallet as System}; +use pallet_mmr::{Nodes, Pallet as Mmr}; +use sp_consensus_beefy::Payload; +use sp_runtime::traits::One; + +pub trait Config: + pallet_mmr::Config + crate::Config +{ +} + +impl Config for T where + T: pallet_mmr::Config + crate::Config +{ +} + +fn init_block(block_num: u32) { + let block_num = block_num.into(); + System::::initialize(&block_num, &::Hash::default(), &Default::default()); + Mmr::::on_initialize(block_num); +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn extract_validation_context() { + if !cfg!(feature = "test") { + pallet_mmr::UseLocalStorage::::set(true); + } + + init_block::(1); + let header = System::::finalize(); + frame_system::BlockHash::::insert(BlockNumberFor::::one(), header.hash()); + + let validation_context; + #[block] + { + validation_context = + as AncestryHelper>>::extract_validation_context(header); + } + + assert!(validation_context.is_some()); + } + + #[benchmark] + fn read_peak() { + if !cfg!(feature = "test") { + pallet_mmr::UseLocalStorage::::set(true); + } + + init_block::(1); + + let peak; + #[block] + { + peak = Nodes::::get(0) + } + + assert!(peak.is_some()); + } + + /// Generate ancestry proofs with `n` nodes and benchmark the verification logic. + /// These proofs are inflated, containing all the leafs, so we won't read any peak during + /// the verification. We need to account for the peaks separately. + #[benchmark] + fn n_items_proof_is_non_canonical(n: Linear<2, 512>) { + if !cfg!(feature = "test") { + pallet_mmr::UseLocalStorage::::set(true); + } + + for block_num in 1..=n { + init_block::(block_num); + } + let proof = Mmr::::generate_mock_ancestry_proof().unwrap(); + assert_eq!(proof.items.len(), n as usize); + + let is_non_canonical; + #[block] + { + is_non_canonical = as AncestryHelper>>::is_non_canonical( + &Commitment { + payload: Payload::from_single_entry( + known_payloads::MMR_ROOT_ID, + MerkleRootOf::::default().encode(), + ), + block_number: n.into(), + validator_set_id: 0, + }, + proof, + Mmr::::mmr_root(), + ); + }; + + assert_eq!(is_non_canonical, true); + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(Default::default()), + crate::mock::Test + ); +} diff --git a/substrate/frame/beefy-mmr/src/lib.rs b/substrate/frame/beefy-mmr/src/lib.rs index 195bbfbf2f29794ba3b09cab5e733c323cf58dfc..73119c3faa9b28abd1e1c6018939a9156e9f9276 100644 --- a/substrate/frame/beefy-mmr/src/lib.rs +++ b/substrate/frame/beefy-mmr/src/lib.rs @@ -35,27 +35,34 @@ extern crate alloc; -use sp_runtime::traits::{Convert, Header, Member}; +use sp_runtime::{ + generic::OpaqueDigestItemId, + traits::{Convert, Header, Member}, + SaturatedConversion, +}; use alloc::vec::Vec; use codec::Decode; -use pallet_mmr::{primitives::AncestryProof, LeafDataProvider, ParentNumberAndHash}; +use pallet_mmr::{primitives::AncestryProof, LeafDataProvider, NodesUtils, ParentNumberAndHash}; use sp_consensus_beefy::{ known_payloads, mmr::{BeefyAuthoritySet, BeefyDataProvider, BeefyNextAuthoritySet, MmrLeaf, MmrLeafVersion}, - AncestryHelper, Commitment, ConsensusLog, ValidatorSet as BeefyValidatorSet, + AncestryHelper, AncestryHelperWeightInfo, Commitment, ConsensusLog, + ValidatorSet as BeefyValidatorSet, }; -use frame_support::{crypto::ecdsa::ECDSAExt, traits::Get}; +use frame_support::{crypto::ecdsa::ECDSAExt, pallet_prelude::Weight, traits::Get}; use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor}; pub use pallet::*; -use sp_runtime::generic::OpaqueDigestItemId; +pub use weights::WeightInfo; +mod benchmarking; #[cfg(test)] mod mock; #[cfg(test)] mod tests; +mod weights; /// A BEEFY consensus digest item with MMR root hash. pub struct DepositBeefyDigest(core::marker::PhantomData); @@ -126,6 +133,8 @@ pub mod pallet { /// Retrieve arbitrary data that should be added to the mmr leaf type BeefyDataProvider: BeefyDataProvider; + + type WeightInfo: WeightInfo; } /// Details of current BEEFY authority set. @@ -263,6 +272,30 @@ where } } +impl AncestryHelperWeightInfo> for Pallet +where + T: pallet_mmr::Config, +{ + fn extract_validation_context() -> Weight { + ::WeightInfo::extract_validation_context() + } + + fn is_non_canonical(proof: &>>::Proof) -> Weight { + let mmr_utils = NodesUtils::new(proof.leaf_count); + let num_peaks = mmr_utils.number_of_peaks(); + + // The approximated cost of verifying an ancestry proof with `n` nodes. + // We add the previous peaks to the total number of nodes, + // since they have to be processed as well. + ::WeightInfo::n_items_proof_is_non_canonical( + proof.items.len().saturating_add(proof.prev_peaks.len()).saturated_into(), + ) + // `n_items_proof_is_non_canonical()` uses inflated proofs that contain all the leafs, + // where no peak needs to be read. So we need to also add the cost of reading the peaks. + .saturating_add(::WeightInfo::read_peak().saturating_mul(num_peaks)) + } +} + impl Pallet { /// Return the currently active BEEFY authority set proof. pub fn authority_set_proof() -> BeefyAuthoritySet> { diff --git a/substrate/frame/beefy-mmr/src/mock.rs b/substrate/frame/beefy-mmr/src/mock.rs index 1102f9677aaa4504c0432fd94338476dd6f5eaee..6756c618d706d2b0b6bc79560df954c93cdb54ea 100644 --- a/substrate/frame/beefy-mmr/src/mock.rs +++ b/substrate/frame/beefy-mmr/src/mock.rs @@ -37,6 +37,7 @@ use crate as pallet_beefy_mmr; pub use sp_consensus_beefy::{ ecdsa_crypto::AuthorityId as BeefyId, mmr::BeefyDataProvider, ConsensusLog, BEEFY_ENGINE_ID, }; +use sp_core::offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}; impl_opaque_keys! { pub struct MockSessionKeys { @@ -122,6 +123,7 @@ impl pallet_beefy_mmr::Config for Test { type LeafExtra = Vec; type BeefyDataProvider = DummyDataProvider; + type WeightInfo = (); } pub struct DummyDataProvider; @@ -191,5 +193,10 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<(u64, BeefyId)>) -> TestExt .assimilate_storage(&mut t) .unwrap(); - t.into() + let mut ext: TestExternalities = t.into(); + let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); + ext.register_extension(OffchainDbExt::new(offchain.clone())); + ext.register_extension(OffchainWorkerExt::new(offchain)); + + ext } diff --git a/substrate/frame/beefy-mmr/src/tests.rs b/substrate/frame/beefy-mmr/src/tests.rs index f99835a1dc0a5fc2535cdb6cd457351770978987..b126a01012b4e9e1c57dcc223b4981dd5fe6946a 100644 --- a/substrate/frame/beefy-mmr/src/tests.rs +++ b/substrate/frame/beefy-mmr/src/tests.rs @@ -24,10 +24,7 @@ use sp_consensus_beefy::{ AncestryHelper, Commitment, Payload, ValidatorSet, }; -use sp_core::{ - offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, - H256, -}; +use sp_core::H256; use sp_io::TestExternalities; use sp_runtime::{traits::Keccak256, DigestItem}; @@ -40,8 +37,6 @@ fn init_block(block: u64, maybe_parent_hash: Option) { System::initialize(&block, &parent_hash, &Default::default()); Session::on_initialize(block); Mmr::on_initialize(block); - Beefy::on_initialize(block); - BeefyMmr::on_initialize(block); } pub fn beefy_log(log: ConsensusLog) -> DigestItem { @@ -211,11 +206,6 @@ fn should_update_authorities() { fn extract_validation_context_should_work_correctly() { let mut ext = new_test_ext(vec![1, 2]); - // Register offchain ext. - let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); - ext.register_extension(OffchainDbExt::new(offchain.clone())); - ext.register_extension(OffchainWorkerExt::new(offchain)); - ext.execute_with(|| { init_block(1, None); let h1 = System::finalize(); @@ -262,13 +252,8 @@ fn is_non_canonical_should_work_correctly() { }); ext.persist_offchain_overlay(); - // Register offchain ext. - let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); - ext.register_extension(OffchainDbExt::new(offchain.clone())); - ext.register_extension(OffchainWorkerExt::new(offchain)); - ext.execute_with(|| { - let valid_proof = Mmr::generate_ancestry_proof(250, None).unwrap(); + let valid_proof = BeefyMmr::generate_proof(250, None).unwrap(); let mut invalid_proof = valid_proof.clone(); invalid_proof.items.push((300, Default::default())); @@ -343,7 +328,7 @@ fn is_non_canonical_should_work_correctly() { // - should return false, if the commitment is targeting the canonical chain // - should return true if the commitment is NOT targeting the canonical chain for prev_block_number in 1usize..=500 { - let proof = Mmr::generate_ancestry_proof(prev_block_number as u64, None).unwrap(); + let proof = BeefyMmr::generate_proof(prev_block_number as u64, None).unwrap(); assert_eq!( BeefyMmr::is_non_canonical( diff --git a/substrate/frame/beefy-mmr/src/weights.rs b/substrate/frame/beefy-mmr/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..c292f25400cc3689d7893c07c1ccbcb691ed4d7a --- /dev/null +++ b/substrate/frame/beefy-mmr/src/weights.rs @@ -0,0 +1,134 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_beefy_mmr` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/production/substrate-node +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_beefy_mmr +// --chain=dev +// --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/beefy-mmr/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_beefy_mmr`. +pub trait WeightInfo { + fn extract_validation_context() -> Weight; + fn read_peak() -> Weight; + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight; +} + +/// Weights for `pallet_beefy_mmr` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_461_000 picoseconds. + Weight::from_parts(7_669_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `3505` + // Minimum execution time: 6_137_000 picoseconds. + Weight::from_parts(6_423_000, 3505) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `1517` + // Minimum execution time: 10_687_000 picoseconds. + Weight::from_parts(14_851_626, 1517) + // Standard Error: 1_455 + .saturating_add(Weight::from_parts(961_703, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn extract_validation_context() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 7_461_000 picoseconds. + Weight::from_parts(7_669_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::Nodes` (r:1 w:0) + /// Proof: `Mmr::Nodes` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + fn read_peak() -> Weight { + // Proof Size summary in bytes: + // Measured: `333` + // Estimated: `3505` + // Minimum execution time: 6_137_000 picoseconds. + Weight::from_parts(6_423_000, 3505) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Mmr::RootHash` (r:1 w:0) + /// Proof: `Mmr::RootHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Mmr::NumberOfLeaves` (r:1 w:0) + /// Proof: `Mmr::NumberOfLeaves` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 512]`. + fn n_items_proof_is_non_canonical(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `325` + // Estimated: `1517` + // Minimum execution time: 10_687_000 picoseconds. + Weight::from_parts(14_851_626, 1517) + // Standard Error: 1_455 + .saturating_add(Weight::from_parts(961_703, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } +} diff --git a/substrate/frame/beefy/src/default_weights.rs b/substrate/frame/beefy/src/default_weights.rs index 70dd3bb02bf1e7f4ea8e6946329dafa207114ca9..6b83015459d61009efd1f076640f37b1ee79e53c 100644 --- a/substrate/frame/beefy/src/default_weights.rs +++ b/substrate/frame/beefy/src/default_weights.rs @@ -57,11 +57,6 @@ impl crate::WeightInfo for () { .saturating_add(DbWeight::get().reads(2)) } - // TODO: Calculate - fn report_fork_voting(_validator_count: u32, _max_nominators_per_validator: u32) -> Weight { - Weight::MAX - } - fn set_new_genesis() -> Weight { DbWeight::get().writes(1) } diff --git a/substrate/frame/beefy/src/lib.rs b/substrate/frame/beefy/src/lib.rs index 353ba876c7eda68661a498df52b46054c0aea9ba..cf690a9df339dcd3e93ad3886a84ba3da2085f8c 100644 --- a/substrate/frame/beefy/src/lib.rs +++ b/substrate/frame/beefy/src/lib.rs @@ -19,21 +19,33 @@ extern crate alloc; +mod default_weights; +mod equivocation; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + use alloc::{boxed::Box, vec::Vec}; use codec::{Encode, MaxEncodedLen}; +use log; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, pallet_prelude::*, traits::{Get, OneSessionHandler}, - weights::Weight, + weights::{constants::RocksDbWeight as DbWeight, Weight}, BoundedSlice, BoundedVec, Parameter, }; use frame_system::{ ensure_none, ensure_signed, pallet_prelude::{BlockNumberFor, HeaderFor, OriginFor}, }; -use log; +use sp_consensus_beefy::{ + AncestryHelper, AncestryHelperWeightInfo, AuthorityIndex, BeefyAuthorityId, ConsensusLog, + DoubleVotingProof, ForkVotingProof, FutureBlockVotingProof, OnNewValidatorSet, ValidatorSet, + BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID, +}; use sp_runtime::{ generic::DigestItem, traits::{IsMember, Member, One}, @@ -42,24 +54,10 @@ use sp_runtime::{ use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{offence::OffenceReportSystem, SessionIndex}; -use sp_consensus_beefy::{ - AncestryHelper, AuthorityIndex, BeefyAuthorityId, ConsensusLog, DoubleVotingProof, - ForkVotingProof, FutureBlockVotingProof, OnNewValidatorSet, ValidatorSet, BEEFY_ENGINE_ID, - GENESIS_AUTHORITY_SET_ID, -}; - -mod default_weights; -mod equivocation; -#[cfg(test)] -mod mock; -#[cfg(test)] -mod tests; - +use crate::equivocation::EquivocationEvidenceFor; pub use crate::equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot}; pub use pallet::*; -use crate::equivocation::EquivocationEvidenceFor; - const LOG_TARGET: &str = "runtime::beefy"; #[frame_support::pallet] @@ -102,7 +100,8 @@ pub mod pallet { type OnNewValidatorSet: OnNewValidatorSet<::BeefyId>; /// Hook for checking commitment canonicity. - type AncestryHelper: AncestryHelper>; + type AncestryHelper: AncestryHelper> + + AncestryHelperWeightInfo>; /// Weights for this pallet. type WeightInfo: WeightInfo; @@ -295,9 +294,10 @@ pub mod pallet { /// and validate the given key ownership proof against the extracted offender. /// If both are valid, the offence will be reported. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_fork_voting::( key_owner_proof.validator_count(), T::MaxNominators::get(), + &equivocation_proof.ancestry_proof ))] pub fn report_fork_voting( origin: OriginFor, @@ -329,9 +329,10 @@ pub mod pallet { /// if the block author is defined it will be defined as the equivocation /// reporter. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_fork_voting::( key_owner_proof.validator_count(), T::MaxNominators::get(), + &equivocation_proof.ancestry_proof ))] pub fn report_fork_voting_unsigned( origin: OriginFor, @@ -358,7 +359,7 @@ pub mod pallet { /// and validate the given key ownership proof against the extracted offender. /// If both are valid, the offence will be reported. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_future_block_voting( key_owner_proof.validator_count(), T::MaxNominators::get(), ))] @@ -389,7 +390,7 @@ pub mod pallet { /// if the block author is defined it will be defined as the equivocation /// reporter. #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::report_fork_voting( + #[pallet::weight(T::WeightInfo::report_future_block_voting( key_owner_proof.validator_count(), T::MaxNominators::get(), ))] @@ -740,15 +741,52 @@ pub trait WeightInfo { validator_count: u32, max_nominators_per_validator: u32, ) -> Weight; + + fn set_new_genesis() -> Weight; +} + +pub(crate) trait WeightInfoExt: WeightInfo { fn report_double_voting(validator_count: u32, max_nominators_per_validator: u32) -> Weight { Self::report_voting_equivocation(2, validator_count, max_nominators_per_validator) } - fn report_fork_voting(validator_count: u32, max_nominators_per_validator: u32) -> Weight; + + fn report_fork_voting( + validator_count: u32, + max_nominators_per_validator: u32, + ancestry_proof: &>>::Proof, + ) -> Weight { + let _weight = >>::extract_validation_context() + .saturating_add( + >>::is_non_canonical( + ancestry_proof, + ), + ) + .saturating_add(Self::report_voting_equivocation( + 1, + validator_count, + max_nominators_per_validator, + )); + + // TODO: https://github.com/paritytech/polkadot-sdk/issues/4523 - return `_weight` here. + // We return `Weight::MAX` currently in order to disallow this extrinsic for the moment. + // We need to check that the proof is optimal. + Weight::MAX + } + fn report_future_block_voting( validator_count: u32, max_nominators_per_validator: u32, ) -> Weight { - Self::report_voting_equivocation(1, validator_count, max_nominators_per_validator) + // checking if the report is for a future block + DbWeight::get() + .reads(1) + // check and report the equivocated vote + .saturating_add(Self::report_voting_equivocation( + 1, + validator_count, + max_nominators_per_validator, + )) } - fn set_new_genesis() -> Weight; } + +impl WeightInfoExt for T where T: WeightInfo {} diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index b423fa0bda89ffc3dc4f4cf1a0fa716851c012bd..5c79d8f7d7d7fb8933909452cc7ca8a1aefdc576 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -21,12 +21,13 @@ use std::vec; use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, - onchain, SequentialPhragmen, + onchain, SequentialPhragmen, Weight, }; use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ConstU32, ConstU64, KeyOwnerProofSystem, OnFinalize, OnInitialize}, }; +use frame_system::pallet_prelude::HeaderFor; use pallet_session::historical as pallet_session_historical; use sp_core::{crypto::KeyTypeId, ConstU128}; use sp_runtime::{ @@ -43,7 +44,7 @@ use sp_state_machine::BasicExternalities; use crate as pallet_beefy; pub use sp_consensus_beefy::{ecdsa_crypto::AuthorityId as BeefyId, ConsensusLog, BEEFY_ENGINE_ID}; -use sp_consensus_beefy::{AncestryHelper, Commitment}; +use sp_consensus_beefy::{AncestryHelper, AncestryHelperWeightInfo, Commitment}; impl_opaque_keys! { pub struct MockSessionKeys { @@ -131,6 +132,16 @@ impl AncestryHelper

for MockAncestryHelper { } } +impl AncestryHelperWeightInfo
for MockAncestryHelper { + fn extract_validation_context() -> Weight { + unimplemented!() + } + + fn is_non_canonical(_proof: &>>::Proof) -> Weight { + unimplemented!() + } +} + impl pallet_beefy::Config for Test { type BeefyId = BeefyId; type MaxAuthorities = ConstU32<100>; diff --git a/substrate/frame/beefy/src/tests.rs b/substrate/frame/beefy/src/tests.rs index a63b3532b6983402866728f558bf1d5cd1a4fc14..d75237205cac2a68537bed41a07b83fcf5ac4aa3 100644 --- a/substrate/frame/beefy/src/tests.rs +++ b/substrate/frame/beefy/src/tests.rs @@ -35,7 +35,7 @@ use sp_consensus_beefy::{ use sp_runtime::DigestItem; use sp_session::MembershipProof; -use crate::{self as beefy, mock::*, Call, Config, Error, WeightInfo}; +use crate::{self as beefy, mock::*, Call, Config, Error, WeightInfoExt}; fn init_block(block: u64) { System::set_block_number(block); @@ -765,7 +765,9 @@ fn report_double_voting_has_valid_weight() { // the weight depends on the size of the validator set, // but there's a lower bound of 100 validators. assert!((1..=100) - .map(|validators| ::WeightInfo::report_double_voting(validators, 1000)) + .map(|validators| <::WeightInfo as WeightInfoExt>::report_double_voting( + validators, 1000 + )) .collect::>() .windows(2) .all(|w| w[0] == w[1])); @@ -773,7 +775,9 @@ fn report_double_voting_has_valid_weight() { // after 100 validators the weight should keep increasing // with every extra validator. assert!((100..=1000) - .map(|validators| ::WeightInfo::report_double_voting(validators, 1000)) + .map(|validators| <::WeightInfo as WeightInfoExt>::report_double_voting( + validators, 1000 + )) .collect::>() .windows(2) .all(|w| w[0].ref_time() < w[1].ref_time())); diff --git a/substrate/frame/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 6b0751571cc9c1cc8b3284c2ea17a23ca48c5194..4c01c1f061b7a8e70251a22e46add05b86971d60 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -13,15 +13,11 @@ workspace = true [dependencies] frame-system = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true } [build-dependencies] parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } twox-hash = { workspace = true, default-features = true } -polkavm-linker = { workspace = true, optional = true } -anyhow = { workspace = true } - -[features] -riscv = ["polkavm-linker"] +anyhow = { workspace = true, default-features = true } 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/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/Cargo.toml b/substrate/frame/delegated-staking/Cargo.toml index 1bd17c59f1e795c3e0dbb382e39ede3b692c7d8b..8d5ccd342b6b639ae0644a5e1a14f0c0ed14be3e 100644 --- a/substrate/frame/delegated-staking/Cargo.toml +++ b/substrate/frame/delegated-staking/Cargo.toml @@ -18,6 +18,8 @@ frame-system = { workspace = true } scale-info = { features = ["derive"], workspace = true } sp-runtime = { workspace = true } sp-staking = { workspace = true } +sp-io = { workspace = true } +log = { workspace = true } [dev-dependencies] sp-core = { workspace = true, default-features = true } @@ -38,6 +40,7 @@ std = [ "frame-election-provider-support/std", "frame-support/std", "frame-system/std", + "log/std", "pallet-balances/std", "pallet-nomination-pools/std", "pallet-staking/std", diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index f8df9dfe7b46c9c1730321d657bbd9b3e11617c8..4e6812dee24930ad204784f57125dec07f907427 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -19,7 +19,7 @@ //! Implementations of public traits, namely [`DelegationInterface`] and [`OnStakingUpdate`]. use super::*; -use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator, OnStakingUpdate}; +use sp_staking::{DelegationInterface, DelegationMigrator, OnStakingUpdate}; impl DelegationInterface for Pallet { type Balance = BalanceOf; @@ -32,28 +32,34 @@ impl DelegationInterface for Pallet { .ok() } + fn agent_transferable_balance(agent: Agent) -> Option { + AgentLedgerOuter::::get(&agent.get()) + .map(|a| a.ledger.unclaimed_withdrawals) + .ok() + } + fn delegator_balance(delegator: Delegator) -> Option { Delegation::::get(&delegator.get()).map(|d| d.amount) } /// Delegate funds to an `Agent`. - fn delegate( - who: Delegator, + fn register_agent( agent: Agent, reward_account: &Self::AccountId, - amount: Self::Balance, ) -> DispatchResult { Pallet::::register_agent( RawOrigin::Signed(agent.clone().get()).into(), reward_account.clone(), - )?; + ) + } - // Delegate the funds from who to the `Agent` account. - Pallet::::delegate_to_agent(RawOrigin::Signed(who.get()).into(), agent.get(), amount) + /// Remove `Agent` registration. + fn remove_agent(agent: Agent) -> DispatchResult { + Pallet::::remove_agent(RawOrigin::Signed(agent.clone().get()).into()) } /// Add more delegation to the `Agent` account. - fn delegate_extra( + fn delegate( who: Delegator, agent: Agent, amount: Self::Balance, @@ -118,7 +124,7 @@ impl DelegationMigrator for Pallet { /// Only used for testing. #[cfg(feature = "runtime-benchmarks")] - fn drop_agent(agent: Agent) { + fn migrate_to_direct_staker(agent: Agent) { >::remove(agent.clone().get()); >::iter() .filter(|(_, delegation)| delegation.agent == agent.clone().get()) diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 8203f75133057dff3b2856a635b762876168da56..7b8d14b0a611897339f8c2104cc1ab3187ddd215 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -126,6 +126,7 @@ #![deny(rustdoc::broken_intra_doc_links)] mod impls; +pub mod migration; #[cfg(test)] mod mock; #[cfg(test)] @@ -148,16 +149,29 @@ use frame_support::{ }, Balanced, Inspect as FunInspect, Mutate as FunMutate, }, - tokens::{fungible::Credit, Fortitude, Precision, Preservation}, + tokens::{fungible::Credit, Fortitude, Precision, Preservation, Restriction}, Defensive, DefensiveOption, Imbalance, OnUnbalanced, }, }; +use sp_io::hashing::blake2_256; use sp_runtime::{ - traits::{AccountIdConversion, CheckedAdd, CheckedSub, Zero}, + traits::{CheckedAdd, CheckedSub, TrailingZeroInput, Zero}, ArithmeticError, DispatchResult, Perbill, RuntimeDebug, Saturating, }; use sp_staking::{Agent, Delegator, EraIndex, StakingInterface, StakingUnchecked}; +/// The log target of this pallet. +pub const LOG_TARGET: &str = "runtime::delegated-staking"; +// syntactic sugar for logging. +#[macro_export] +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + log::$level!( + target: $crate::LOG_TARGET, + concat!("[{:?}] 🏊‍♂️ ", $patter), >::block_number() $(, $values)* + ) + }; +} pub type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; @@ -304,6 +318,25 @@ pub mod pallet { Ok(()) } + /// Remove an account from being an `Agent`. + /// + /// This can only be called if the agent has no delegated funds, no pending slashes and no + /// unclaimed withdrawals. + pub fn remove_agent(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + let ledger = AgentLedger::::get(&who).ok_or(Error::::NotAgent)?; + + ensure!( + ledger.total_delegated == Zero::zero() && + ledger.pending_slash == Zero::zero() && + ledger.unclaimed_withdrawals == Zero::zero(), + Error::::NotAllowed + ); + + AgentLedger::::remove(&who); + Ok(()) + } + /// Migrate from a `Nominator` account to `Agent` account. /// /// The origin needs to @@ -371,9 +404,6 @@ pub mod pallet { ) -> DispatchResult { let agent = ensure_signed(origin)?; - // Ensure they have minimum delegation. - ensure!(amount >= T::Currency::minimum_balance(), Error::::NotEnoughFunds); - // Ensure delegator is sane. ensure!(!Self::is_agent(&delegator), Error::::NotAllowed); ensure!(!Self::is_delegator(&delegator), Error::::NotAllowed); @@ -447,7 +477,9 @@ impl Pallet { /// Derive a (keyless) pot account from the given agent account and account type. fn sub_account(account_type: AccountType, acc: T::AccountId) -> T::AccountId { - T::PalletId::get().into_sub_account_truncating((account_type, acc.clone())) + let entropy = (T::PalletId::get(), acc, account_type).using_encoded(blake2_256); + Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) + .expect("infinite length input; no invalid inputs for type; qed") } /// Held balance of a delegator. @@ -472,13 +504,8 @@ impl Pallet { /// Registers a new agent in the system. fn do_register_agent(who: &T::AccountId, reward_account: &T::AccountId) { + // TODO: Consider taking a deposit for being an agent. AgentLedger::::new(reward_account).update(who); - - // Agent does not hold balance of its own but this pallet will provide for this to exist. - // This is expected to be a keyless account and not created by any user directly so safe. - // TODO: Someday if we allow anyone to be an agent, we should take a deposit for - // being a delegator. - frame_system::Pallet::::inc_providers(who); } /// Migrate existing staker account `who` to an `Agent` account. @@ -489,9 +516,6 @@ impl Pallet { // transferred to actual delegator. let proxy_delegator = Self::generate_proxy_delegator(Agent::from(who.clone())); - // Keep proxy delegator alive until all funds are migrated. - frame_system::Pallet::::inc_providers(&proxy_delegator.clone().get()); - // Get current stake let stake = T::CoreStaking::stake(who)?; @@ -513,7 +537,6 @@ impl Pallet { T::CoreStaking::set_payee(who, reward_account)?; // delegate all transferred funds back to agent. Self::do_delegate(proxy_delegator, Agent::from(who.clone()), amount_to_transfer)?; - // if the transferred/delegated amount was greater than the stake, mark the extra as // unclaimed withdrawal. let unclaimed_withdraws = amount_to_transfer @@ -557,21 +580,23 @@ impl Pallet { let delegator = delegator.get(); let mut ledger = AgentLedger::::get(&agent).ok_or(Error::::NotAgent)?; + + if let Some(mut existing_delegation) = Delegation::::get(&delegator) { + ensure!(existing_delegation.agent == agent, Error::::InvalidDelegation); + // update amount and return the updated delegation. + existing_delegation.amount = existing_delegation + .amount + .checked_add(&amount) + .ok_or(ArithmeticError::Overflow)?; + existing_delegation + } else { + Delegation::::new(&agent, amount) + } + .update(&delegator); + // try to hold the funds. T::Currency::hold(&HoldReason::StakingDelegation.into(), &delegator, amount)?; - let new_delegation_amount = - if let Some(existing_delegation) = Delegation::::get(&delegator) { - ensure!(existing_delegation.agent == agent, Error::::InvalidDelegation); - existing_delegation - .amount - .checked_add(&amount) - .ok_or(ArithmeticError::Overflow)? - } else { - amount - }; - - Delegation::::new(&agent, new_delegation_amount).update_or_kill(&delegator); ledger.total_delegated = ledger.total_delegated.checked_add(&amount).ok_or(ArithmeticError::Overflow)?; ledger.update(&agent); @@ -599,52 +624,24 @@ impl Pallet { ensure!(delegation.agent == agent, Error::::NotAgent); ensure!(delegation.amount >= amount, Error::::NotEnoughFunds); - // if we do not already have enough funds to be claimed, try withdraw some more. - // keep track if we killed the staker in the process. - let stash_killed = if agent_ledger.ledger.unclaimed_withdrawals < amount { + // if we do not already have enough funds to be claimed, try to withdraw some more. + if agent_ledger.ledger.unclaimed_withdrawals < amount { // withdraw account. - let killed = T::CoreStaking::withdraw_unbonded(agent.clone(), num_slashing_spans) + let _ = T::CoreStaking::withdraw_unbonded(agent.clone(), num_slashing_spans) .map_err(|_| Error::::WithdrawFailed)?; // reload agent from storage since withdrawal might have changed the state. agent_ledger = agent_ledger.reload()?; - Some(killed) - } else { - None - }; + } // if we still do not have enough funds to release, abort. ensure!(agent_ledger.ledger.unclaimed_withdrawals >= amount, Error::::NotEnoughFunds); + agent_ledger.remove_unclaimed_withdraw(amount)?.update(); - // Claim withdraw from agent. Kill agent if no delegation left. - // TODO: Ideally if there is a register, there should be an unregister that should - // clean up the agent. Can be improved in future. - if agent_ledger.remove_unclaimed_withdraw(amount)?.update_or_kill()? { - match stash_killed { - Some(killed) => { - // this implies we did a `CoreStaking::withdraw` before release. Ensure - // we killed the staker as well. - ensure!(killed, Error::::BadState); - }, - None => { - // We did not do a `CoreStaking::withdraw` before release. Ensure staker is - // already killed in `CoreStaking`. - ensure!(T::CoreStaking::status(&agent).is_err(), Error::::BadState); - }, - } - - // Remove provider reference for `who`. - let _ = frame_system::Pallet::::dec_providers(&agent).defensive(); - } - - // book keep delegation delegation.amount = delegation .amount .checked_sub(&amount) .defensive_ok_or(ArithmeticError::Overflow)?; - // remove delegator if nothing delegated anymore - delegation.update_or_kill(&delegator); - let released = T::Currency::release( &HoldReason::StakingDelegation.into(), &delegator, @@ -654,6 +651,9 @@ impl Pallet { defensive_assert!(released == amount, "hold should have been released fully"); + // update delegation. + delegation.update(&delegator); + Self::deposit_event(Event::::Released { agent, delegator, amount }); Ok(()) @@ -672,49 +672,34 @@ impl Pallet { let mut source_delegation = Delegators::::get(&source_delegator).defensive_ok_or(Error::::BadState)?; - // some checks that must have already been checked before. + // ensure source has enough funds to migrate. ensure!(source_delegation.amount >= amount, Error::::NotEnoughFunds); debug_assert!( !Self::is_delegator(&destination_delegator) && !Self::is_agent(&destination_delegator) ); let agent = source_delegation.agent.clone(); - // update delegations - Delegation::::new(&agent, amount).update_or_kill(&destination_delegator); + // create a new delegation for destination delegator. + Delegation::::new(&agent, amount).update(&destination_delegator); source_delegation.amount = source_delegation .amount .checked_sub(&amount) .defensive_ok_or(Error::::BadState)?; - source_delegation.update_or_kill(&source_delegator); - - // release funds from source - let released = T::Currency::release( + // transfer the held amount in `source_delegator` to `destination_delegator`. + let _ = T::Currency::transfer_on_hold( &HoldReason::StakingDelegation.into(), - &source_delegator, - amount, - Precision::BestEffort, - )?; - - defensive_assert!(released == amount, "hold should have been released fully"); - - // transfer the released amount to `destination_delegator`. - let post_balance = T::Currency::transfer( &source_delegator, &destination_delegator, amount, - Preservation::Expendable, - ) - .map_err(|_| Error::::BadState)?; - - // if balance is zero, clear provider for source (proxy) delegator. - if post_balance == Zero::zero() { - let _ = frame_system::Pallet::::dec_providers(&source_delegator).defensive(); - } + Precision::Exact, + Restriction::OnHold, + Fortitude::Polite, + )?; - // hold the funds again in the new delegator account. - T::Currency::hold(&HoldReason::StakingDelegation.into(), &destination_delegator, amount)?; + // update source delegation. + source_delegation.update(&source_delegator); Self::deposit_event(Event::::MigratedDelegation { agent, @@ -756,7 +741,7 @@ impl Pallet { agent_ledger.remove_slash(actual_slash).save(); delegation.amount = delegation.amount.checked_sub(&actual_slash).ok_or(ArithmeticError::Overflow)?; - delegation.update_or_kill(&delegator); + delegation.update(&delegator); if let Some(reporter) = maybe_reporter { let reward_payout: BalanceOf = T::SlashRewardFraction::get() * actual_slash; @@ -805,18 +790,21 @@ impl Pallet { ledgers: BTreeMap>, ) -> Result<(), sp_runtime::TryRuntimeError> { for (agent, ledger) in ledgers { - ensure!( - matches!( - T::CoreStaking::status(&agent).expect("agent should be bonded"), - sp_staking::StakerStatus::Nominator(_) | sp_staking::StakerStatus::Idle - ), - "agent should be bonded and not validator" - ); + let staked_value = ledger.stakeable_balance(); + + if !staked_value.is_zero() { + ensure!( + matches!( + T::CoreStaking::status(&agent).expect("agent should be bonded"), + sp_staking::StakerStatus::Nominator(_) | sp_staking::StakerStatus::Idle + ), + "agent should be bonded and not validator" + ); + } ensure!( ledger.stakeable_balance() >= - T::CoreStaking::total_stake(&agent) - .expect("agent should exist as a nominator"), + T::CoreStaking::total_stake(&agent).unwrap_or_default(), "Cannot stake more than balance" ); } diff --git a/substrate/frame/delegated-staking/src/migration.rs b/substrate/frame/delegated-staking/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..8bc7312c4eabb4bfc8d938809f4572136f2a25a7 --- /dev/null +++ b/substrate/frame/delegated-staking/src/migration.rs @@ -0,0 +1,107 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use frame_support::traits::OnRuntimeUpgrade; + +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; + +pub mod unversioned { + use super::*; + #[cfg(feature = "try-runtime")] + use alloc::vec::Vec; + use sp_runtime::traits::AccountIdConversion; + + /// Migrates `ProxyDelegator` accounts with better entropy than the old logic which didn't take + /// into account all the bytes of the agent account ID. + pub struct ProxyDelegatorMigration(PhantomData<(T, MaxAgents)>); + + impl> OnRuntimeUpgrade for ProxyDelegatorMigration { + fn on_runtime_upgrade() -> Weight { + let mut weight = Weight::zero(); + let old_proxy_delegator = |agent: T::AccountId| { + T::PalletId::get() + .into_sub_account_truncating((AccountType::ProxyDelegator, agent.clone())) + }; + + Agents::::iter_keys().take(MaxAgents::get() as usize).for_each(|agent| { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); + let old_proxy = old_proxy_delegator(agent.clone()); + + // if delegation does not exist, it does not need to be migrated. + if let Some(delegation) = Delegation::::get(&old_proxy) { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); + + let new_proxy = + Pallet::::generate_proxy_delegator(Agent::from(agent.clone())); + + // accrue read writes for `do_migrate_delegation` + weight.saturating_accrue(T::DbWeight::get().reads_writes(8, 8)); + let _ = Pallet::::do_migrate_delegation( + Delegator::from(old_proxy.clone()), + new_proxy.clone(), + delegation.amount, + ) + .map_err(|e| { + log!( + error, + "Failed to migrate old proxy delegator {:?} to new proxy {:?} for agent {:?} with error: {:?}", + old_proxy, + new_proxy, + agent, + e, + ); + }); + }; + }); + + log!(info, "Finished migrating old proxy delegator accounts to new ones"); + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_data: Vec) -> Result<(), TryRuntimeError> { + let mut unmigrated_count = 0; + let old_proxy_delegator = |agent: T::AccountId| { + T::PalletId::get() + .into_sub_account_truncating((AccountType::ProxyDelegator, agent.clone())) + }; + + Agents::::iter_keys().take(MaxAgents::get() as usize).for_each(|agent| { + let old_proxy: T::AccountId = old_proxy_delegator(agent.clone()); + let held_balance = Pallet::::held_balance_of(Delegator::from(old_proxy.clone())); + let delegation = Delegation::::get(&old_proxy); + if delegation.is_some() || !held_balance.is_zero() { + log!( + error, + "Old proxy delegator {:?} for agent {:?} is not migrated.", + old_proxy, + agent, + ); + unmigrated_count += 1; + } + }); + + if unmigrated_count > 0 { + Err(TryRuntimeError::Other("Some old proxy delegator accounts are not migrated.")) + } else { + Ok(()) + } + } + } +} diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index ade0872dd3900d0f44962de2e89e4b47247ce06d..2c965e18b1b335d25ec4fbd571c3e032a782dba5 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -334,6 +334,36 @@ fn apply_pending_slash() { }); } +#[test] +fn allow_full_amount_to_be_delegated() { + ExtBuilder::default().build_and_execute(|| { + let agent: AccountId = 200; + let reward_acc: AccountId = 201; + let delegator: AccountId = 300; + + // set intention to accept delegation. + fund(&agent, 1000); + assert_ok!(DelegatedStaking::register_agent(RawOrigin::Signed(agent).into(), reward_acc)); + + // delegate to this account + fund(&delegator, 1000); + assert_ok!(DelegatedStaking::delegate_to_agent( + RawOrigin::Signed(delegator).into(), + agent, + 1000 + )); + + // verify + assert!(DelegatedStaking::is_agent(&agent)); + assert_eq!(DelegatedStaking::stakeable_balance(Agent::from(agent)), 1000); + assert_eq!( + Balances::balance_on_hold(&HoldReason::StakingDelegation.into(), &delegator), + 1000 + ); + assert_eq!(DelegatedStaking::held_balance_of(Delegator::from(delegator)), 1000); + }); +} + /// Integration tests with pallet-staking. mod staking_integration { use super::*; @@ -625,32 +655,42 @@ mod staking_integration { #[test] fn migration_works() { ExtBuilder::default().build_and_execute(|| { + // initial era + start_era(1); + // add a nominator let staked_amount = 4000; let agent_amount = 5000; - fund(&200, agent_amount); + let agent = 200; + fund(&agent, agent_amount); assert_ok!(Staking::bond( - RuntimeOrigin::signed(200), + RuntimeOrigin::signed(agent), staked_amount, RewardDestination::Account(201) )); - assert_ok!(Staking::nominate(RuntimeOrigin::signed(200), vec![GENESIS_VALIDATOR],)); - let init_stake = Staking::stake(&200).unwrap(); + assert_ok!(Staking::nominate(RuntimeOrigin::signed(agent), vec![GENESIS_VALIDATOR],)); + let init_stake = Staking::stake(&agent).unwrap(); // scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304) // in equal parts. lets try to migrate this nominator into delegate based stake. // all balance currently is in 200 - assert_eq!(Balances::free_balance(200), agent_amount); + assert_eq!(Balances::free_balance(agent), agent_amount); // to migrate, nominator needs to set an account as a proxy delegator where staked funds // will be moved and delegated back to this old nominator account. This should be funded // with at least ED. let proxy_delegator = - DelegatedStaking::generate_proxy_delegator(Agent::from(200)).get(); + DelegatedStaking::generate_proxy_delegator(Agent::from(agent)).get(); - assert_ok!(DelegatedStaking::migrate_to_agent(RawOrigin::Signed(200).into(), 201)); + assert_ok!(DelegatedStaking::migrate_to_agent(RawOrigin::Signed(agent).into(), 201)); + // after migration, funds are moved to proxy delegator, still a provider exists. + assert_eq!(System::providers(&agent), 1); + assert_eq!(Balances::free_balance(agent), 0); + // proxy delegator has one provider as well with no free balance. + assert_eq!(System::providers(&proxy_delegator), 1); + assert_eq!(Balances::free_balance(proxy_delegator), 0); // verify all went well let mut expected_proxy_delegated_amount = agent_amount; @@ -659,12 +699,12 @@ mod staking_integration { expected_proxy_delegated_amount ); // stake amount is transferred from delegate to proxy delegator account. - assert_eq!(Balances::free_balance(200), 0); - assert_eq!(Staking::stake(&200).unwrap(), init_stake); - assert_eq!(get_agent_ledger(&200).ledger.effective_balance(), agent_amount); - assert_eq!(get_agent_ledger(&200).available_to_bond(), 0); + assert_eq!(Balances::free_balance(agent), 0); + assert_eq!(Staking::stake(&agent).unwrap(), init_stake); + assert_eq!(get_agent_ledger(&agent).ledger.effective_balance(), agent_amount); + assert_eq!(get_agent_ledger(&agent).available_to_bond(), 0); assert_eq!( - get_agent_ledger(&200).ledger.unclaimed_withdrawals, + get_agent_ledger(&agent).ledger.unclaimed_withdrawals, agent_amount - staked_amount ); @@ -672,14 +712,15 @@ mod staking_integration { let delegator_share = agent_amount / 4; for delegator in 300..304 { assert_eq!(Balances::free_balance(delegator), 0); - // fund them with ED - fund(&delegator, ExistentialDeposit::get()); - // migrate 1/4th amount into each delegator + assert_eq!(System::providers(&delegator), 0); + + // No pre-balance needed to migrate delegator. assert_ok!(DelegatedStaking::migrate_delegation( - RawOrigin::Signed(200).into(), + RawOrigin::Signed(agent).into(), delegator, delegator_share )); + assert_eq!(System::providers(&delegator), 1); assert_eq!( Balances::balance_on_hold(&HoldReason::StakingDelegation.into(), &delegator), delegator_share @@ -694,20 +735,123 @@ mod staking_integration { ); // delegate stake is unchanged. - assert_eq!(Staking::stake(&200).unwrap(), init_stake); - assert_eq!(get_agent_ledger(&200).ledger.effective_balance(), agent_amount); - assert_eq!(get_agent_ledger(&200).available_to_bond(), 0); + assert_eq!(Staking::stake(&agent).unwrap(), init_stake); + assert_eq!(get_agent_ledger(&agent).ledger.effective_balance(), agent_amount); + assert_eq!(get_agent_ledger(&agent).available_to_bond(), 0); assert_eq!( - get_agent_ledger(&200).ledger.unclaimed_withdrawals, + get_agent_ledger(&agent).ledger.unclaimed_withdrawals, agent_amount - staked_amount ); } // cannot use migrate delegator anymore assert_noop!( - DelegatedStaking::migrate_delegation(RawOrigin::Signed(200).into(), 305, 1), + DelegatedStaking::migrate_delegation(RawOrigin::Signed(agent).into(), 305, 1), Error::::NotEnoughFunds ); + + // no provider left on proxy delegator since all funds are migrated + assert_eq!(System::providers(&proxy_delegator), 0); + + // withdraw all delegations from delegators + assert_ok!(Staking::chill(RuntimeOrigin::signed(agent))); + assert_ok!(Staking::unbond(RawOrigin::Signed(agent).into(), staked_amount)); + start_era(4); + assert_ok!(Staking::withdraw_unbonded(RawOrigin::Signed(agent).into(), 0)); + for delegator in 300..304 { + assert_ok!(DelegatedStaking::release_delegation( + RawOrigin::Signed(agent).into(), + delegator, + delegator_share, + 0 + )); + // delegator is cleaned up from storage. + assert!(!Delegators::::contains_key(delegator)); + // has free balance now + assert_eq!(Balances::free_balance(delegator), delegator_share); + // and only one provider as delegator_share > ED + assert_eq!(System::providers(&delegator), 1); + } + + // Agent can be removed now. + assert_ok!(DelegatedStaking::remove_agent(RawOrigin::Signed(agent).into())); + // agent is correctly removed. + assert!(!Agents::::contains_key(agent)); + // and no provider left. + assert_eq!(System::providers(&agent), 0); + }); + } + + #[test] + fn accounts_are_cleaned_up() { + ExtBuilder::default().build_and_execute(|| { + let agent: AccountId = 200; + let reward_acc: AccountId = 201; + let delegator: AccountId = 300; + + // set intention to accept delegation. + fund(&agent, 1000); + + // Agent is provided since it has balance > ED. + assert_eq!(System::providers(&agent), 1); + assert_ok!(DelegatedStaking::register_agent( + RawOrigin::Signed(agent).into(), + reward_acc + )); + // becoming an agent adds another provider. + assert_eq!(System::providers(&agent), 2); + + // delegate to this account + fund(&delegator, 1000); + // account has one provider since its funded. + assert_eq!(System::providers(&delegator), 1); + assert_ok!(DelegatedStaking::delegate_to_agent( + RawOrigin::Signed(delegator).into(), + agent, + 500 + )); + // delegator has an extra provider now. + assert_eq!(System::providers(&delegator), 2); + // all 1000 tokens including ED can be held. + assert_ok!(DelegatedStaking::delegate_to_agent( + RawOrigin::Signed(delegator).into(), + agent, + 500 + )); + // free balance dropping below ED will reduce a provider, but it still has one provider + // left. + assert_eq!(System::providers(&delegator), 1); + + // withdraw all delegation + assert_ok!(Staking::unbond(RawOrigin::Signed(agent).into(), 1000)); + start_era(4); + assert_ok!(Staking::withdraw_unbonded(RawOrigin::Signed(agent).into(), 0)); + + // Since delegations are still left, agents cannot be removed yet from storage. + assert_noop!( + DelegatedStaking::remove_agent(RawOrigin::Signed(agent).into()), + Error::::NotAllowed + ); + + assert_ok!(DelegatedStaking::release_delegation( + RawOrigin::Signed(agent).into(), + delegator, + 1000, + 0 + )); + + // now agents can be removed. + assert_ok!(DelegatedStaking::remove_agent(RawOrigin::Signed(agent).into())); + + // agent and delegator provider is decremented. + assert_eq!(System::providers(&delegator), 1); + assert_eq!(System::providers(&agent), 1); + + // if we transfer all funds, providers are removed. + assert_ok!(Balances::transfer_all(RawOrigin::Signed(delegator).into(), 1337, false)); + assert_ok!(Balances::transfer_all(RawOrigin::Signed(agent).into(), 1337, false)); + assert_eq!(System::providers(&delegator), 0); + assert_eq!(System::providers(&agent), 0); }); } } @@ -943,7 +1087,7 @@ mod pool_integration { vec![ PoolsEvent::Withdrawn { member: 302, pool_id, balance: 100, points: 100 }, PoolsEvent::Withdrawn { member: 303, pool_id, balance: 200, points: 200 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 303 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 303, released_balance: 0 }, ] ); @@ -1055,7 +1199,7 @@ mod pool_integration { balance: creator_stake, points: creator_stake, }, - PoolsEvent::MemberRemoved { pool_id, member: creator }, + PoolsEvent::MemberRemoved { pool_id, member: creator, released_balance: 0 }, PoolsEvent::Destroyed { pool_id }, ] ); diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index 24b4573565441301c3a63e4adf159488b44feb73..a78aa3f559060080344b84002d099ca8cb18caaa 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -64,15 +64,25 @@ impl Delegation { ) } - /// Save self to storage. If the delegation amount is zero, remove the delegation. - pub(crate) fn update_or_kill(self, key: &T::AccountId) { - // Clean up if no delegation left. - if self.amount == Zero::zero() { - >::remove(key); - return + /// Save self to storage. + /// + /// If the delegation amount is zero, remove the delegation. Also adds and removes provider + /// reference as needed. + pub(crate) fn update(self, key: &T::AccountId) { + if >::contains_key(key) { + // Clean up if no delegation left. + if self.amount == Zero::zero() { + >::remove(key); + // Remove provider if no delegation left. + let _ = frame_system::Pallet::::dec_providers(key).defensive(); + return + } + } else { + // this is a new delegation. Provide for this account. + frame_system::Pallet::::inc_providers(key); } - >::insert(key, self) + >::insert(key, self); } } @@ -118,10 +128,24 @@ impl AgentLedger { } /// Save self to storage with the given key. + /// + /// Increments provider count if this is a new agent. pub(crate) fn update(self, key: &T::AccountId) { + if !>::contains_key(key) { + // This is a new agent. Provide for this account. + frame_system::Pallet::::inc_providers(key); + } >::insert(key, self) } + /// Remove self from storage. + pub(crate) fn remove(key: &T::AccountId) { + debug_assert!(>::contains_key(key), "Agent should exist in storage"); + >::remove(key); + // Remove provider reference. + let _ = frame_system::Pallet::::dec_providers(key).defensive(); + } + /// Effective total balance of the `Agent`. /// /// This takes into account any slashes reported to `Agent` but unapplied. @@ -251,25 +275,10 @@ impl AgentLedgerOuter { self.ledger.update(&key) } - /// Save self and remove if no delegation left. - /// - /// Returns: - /// - true if agent killed. - /// - error if the delegate is in an unexpected state. - pub(crate) fn update_or_kill(self) -> Result { + /// Update agent ledger. + pub(crate) fn update(self) { let key = self.key; - // see if delegate can be killed - if self.ledger.total_delegated == Zero::zero() { - ensure!( - self.ledger.unclaimed_withdrawals == Zero::zero() && - self.ledger.pending_slash == Zero::zero(), - Error::::BadState - ); - >::remove(key); - return Ok(true) - } self.ledger.update(&key); - Ok(false) } /// Reloads self from storage. diff --git a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs index 838ba29a621247593a13f472082238bde1075e40..3d2360d63a7328ef626c82f804fa840012099d4e 100644 --- a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs +++ b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/tests.rs @@ -27,6 +27,7 @@ use crate::{ System, }, }; +use codec::Decode; use frame_support::traits::OnRuntimeUpgrade; use pallet_migrations::WeightInfo as _; @@ -51,10 +52,18 @@ fn lazy_migration_works() { let mut last_decodable = 0; for block in 2..=65 { run_to_block(block); + let mut decodable = 0; for i in 0..1024 { - if crate::MyMap::::get(i).is_some() { + let key = crate::MyMap::::hashed_key_for(i); + let value = + frame_support::storage::unhashed::get_raw(&key[..]).expect("value exists"); + + if let Ok(value) = u64::decode(&mut &value[..]) { + assert_eq!(value, i as u64); decodable += 1; + } else { + assert_eq!(u32::decode(&mut &value[..]).expect("not migrated yet"), i); } } 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/identity/README.md b/substrate/frame/identity/README.md index 0203656eff46b6eb49726704a535e68dccad60f2..94b2ae0231d76629b0e415995adebc8426004fc7 100644 --- a/substrate/frame/identity/README.md +++ b/substrate/frame/identity/README.md @@ -1,9 +1,11 @@ -# Identity Module +# pallet-identity -- [`identity::Config`](https://docs.rs/pallet-identity/latest/pallet_identity/trait.Config.html) -- [`Call`](https://docs.rs/pallet-identity/latest/pallet_identity/enum.Call.html) +## Identity Pallet -## Overview +- [`Config`] +- [`Call`] + +### Overview A federated naming system, allowing for multiple registrars to be added from a specified origin. Registrars can set a fee to provide identity-verification service. Anyone can put forth a @@ -23,32 +25,53 @@ by definition, these have equivalent ownership and each has an individual name. The number of registrars should be limited, and the deposit made sufficiently large, to ensure no state-bloat attack is viable. -## Interface +#### Usernames + +The pallet provides functionality for username authorities to issue usernames. When an account +receives a username, they get a default instance of `IdentityInfo`. Usernames also serve as a +reverse lookup from username to account. + +Username authorities are given an allocation by governance to prevent state bloat. Usernames +impose no cost or deposit on the user. -### Dispatchable Functions +Users can have multiple usernames that map to the same `AccountId`, however one `AccountId` can +only map to a single username, known as the *primary*. -#### For general users +### Interface + +#### Dispatchable Functions + +##### For General Users - `set_identity` - Set the associated identity of an account; a small deposit is reserved if not already taken. - `clear_identity` - Remove an account's associated identity; the deposit is returned. - `request_judgement` - Request a judgement from a registrar, paying a fee. - `cancel_request` - Cancel the previous request for a judgement. +- `accept_username` - Accept a username issued by a username authority. +- `remove_expired_approval` - Remove a username that was issued but never accepted. +- `set_primary_username` - Set a given username as an account's primary. +- `remove_dangling_username` - Remove a username that maps to an account without an identity. -#### For general users with sub-identities +##### For General Users with Sub-Identities - `set_subs` - Set the sub-accounts of an identity. - `add_sub` - Add a sub-identity to an identity. - `remove_sub` - Remove a sub-identity of an identity. - `rename_sub` - Rename a sub-identity of an identity. - `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity). -#### For registrars +##### For Registrars - `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar. - `set_fields` - Set the fields that a registrar cares about in their judgements. - `provide_judgement` - Provide a judgement to an identity. -#### For super-users +##### For Username Authorities +- `set_username_for` - Set a username for a given account. The account must approve it. + +##### For Superusers - `add_registrar` - Add a new registrar to the system. - `kill_identity` - Forcibly remove the associated identity; the deposit is lost. +- `add_username_authority` - Add an account with the ability to issue usernames. +- `remove_username_authority` - Remove an account with the ability to issue usernames. [`Call`]: ./enum.Call.html [`Config`]: ./trait.Config.html diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index 0ab44711bcf5d854c3c158434740fdbaf1931b1b..7dfe95c83361c6f6e0d70ac5bfcd43bf7d87788b 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -240,6 +240,11 @@ pub mod pallet { pub type Nodes, I: 'static = ()> = StorageMap<_, Identity, NodeIndex, HashOf, OptionQuery>; + /// Helper flag used in the runtime benchmarks for the initial setup. + #[cfg(feature = "runtime-benchmarks")] + #[pallet::storage] + pub type UseLocalStorage = StorageValue<_, bool, ValueQuery>; + #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { @@ -439,6 +444,14 @@ impl, I: 'static> Pallet { mmr.generate_ancestry_proof(prev_leaf_count) } + #[cfg(feature = "runtime-benchmarks")] + pub fn generate_mock_ancestry_proof() -> Result>, Error> + { + let leaf_count = Self::block_num_to_leaf_count(>::block_number())?; + let mmr: ModuleMmr = mmr::Mmr::new(leaf_count); + mmr.generate_mock_ancestry_proof() + } + pub fn verify_ancestry_proof( root: HashOf, ancestry_proof: primitives::AncestryProof>, diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs index 2b46357c50723dfa8299a42fab1c9030ad0077fa..f9a4580b9bb30b34d6d5c18a0ddcd4fee7100e52 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -156,7 +156,7 @@ where } /// Return the internal size of the MMR (number of nodes). - #[cfg(test)] + #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn size(&self) -> NodeIndex { self.mmr.mmr_size() } @@ -252,4 +252,48 @@ where .collect(), }) } + + /// Generate an inflated ancestry proof for the latest leaf in the MMR. + /// + /// The generated proof contains all the leafs in the MMR, so this way we can generate a proof + /// with exactly `leaf_count` items. + #[cfg(feature = "runtime-benchmarks")] + pub fn generate_mock_ancestry_proof( + &self, + ) -> Result>, Error> { + use crate::ModuleMmr; + use alloc::vec; + use sp_mmr_primitives::mmr_lib::helper; + + let mmr: ModuleMmr = Mmr::new(self.leaves); + let store = >::default(); + + let mut prev_peaks = vec![]; + for peak_pos in helper::get_peaks(mmr.size()) { + let peak = store + .get_elem(peak_pos) + .map_err(|_| Error::GenerateProof)? + .ok_or(Error::GenerateProof)? + .hash(); + prev_peaks.push(peak); + } + + let mut proof_items = vec![]; + for leaf_idx in 0..self.leaves { + let leaf_pos = NodesUtils::leaf_index_to_leaf_node_index(leaf_idx); + let leaf = store + .get_elem(leaf_pos) + .map_err(|_| Error::GenerateProof)? + .ok_or(Error::GenerateProof)? + .hash(); + proof_items.push((leaf_pos, leaf)); + } + + Ok(sp_mmr_primitives::AncestryProof { + prev_peaks, + prev_leaf_count: self.leaves, + leaf_count: self.leaves, + items: proof_items, + }) + } } diff --git a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs index a390898014846077f4547a355909e074a2adcf5a..02852388b41715cd0b5272c4609df7b8432ede9b 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs @@ -22,7 +22,6 @@ use codec::Encode; use core::iter::Peekable; use log::{debug, trace}; use sp_core::offchain::StorageKind; -use sp_io::offchain_index; use sp_mmr_primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils}; use crate::{ @@ -47,6 +46,26 @@ pub struct RuntimeStorage; /// DOES NOT support adding new items to the MMR. pub struct OffchainStorage; +impl OffchainStorage { + fn get(key: &[u8]) -> Option> { + sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) + } + + #[cfg(not(feature = "runtime-benchmarks"))] + fn set, I: 'static>(key: &[u8], value: &[u8]) { + sp_io::offchain_index::set(key, value); + } + + #[cfg(feature = "runtime-benchmarks")] + fn set, I: 'static>(key: &[u8], value: &[u8]) { + if crate::pallet::UseLocalStorage::::get() { + sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, key, value); + } else { + sp_io::offchain_index::set(key, value); + } + } +} + /// A storage layer for MMR. /// /// There are two different implementations depending on the use case. @@ -78,7 +97,7 @@ where pos, ancestor_leaf_idx, key ); // Try to retrieve the element from Off-chain DB. - if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + if let Some(elem) = OffchainStorage::get(&key) { return Ok(codec::Decode::decode(&mut &*elem).ok()) } @@ -93,8 +112,7 @@ where pos, ancestor_leaf_idx, ancestor_parent_hash, temp_key ); // Retrieve the element from Off-chain DB. - Ok(sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &temp_key) - .and_then(|v| codec::Decode::decode(&mut &*v).ok())) + Ok(OffchainStorage::get(&temp_key).and_then(|v| codec::Decode::decode(&mut &*v).ok())) } } @@ -203,8 +221,7 @@ where target: "runtime::mmr::offchain", "offchain db set: pos {} parent_hash {:?} key {:?}", pos, parent_hash, temp_key ); - // Indexing API is used to store the full node content. - offchain_index::set(&temp_key, &encoded_node); + OffchainStorage::set::(&temp_key, &encoded_node); } } diff --git a/substrate/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/src/lib.rs b/substrate/frame/migrations/src/lib.rs index 68041a57eaa2e8f673f5f9c9fb774027587c25cb..1823e5a2f9527fb7e02c06132cf42a7083fc6e1d 100644 --- a/substrate/frame/migrations/src/lib.rs +++ b/substrate/frame/migrations/src/lib.rs @@ -678,7 +678,7 @@ impl Pallet { return Some(ControlFlow::Break(cursor)) } - let Some(id) = T::Migrations::nth_id(cursor.index) else { + if cursor.index >= T::Migrations::len() { // No more migrations in the tuple - we are done. defensive_assert!(cursor.index == T::Migrations::len(), "Inconsistent MBMs tuple"); Self::deposit_event(Event::UpgradeCompleted); @@ -687,8 +687,9 @@ impl Pallet { return None; }; - let Ok(bounded_id): Result, _> = id.try_into() else { - defensive!("integrity_test ensures that all identifiers' MEL bounds fit into CursorMaxLen; qed."); + let id = T::Migrations::nth_id(cursor.index).map(TryInto::try_into); + let Some(Ok(bounded_id)): Option, _>> = id else { + defensive!("integrity_test ensures that all identifiers are present and bounde; qed."); Self::upgrade_failed(Some(cursor.index)); return None }; diff --git a/substrate/frame/migrations/src/tests.rs b/substrate/frame/migrations/src/tests.rs index 9c9043d37a62e5f1df3d12da979b68251621c9d0..73ca2a9a09cf6451c65b0927d6090ef7452e8554 100644 --- a/substrate/frame/migrations/src/tests.rs +++ b/substrate/frame/migrations/src/tests.rs @@ -27,6 +27,31 @@ use frame_support::{pallet_prelude::Weight, traits::OnRuntimeUpgrade}; #[docify::export] #[test] fn simple_works() { + use Event::*; + test_closure(|| { + // Add three migrations, each taking one block longer than the previous. + MockedMigrations::set(vec![(SucceedAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Check that the executed migrations are recorded in `Historical`. + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 2),]); + + // Check that we got all events. + assert_events(vec![ + UpgradeStarted { migrations: 1 }, + MigrationAdvanced { index: 0, took: 1 }, + MigrationAdvanced { index: 0, took: 2 }, + MigrationCompleted { index: 0, took: 3 }, + UpgradeCompleted, + ]); + }); +} + +#[test] +fn simple_multiple_works() { use Event::*; test_closure(|| { // Add three migrations, each taking one block longer than the previous. diff --git a/substrate/frame/nfts/src/types.rs b/substrate/frame/nfts/src/types.rs index 1687a03520afe88c29831b94d6db23567d2ffce9..60d7c639c88cb8f1c64af75f0042d3a50fb3e87c 100644 --- a/substrate/frame/nfts/src/types.rs +++ b/substrate/frame/nfts/src/types.rs @@ -193,13 +193,13 @@ pub struct ItemMetadata> { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ItemTip { /// The collection of the item. - pub(super) collection: CollectionId, + pub collection: CollectionId, /// An item of which the tip is sent for. - pub(super) item: ItemId, + pub item: ItemId, /// A sender of the tip. - pub(super) receiver: AccountId, + pub receiver: AccountId, /// An amount the sender is willing to tip. - pub(super) amount: Amount, + pub amount: Amount, } /// Information about the pending swap. @@ -246,9 +246,9 @@ pub enum PriceDirection { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct PriceWithDirection { /// An amount. - pub(super) amount: Amount, + pub amount: Amount, /// A direction (send or receive). - pub(super) direction: PriceDirection, + pub direction: PriceDirection, } /// Support for up to 64 user-enabled features on a collection. @@ -518,31 +518,31 @@ impl_codec_bitflags!(CollectionRoles, u8, CollectionRole); #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct PreSignedMint { /// A collection of the item to be minted. - pub(super) collection: CollectionId, + pub collection: CollectionId, /// Item's ID. - pub(super) item: ItemId, + pub item: ItemId, /// Additional item's key-value attributes. - pub(super) attributes: Vec<(Vec, Vec)>, + pub attributes: Vec<(Vec, Vec)>, /// Additional item's metadata. - pub(super) metadata: Vec, + pub metadata: Vec, /// Restrict the claim to a particular account. - pub(super) only_account: Option, + pub only_account: Option, /// A deadline for the signature. - pub(super) deadline: Deadline, + pub deadline: Deadline, /// An optional price the claimer would need to pay for the mint. - pub(super) mint_price: Option, + pub mint_price: Option, } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct PreSignedAttributes { /// Collection's ID. - pub(super) collection: CollectionId, + pub collection: CollectionId, /// Item's ID. - pub(super) item: ItemId, + pub item: ItemId, /// Key-value attributes. - pub(super) attributes: Vec<(Vec, Vec)>, + pub attributes: Vec<(Vec, Vec)>, /// Attributes' namespace. - pub(super) namespace: AttributeNamespace, + pub namespace: AttributeNamespace, /// A deadline for the signature. - pub(super) deadline: Deadline, + pub deadline: Deadline, } diff --git a/substrate/frame/nomination-pools/runtime-api/src/lib.rs b/substrate/frame/nomination-pools/runtime-api/src/lib.rs index 67627e0acb13de6120fdd7f9b0c0cf910e8d6dc3..d81ad1dd4954e003680d5e467e5796526bc3edf0 100644 --- a/substrate/frame/nomination-pools/runtime-api/src/lib.rs +++ b/substrate/frame/nomination-pools/runtime-api/src/lib.rs @@ -63,5 +63,11 @@ sp_api::decl_runtime_apis! { /// [`migrate_delegation`](pallet_nomination_pools::Call::migrate_delegation) /// to migrate the funds of the pool member. fn member_needs_delegate_migration(member: AccountId) -> bool; + + /// Returns the total contribution of a pool member including any balance that is unbonding. + fn member_total_balance(who: AccountId) -> Balance; + + /// Total balance contributed to the pool. + fn pool_balance(pool_id: PoolId) -> Balance; } } diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 4d571855e4fe8a967d80579c4f2b8fa521f43309..272b3b60612b1324b7ef393c232f0b465d4bada6 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -106,9 +106,11 @@ pub trait StakeStrategy { /// Balance that can be transferred from pool account to member. /// - /// This is part of the pool balance that is not actively staked. That is, tokens that are - /// in unbonding period or unbonded. - fn transferable_balance(pool_account: Pool) -> Self::Balance; + /// This is part of the pool balance that can be withdrawn. + fn transferable_balance( + pool_account: Pool, + member_account: Member, + ) -> Self::Balance; /// Total balance of the pool including amount that is actively staked. fn total_balance(pool_account: Pool) -> Option; @@ -181,6 +183,9 @@ pub trait StakeStrategy { num_slashing_spans: u32, ) -> DispatchResult; + /// Dissolve the pool account. + fn dissolve(pool_account: Pool) -> DispatchResult; + /// Check if there is any pending slash for the pool. fn pending_slash(pool_account: Pool) -> Self::Balance; @@ -253,12 +258,15 @@ impl, AccountId = T: StakeStrategyType::Transfer } - fn transferable_balance(pool_account: Pool) -> BalanceOf { + fn transferable_balance( + pool_account: Pool, + _: Member, + ) -> BalanceOf { T::Currency::balance(&pool_account.0).saturating_sub(Self::active_stake(pool_account)) } fn total_balance(pool_account: Pool) -> Option> { - Some(T::Currency::total_balance(&pool_account.0)) + Some(T::Currency::total_balance(&pool_account.get())) } fn member_delegation_balance( @@ -300,6 +308,17 @@ impl, AccountId = T: Ok(()) } + fn dissolve(pool_account: Pool) -> DispatchResult { + defensive_assert!( + T::Currency::total_balance(&pool_account.clone().get()).is_zero(), + "dissolving pool should not have any balance" + ); + + // Defensively force set balance to zero. + T::Currency::set_balance(&pool_account.get(), Zero::zero()); + Ok(()) + } + fn pending_slash(_: Pool) -> Self::Balance { // for transfer stake strategy, slashing is greedy and never deferred. Zero::zero() @@ -360,11 +379,14 @@ impl< StakeStrategyType::Delegate } - fn transferable_balance(pool_account: Pool) -> BalanceOf { - Delegation::agent_balance(pool_account.clone().into()) + fn transferable_balance( + pool_account: Pool, + member_account: Member, + ) -> BalanceOf { + Delegation::agent_transferable_balance(pool_account.clone().into()) // pool should always be an agent. .defensive_unwrap_or_default() - .saturating_sub(Self::active_stake(pool_account)) + .min(Delegation::delegator_balance(member_account.into()).unwrap_or_default()) } fn total_balance(pool_account: Pool) -> Option> { @@ -384,12 +406,13 @@ impl< ) -> DispatchResult { match bond_type { BondType::Create => { - // first delegation - Delegation::delegate(who.into(), pool_account.into(), reward_account, amount) + // first delegation. Register agent first. + Delegation::register_agent(pool_account.clone().into(), reward_account)?; + Delegation::delegate(who.into(), pool_account.into(), amount) }, BondType::Extra => { // additional delegation - Delegation::delegate_extra(who.into(), pool_account.into(), amount) + Delegation::delegate(who.into(), pool_account.into(), amount) }, } } @@ -403,6 +426,10 @@ impl< Delegation::withdraw_delegation(who.into(), pool_account.into(), amount, num_slashing_spans) } + fn dissolve(pool_account: Pool) -> DispatchResult { + Delegation::remove_agent(pool_account.into()) + } + fn pending_slash(pool_account: Pool) -> Self::Balance { Delegation::pending_slash(pool_account.into()).defensive_unwrap_or_default() } @@ -433,6 +460,6 @@ impl< #[cfg(feature = "runtime-benchmarks")] fn remove_as_agent(pool: Pool) { - Delegation::drop_agent(pool.into()) + Delegation::migrate_to_direct_staker(pool.into()) } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 70ad06e2a4da3bb0f1a55ba6d8ac850076ac3b70..177c5da74d4ff63defd576322ed5097c90ef4a28 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -1831,7 +1831,9 @@ pub mod pallet { /// A member has been removed from a pool. /// /// The removal can be voluntary (withdrawn all unbonded funds) or involuntary (kicked). - MemberRemoved { pool_id: PoolId, member: T::AccountId }, + /// Any funds that are still delegated (i.e. dangling delegation) are released and are + /// represented by `released_balance`. + MemberRemoved { pool_id: PoolId, member: T::AccountId, released_balance: BalanceOf }, /// The roles of a pool have been updated to the given new roles. Note that the depositor /// can never change. RolesUpdated { @@ -2008,6 +2010,8 @@ pub mod pallet { pool_id: PoolId, ) -> DispatchResult { let who = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(amount >= MinJoinBond::::get(), Error::::MinimumBondNotMet); // If a member already exists that means they already belong to a pool @@ -2070,6 +2074,13 @@ pub mod pallet { )] pub fn bond_extra(origin: OriginFor, extra: BondExtra>) -> DispatchResult { let who = ensure_signed(origin)?; + + // ensure who is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(who.clone()), + Error::::NotMigrated + ); + Self::do_bond_extra(who.clone(), who, extra) } @@ -2085,6 +2096,12 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::claim_payout())] pub fn claim_payout(origin: OriginFor) -> DispatchResult { let signer = ensure_signed(origin)?; + // ensure signer is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(signer.clone()), + Error::::NotMigrated + ); + Self::do_claim_payout(signer.clone(), signer) } @@ -2128,6 +2145,12 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let member_account = T::Lookup::lookup(member_account)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(member_account.clone()), + Error::::NotMigrated + ); + let (mut member, mut bonded_pool, mut reward_pool) = Self::get_member_with_pools(&member_account)?; @@ -2211,6 +2234,9 @@ pub mod pallet { num_slashing_spans: u32, ) -> DispatchResult { let _ = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + let pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; // For now we only allow a pool to withdraw unbonded if its not destroying. If the pool @@ -2257,6 +2283,12 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let caller = ensure_signed(origin)?; let member_account = T::Lookup::lookup(member_account)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(member_account.clone()), + Error::::NotMigrated + ); + let mut member = PoolMembers::::get(&member_account).ok_or(Error::::PoolMemberNotFound)?; let current_era = T::StakeAdapter::current_era(); @@ -2342,9 +2374,10 @@ pub mod pallet { // don't exist. This check is also defensive in cases where the unbond pool does not // update its balance (e.g. a bug in the slashing hook.) We gracefully proceed in // order to ensure members can leave the pool and it can be destroyed. - .min(T::StakeAdapter::transferable_balance(Pool::from( - bonded_pool.bonded_account(), - ))); + .min(T::StakeAdapter::transferable_balance( + Pool::from(bonded_pool.bonded_account()), + Member::from(member_account.clone()), + )); // this can fail if the pool uses `DelegateStake` strategy and the member delegation // is not claimed yet. See `Call::migrate_delegation()`. @@ -2368,9 +2401,27 @@ pub mod pallet { // member being reaped. PoolMembers::::remove(&member_account); + + // Ensure any dangling delegation is withdrawn. + let dangling_withdrawal = match T::StakeAdapter::member_delegation_balance( + Member::from(member_account.clone()), + ) { + Some(dangling_delegation) => { + T::StakeAdapter::member_withdraw( + Member::from(member_account.clone()), + Pool::from(bonded_pool.bonded_account()), + dangling_delegation, + num_slashing_spans, + )?; + dangling_delegation + }, + None => Zero::zero(), + }; + Self::deposit_event(Event::::MemberRemoved { pool_id: member.pool_id, member: member_account.clone(), + released_balance: dangling_withdrawal, }); if member_account == bonded_pool.roles.depositor { @@ -2472,6 +2523,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) @@ -2506,6 +2559,8 @@ pub mod pallet { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.state != PoolState::Destroying, Error::::CanNotChangeState); + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); if bonded_pool.can_toggle_state(&who) { bonded_pool.set_state(state); @@ -2541,6 +2596,8 @@ pub mod pallet { .can_set_metadata(&who), Error::::DoesNotHavePermission ); + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); Metadata::::mutate(pool_id, |pool_meta| *pool_meta = metadata); @@ -2617,6 +2674,9 @@ pub mod pallet { }, }; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + match new_root { ConfigOp::Noop => (), ConfigOp::Remove => bonded_pool.roles.root = None, @@ -2663,8 +2723,9 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::chill())] pub fn chill(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; - let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); let depositor_points = PoolMembers::::get(&bonded_pool.roles.depositor) .ok_or(Error::::PoolMemberNotFound)? @@ -2699,7 +2760,14 @@ pub mod pallet { extra: BondExtra>, ) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_bond_extra(who, T::Lookup::lookup(member)?, extra) + let member_account = T::Lookup::lookup(member)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(member_account.clone()), + Error::::NotMigrated + ); + + Self::do_bond_extra(who, member_account, extra) } /// Allows a pool member to set a claim permission to allow or disallow permissionless @@ -2716,9 +2784,14 @@ pub mod pallet { permission: ClaimPermission, ) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(PoolMembers::::contains_key(&who), Error::::PoolMemberNotFound); + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(who.clone()), + Error::::NotMigrated + ); + ClaimPermissions::::mutate(who, |source| { *source = permission; }); @@ -2734,6 +2807,12 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::claim_payout())] pub fn claim_payout_other(origin: OriginFor, other: T::AccountId) -> DispatchResult { let signer = ensure_signed(origin)?; + // ensure member is not in an un-migrated state. + ensure!( + !Self::api_member_needs_delegate_migration(other.clone()), + Error::::NotMigrated + ); + Self::do_claim_payout(signer, other) } @@ -2752,6 +2831,9 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); let mut reward_pool = RewardPools::::get(pool_id) @@ -2788,6 +2870,9 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); bonded_pool.commission.try_update_max(pool_id, max_commission)?; @@ -2810,6 +2895,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); bonded_pool.commission.try_update_change_rate(change_rate)?; @@ -2831,6 +2918,9 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::claim_commission())] pub fn claim_commission(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + Self::do_claim_commission(who, pool_id) } @@ -2845,6 +2935,9 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::adjust_pool_deposit())] pub fn adjust_pool_deposit(origin: OriginFor, pool_id: PoolId) -> DispatchResult { let who = ensure_signed(origin)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); + Self::do_adjust_pool_deposit(who, pool_id) } @@ -2861,6 +2954,8 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + // ensure pool is not in an un-migrated state. + ensure!(!Self::api_pool_needs_delegate_migration(pool_id), Error::::NotMigrated); ensure!(bonded_pool.can_manage_commission(&who), Error::::DoesNotHavePermission); bonded_pool.commission.claim_permission = permission.clone(); @@ -2935,9 +3030,12 @@ pub mod pallet { ); let pool_contribution = member.total_balance(); - ensure!(pool_contribution >= MinJoinBond::::get(), Error::::MinimumBondNotMet); - // the member must have some contribution to be migrated. - ensure!(pool_contribution > Zero::zero(), Error::::AlreadyMigrated); + // ensure the pool contribution is greater than the existential deposit otherwise we + // cannot transfer funds to member account. + ensure!( + pool_contribution >= T::Currency::minimum_balance(), + Error::::MinimumBondNotMet + ); let delegation = T::StakeAdapter::member_delegation_balance(Member::from(member_account.clone())); @@ -3078,16 +3176,11 @@ impl Pallet { T::Currency::total_balance(&reward_account) == Zero::zero(), "could not transfer all amount to depositor while dissolving pool" ); - defensive_assert!( - T::StakeAdapter::total_balance(Pool::from(bonded_pool.bonded_account())) - .unwrap_or_default() == - Zero::zero(), - "dissolving pool should not have any balance" - ); // NOTE: Defensively force set balance to zero. T::Currency::set_balance(&reward_account, Zero::zero()); - // NOTE: With `DelegateStake` strategy, this won't do anything. - T::Currency::set_balance(&bonded_pool.bonded_account(), Zero::zero()); + + // dissolve pool account. + let _ = T::StakeAdapter::dissolve(Pool::from(bonded_account)).defensive(); Self::deposit_event(Event::::Destroyed { pool_id: bonded_pool.id }); // Remove bonded pool metadata. @@ -3443,6 +3536,7 @@ impl Pallet { fn do_adjust_pool_deposit(who: T::AccountId, pool: PoolId) -> DispatchResult { let bonded_pool = BondedPool::::get(pool).ok_or(Error::::PoolNotFound)?; + let reward_acc = &bonded_pool.reward_account(); let pre_frozen_balance = T::Currency::balance_frozen(&FreezeReason::PoolMinBalance.into(), reward_acc); @@ -3525,13 +3619,8 @@ impl Pallet { // this is their balance in the pool let expected_balance = pool_member.total_balance(); - defensive_assert!( - actual_balance >= expected_balance, - "actual balance should always be greater or equal to the expected" - ); - // return the amount to be slashed. - Ok(actual_balance.defensive_saturating_sub(expected_balance)) + Ok(actual_balance.saturating_sub(expected_balance)) } /// Apply freeze on reward account to restrict it from going below ED. @@ -3569,6 +3658,7 @@ impl Pallet { /// * each `member.pool_id` must correspond to an existing `BondedPool.id` (which implies the /// existence of the reward pool as well). /// * count of all members must be less than `MaxPoolMembers`. + /// * each `BondedPool.points` must never be lower than the pool's balance. /// /// Then, considering unbonding members: /// @@ -3697,6 +3787,11 @@ impl Pallet { pool is being destroyed and the depositor is the last member", ); + ensure!( + bonded_pool.points >= bonded_pool.points_to_balance(bonded_pool.points), + "Each `BondedPool.points` must never be lower than the pool's balance" + ); + expected_tvl += T::StakeAdapter::total_stake(Pool::from(bonded_pool.bonded_account())); Ok(()) @@ -3869,7 +3964,13 @@ impl Pallet { return false } + // if pool does not exist, return false. + if !BondedPools::::contains_key(pool_id) { + return false + } + let pool_account = Self::generate_bonded_account(pool_id); + // true if pool is still not migrated to `DelegateStake`. T::StakeAdapter::pool_strategy(Pool::from(pool_account)) != adapter::StakeStrategyType::Delegate @@ -3903,6 +4004,22 @@ impl Pallet { }) .unwrap_or_default() } + + /// Contribution of the member in the pool. + /// + /// Includes balance that is unbonded from staking but not claimed yet from the pool, therefore + /// this balance can be higher than the staked funds. + pub fn api_member_total_balance(who: T::AccountId) -> BalanceOf { + PoolMembers::::get(who.clone()) + .map(|m| m.total_balance()) + .unwrap_or_default() + } + + /// Total balance contributed to the pool. + pub fn api_pool_balance(pool_id: PoolId) -> BalanceOf { + T::StakeAdapter::total_balance(Pool::from(Self::generate_bonded_account(pool_id))) + .unwrap_or_default() + } } impl sp_staking::OnStakingUpdate> for Pallet { diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs index 28063c2ecaecd8caa6214553d2e16484ba5f5af8..06261699a5b23ff58c227875b143a1eaba08e439 100644 --- a/substrate/frame/nomination-pools/src/tests.rs +++ b/substrate/frame/nomination-pools/src/tests.rs @@ -2364,10 +2364,10 @@ mod claim_payout { Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, Event::Unbonded { member: 20, pool_id: 1, balance: 20, points: 20, era: 3 }, Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 20 }, + Event::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 6 }, Event::Withdrawn { member: 10, pool_id: 1, balance: 10, points: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 } ] ); @@ -2939,9 +2939,9 @@ mod unbond { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 40, pool_id: 1, points: 6, balance: 6 }, - Event::MemberRemoved { pool_id: 1, member: 40 }, + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 }, Event::Withdrawn { member: 550, pool_id: 1, points: 92, balance: 92 }, - Event::MemberRemoved { pool_id: 1, member: 550 }, + Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 }, Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2, era: 6 } ] @@ -3688,7 +3688,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 550 }, - Event::MemberRemoved { pool_id: 1, member: 550 } + Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 } ] ); assert_eq!( @@ -3709,7 +3709,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 40 }, - Event::MemberRemoved { pool_id: 1, member: 40 } + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 } ] ); assert_eq!( @@ -3731,7 +3731,7 @@ mod withdraw_unbonded { vec![ Event::Unbonded { member: 10, pool_id: 1, balance: 5, points: 5, era: 9 }, Event::Withdrawn { member: 10, pool_id: 1, balance: 5, points: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 } ] ); @@ -3806,7 +3806,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 40 } + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 } ] ); @@ -3827,7 +3827,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 275 }, - Event::MemberRemoved { pool_id: 1, member: 550 } + Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 } ] ); assert!(SubPoolsStorage::::get(1).unwrap().with_era.is_empty()); @@ -3862,7 +3862,7 @@ mod withdraw_unbonded { vec![ Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5, era: 6 }, Event::Withdrawn { member: 10, pool_id: 1, points: 5, balance: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 } ] ); @@ -4018,9 +4018,9 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, - Event::MemberRemoved { pool_id: 1, member: 100 }, + Event::MemberRemoved { pool_id: 1, member: 100, released_balance: 0 }, Event::Withdrawn { member: 200, pool_id: 1, points: 200, balance: 200 }, - Event::MemberRemoved { pool_id: 1, member: 200 } + Event::MemberRemoved { pool_id: 1, member: 200, released_balance: 0 } ] ); }); @@ -4070,7 +4070,7 @@ mod withdraw_unbonded { Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, Event::Unbonded { member: 100, pool_id: 1, points: 100, balance: 100, era: 3 }, Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, - Event::MemberRemoved { pool_id: 1, member: 100 } + Event::MemberRemoved { pool_id: 1, member: 100, released_balance: 0 } ] ); }); @@ -4310,7 +4310,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 100, pool_id: 1, points: 25, balance: 25 }, - Event::MemberRemoved { pool_id: 1, member: 100 } + Event::MemberRemoved { pool_id: 1, member: 100, released_balance: 0 } ] ); }) @@ -4548,7 +4548,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 10, pool_id: 1, points: 13, balance: 13 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 }, ] ); @@ -4588,7 +4588,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 20 } + Event::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 } ] ); @@ -4626,7 +4626,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 10, pool_id: 1, points: 10, balance: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 }, ] ); @@ -4672,7 +4672,7 @@ mod withdraw_unbonded { pool_events_since_last_call(), vec![ Event::Withdrawn { member: 10, pool_id: 1, points: 10, balance: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, Event::Destroyed { pool_id: 1 }, ] ); diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs index 51f6470f90d028caefe38f1a0bdd2ce868a21643..7fee2a0bdb23dfba40963f6c672109bd47999d43 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs @@ -25,16 +25,16 @@ use frame_support::{ }; use mock::*; use pallet_nomination_pools::{ - BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, - PoolMembers, PoolState, + BondExtra, BondedPools, CommissionChangeRate, ConfigOp, Error as PoolsError, + Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers, PoolState, }; use pallet_staking::{ CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination, }; -use pallet_delegated_staking::{Error as DelegatedStakingError, Event as DelegatedStakingEvent}; +use pallet_delegated_staking::Event as DelegatedStakingEvent; -use sp_runtime::{bounded_btree_map, traits::Zero}; +use sp_runtime::{bounded_btree_map, traits::Zero, Perbill}; use sp_staking::Agent; #[test] @@ -152,9 +152,9 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }, ] ); @@ -193,7 +193,7 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -476,10 +476,10 @@ fn pool_slash_e2e() { vec![ // 20 had unbonded 10 safely, and 10 got slashed by half. PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, // 21 unbonded all of it after the slash PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 } + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 } ] ); assert_eq!( @@ -525,7 +525,7 @@ fn pool_slash_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -999,7 +999,6 @@ fn pool_migration_e2e() { LegacyAdapter::set(false); // cannot migrate the member delegation unless pool is migrated first. - assert!(!Pools::api_member_needs_delegate_migration(20)); assert_noop!( Pools::migrate_delegation(RuntimeOrigin::signed(10), 20), PoolsError::::NotMigrated @@ -1031,13 +1030,17 @@ fn pool_migration_e2e() { // move to era 5 when 20 can withdraw unbonded funds. CurrentEra::::set(Some(5)); - // Unbond works even without claiming delegation. Lets unbond 22. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, 5)); + + // Cannot unbond without claiming delegation. Lets unbond 22. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(22), 22, 5), + PoolsError::::NotMigrated + ); // withdraw fails for 20 before claiming delegation assert_noop!( Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 10), - DelegatedStakingError::::NotDelegator + PoolsError::::NotMigrated ); let pre_claim_balance_20 = Balances::total_balance(&20); @@ -1060,17 +1063,11 @@ fn pool_migration_e2e() { assert_eq!( staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }, - StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 } - ] + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 }] ); assert_eq!( pool_events_since_last_call(), - vec![ - PoolsEvent::Unbonded { member: 22, pool_id: 1, balance: 5, points: 5, era: 8 }, - PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 5, points: 5 }, - ] + vec![PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 5, points: 5 },] ); assert_eq!( delegated_staking_events_since_last_call(), @@ -1113,8 +1110,9 @@ fn pool_migration_e2e() { assert_eq!(Balances::total_balance(&21), pre_migrate_balance_21 + 10); // MIGRATE 22 - let pre_migrate_balance_22 = Balances::total_balance(&22); assert_eq!(Balances::total_balance_on_hold(&22), 0); + // make balance of 22 as 0. + let _ = Balances::make_free_balance_be(&22, 0); // migrate delegation for 22. assert!(Pools::api_member_needs_delegate_migration(22)); @@ -1128,17 +1126,20 @@ fn pool_migration_e2e() { ); // tokens moved to 22's account and held there. - assert_eq!(Balances::total_balance(&22), pre_migrate_balance_22 + 10); + assert_eq!(Balances::total_balance(&22), 10); assert_eq!(Balances::total_balance_on_hold(&22), 10); - // withdraw fails since 22 only unbonds at era 8. + // unbond 22 should work now + assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, 5)); + + // withdraw fails since 22 only unbonds after era 9. assert_noop!( Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 5), PoolsError::::CannotWithdrawAny ); // go to era when 22 can unbond - CurrentEra::::set(Some(10)); + CurrentEra::::set(Some(9)); // withdraw works now assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(22), 22, 10)); @@ -1151,6 +1152,7 @@ fn pool_migration_e2e() { staking_events_since_last_call(), vec![ StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }, StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 } ] ); @@ -1160,7 +1162,8 @@ fn pool_migration_e2e() { vec![ PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 10, points: 10 }, // 21 was fully unbonding and removed from pool. - PoolsEvent::MemberRemoved { member: 21, pool_id: 1 }, + PoolsEvent::MemberRemoved { member: 21, pool_id: 1, released_balance: 0 }, + PoolsEvent::Unbonded { member: 22, pool_id: 1, balance: 5, points: 5, era: 9 }, PoolsEvent::Withdrawn { member: 22, pool_id: 1, balance: 5, points: 5 }, ] ); @@ -1183,3 +1186,491 @@ fn pool_migration_e2e() { ); }) } + +#[test] +fn disable_pool_operations_on_non_migrated() { + new_test_ext().execute_with(|| { + LegacyAdapter::set(true); + assert_eq!(Balances::minimum_balance(), 5); + assert_eq!(Staking::current_era(), None); + + // create the pool with TransferStake strategy. + assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); + assert_eq!(LastPoolId::::get(), 1); + + // have the pool nominate. + assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Created { depositor: 10, pool_id: 1 }, + PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, + ] + ); + + let pre_20 = Balances::free_balance(20); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + + // verify members balance is moved to pool. + assert_eq!(Balances::free_balance(20), pre_20 - 10); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },] + ); + + // we reset the adapter to `DelegateStake`. + LegacyAdapter::set(false); + + // pool is pending migration. + assert!(Pools::api_pool_needs_delegate_migration(1)); + + // ensure pool mutation is not allowed until pool is migrated. + assert_noop!( + Pools::join(RuntimeOrigin::signed(21), 10, 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Blocked), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_metadata(RuntimeOrigin::signed(10), 1, vec![1, 1]), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::update_roles( + RuntimeOrigin::signed(10), + 1, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) + ), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::chill(RuntimeOrigin::signed(10), 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(10), 1, None), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(10), 1, Zero::zero()), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission_change_rate( + RuntimeOrigin::signed(10), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } + ), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::claim_commission(RuntimeOrigin::signed(10), 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::adjust_pool_deposit(RuntimeOrigin::signed(10), 1), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::set_commission_claim_permission(RuntimeOrigin::signed(10), 1, None), + PoolsError::::NotMigrated + ); + + // migrate the pool. + assert_ok!(Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1)); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: DelegatedStaking::generate_proxy_delegator(Agent::from(POOL1_BONDED)) + .get(), + amount: 50 + 10 + },] + ); + + // member is pending migration. + assert!(Pools::api_member_needs_delegate_migration(20)); + + // ensure member mutation is not allowed until member's delegation is migrated. + assert_noop!( + Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(5)), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::bond_extra_other(RuntimeOrigin::signed(10), 20, BondExtra::Rewards), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::claim_payout(RuntimeOrigin::signed(20)), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(20), 20, 5), + PoolsError::::NotMigrated + ); + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0), + PoolsError::::NotMigrated + ); + + // migrate 20 + assert_ok!(Pools::migrate_delegation(RuntimeOrigin::signed(10), 20)); + // now `bond_extra` for 20 works. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(5))); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 5 },] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 5, joined: false },] + ); + + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::MigratedDelegation { + agent: POOL1_BONDED, + delegator: 20, + amount: 10 + }, + DelegatedStakingEvent::Delegated { agent: POOL1_BONDED, delegator: 20, amount: 5 }, + ] + ); + }) +} + +#[test] +fn pool_no_dangling_delegation() { + new_test_ext().execute_with(|| { + ExistentialDeposit::set(1); + assert_eq!(Balances::minimum_balance(), 1); + assert_eq!(Staking::current_era(), None); + // pool creator + let alice = 10; + let bob = 20; + let charlie = 21; + + // create the pool, we know this has id 1. + assert_ok!(Pools::create(RuntimeOrigin::signed(alice), 40, alice, alice, alice)); + assert_eq!(LastPoolId::::get(), 1); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Created { depositor: alice, pool_id: 1 }, + PoolsEvent::Bonded { member: alice, pool_id: 1, bonded: 40, joined: true }, + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + + delegator: alice, + amount: 40 + },] + ); + + assert_eq!( + Payee::::get(POOL1_BONDED), + Some(RewardDestination::Account(POOL1_REWARD)) + ); + + // have two members join + assert_ok!(Pools::join(RuntimeOrigin::signed(bob), 20, 1)); + assert_ok!(Pools::join(RuntimeOrigin::signed(charlie), 20, 1)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 }, + StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 } + ] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Bonded { member: bob, pool_id: 1, bonded: 20, joined: true }, + PoolsEvent::Bonded { member: charlie, pool_id: 1, bonded: 20, joined: true }, + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: bob, + amount: 20 + }, + DelegatedStakingEvent::Delegated { + agent: POOL1_BONDED, + delegator: charlie, + amount: 20 + }, + ] + ); + + // now let's progress a bit. + CurrentEra::::set(Some(1)); + + // bob is completely unbonding + assert_ok!(Pools::unbond(RuntimeOrigin::signed(bob), 20, 20)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 20 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Unbonded { member: bob, pool_id: 1, balance: 20, points: 20, era: 4 }] + ); + + // this era will get slashed + CurrentEra::::set(Some(2)); + + assert_ok!(Pools::unbond(RuntimeOrigin::signed(alice), 10, 10)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(charlie), 21, 10)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, + ] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Unbonded { member: alice, pool_id: 1, balance: 10, points: 10, era: 5 }, + PoolsEvent::Unbonded { + member: charlie, + pool_id: 1, + balance: 10, + points: 10, + era: 5 + }, + ] + ); + + // At this point, bob's 20 that is unlocking is safe from slash, 10 (alice) + 10 (charlie) + // are also unlocking but vulnerable to slash, and another 40 are active and vulnerable to + // slash. Let's slash half of them. + pallet_staking::slashing::do_slash::( + &POOL1_BONDED, + 30, + &mut Default::default(), + &mut Default::default(), + 2, // slash era 2, affects chunks at era 5 onwards. + ); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + // unbonding pool of 20 for era 5 has been slashed to 10 + PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 10 }, + // active stake of 40 has been slashed to half + PoolsEvent::PoolSlashed { pool_id: 1, balance: 20 } /* no slash to era 4 + * unbonding pool */ + ] + ); + + // alice's initial stake of 40 is reduced to half + assert_eq!(PoolMembers::::get(alice).unwrap().total_balance(), 20); + // bob unbonded in era 1 and is safe from slash + assert_eq!(PoolMembers::::get(bob).unwrap().total_balance(), 20); + // charlie's initial stake of 20 is slashed to half + assert_eq!(PoolMembers::::get(charlie).unwrap().total_balance(), 10); + + // apply pending slash to alice. + assert_eq!(Pools::api_member_pending_slash(alice), 20); + assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), alice)); + // apply pending slash to charlie. + assert_eq!(Pools::api_member_pending_slash(charlie), 10); + assert_ok!(Pools::apply_slash(RuntimeOrigin::signed(10), charlie)); + // no pending slash for bob + assert_eq!(Pools::api_member_pending_slash(bob), 0); + + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::Slashed { + agent: POOL1_BONDED, + delegator: alice, + amount: 20 + }, + DelegatedStakingEvent::Slashed { + agent: POOL1_BONDED, + delegator: charlie, + amount: 10 + }, + ] + ); + + // go forward to an era after PostUnbondingPoolsWindow = 10 ends for era 5. + CurrentEra::::set(Some(15)); + // At this point subpools will all be merged in no-era causing Bob to lose some value while + // Alice and Charlie will gain some value. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(charlie), charlie, 10)); + + // Now alice and charlie has less balance locked than their contribution. + assert_eq!(Balances::total_balance_on_hold(&alice), 20); + assert_eq!(PoolMembers::::get(alice).unwrap().total_balance(), 22); + assert_eq!(Balances::total_balance_on_hold(&charlie), 10); + assert_eq!(PoolMembers::::get(charlie).unwrap().total_balance(), 12); + + // and bob has more balance locked than his contribution. + assert_eq!(Balances::total_balance_on_hold(&bob), 20); + assert_eq!(PoolMembers::::get(bob).unwrap().total_balance(), 15); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![PoolsEvent::Unbonded { + member: charlie, + pool_id: 1, + balance: 5, + points: 5, + era: 18 + }] + ); + + // When bob withdraws all, he gets all his locked funds back. + let bob_pre_withdraw_balance = Balances::free_balance(&bob); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(bob), bob, 0)); + assert_eq!(Balances::free_balance(&bob), bob_pre_withdraw_balance + 20); + assert_eq!(Balances::total_balance_on_hold(&bob), 0); + assert!(!PoolMembers::::contains_key(bob)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 30 },] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { pool_id: 1, member: bob, balance: 15, points: 20 }, + // dangling delegation of 5 is released + PoolsEvent::MemberRemoved { pool_id: 1, member: bob, released_balance: 5 }, + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![ + DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: bob, amount: 15 }, + // the second release is the dangling delegation when member is removed. + DelegatedStakingEvent::Released { agent: POOL1_BONDED, delegator: bob, amount: 5 }, + ] + ); + + // Charlie can withdraw as much as he has locked. + CurrentEra::::set(Some(18)); + let charlie_pre_withdraw_balance = Balances::free_balance(&charlie); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(charlie), charlie, 0)); + // Charlie's total balance was 12, but we don't have enough funds to unlock. We try the best + // effort and unlock 10. + assert_eq!(Balances::free_balance(&charlie), charlie_pre_withdraw_balance + 10); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 5 },] + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { pool_id: 1, member: charlie, balance: 10, points: 15 }, + PoolsEvent::MemberRemoved { member: charlie, pool_id: 1, released_balance: 0 } + ] + ); + assert_eq!( + delegated_staking_events_since_last_call(), + vec![DelegatedStakingEvent::Released { + agent: POOL1_BONDED, + delegator: charlie, + amount: 10 + },] + ); + + // Set pools to destroying so alice can withdraw + assert_ok!(Pools::set_state(RuntimeOrigin::signed(alice), 1, PoolState::Destroying)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(alice), alice, 30)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 15 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, + PoolsEvent::Unbonded { + member: alice, + pool_id: 1, + points: 15, + balance: 15, + era: 21 + } + ] + ); + + CurrentEra::::set(Some(21)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(alice), alice, 0)); + + assert_eq!( + staking_events_since_last_call(), + vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 15 }] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + PoolsEvent::Withdrawn { member: alice, pool_id: 1, balance: 20, points: 25 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: alice, released_balance: 0 }, + PoolsEvent::Destroyed { pool_id: 1 } + ] + ); + + // holds for all members are released. + assert_eq!(Balances::total_balance_on_hold(&alice), 0); + assert_eq!(Balances::total_balance_on_hold(&bob), 0); + assert_eq!(Balances::total_balance_on_hold(&charlie), 0); + }); +} diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs index ed47932a323bf14335d5bb74fec03cfa749625e3..d1bc4ef8ff281986532d069292b2fed7785e6ca4 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs @@ -154,11 +154,14 @@ impl pallet_nomination_pools::adapter::StakeStrategy for MockAdapter { } DelegateStake::strategy_type() } - fn transferable_balance(pool_account: Pool) -> Self::Balance { + fn transferable_balance( + pool_account: Pool, + member_account: Member, + ) -> Self::Balance { if LegacyAdapter::get() { - return TransferStake::transferable_balance(pool_account) + return TransferStake::transferable_balance(pool_account, member_account) } - DelegateStake::transferable_balance(pool_account) + DelegateStake::transferable_balance(pool_account, member_account) } fn total_balance(pool_account: Pool) -> Option { @@ -200,6 +203,13 @@ impl pallet_nomination_pools::adapter::StakeStrategy for MockAdapter { DelegateStake::member_withdraw(who, pool_account, amount, num_slashing_spans) } + fn dissolve(pool_account: Pool) -> DispatchResult { + if LegacyAdapter::get() { + return TransferStake::dissolve(pool_account) + } + DelegateStake::dissolve(pool_account) + } + fn pending_slash(pool_account: Pool) -> Self::Balance { if LegacyAdapter::get() { return TransferStake::pending_slash(pool_account) diff --git a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs b/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs index aa91350259000b810eb63a0036754517eeb8421d..28e978bba0e5da7af32b7465128ba824601598f8 100644 --- a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs +++ b/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs @@ -145,9 +145,9 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }, ] ); @@ -186,7 +186,7 @@ fn pool_lifecycle_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -275,7 +275,7 @@ fn destroy_pool_with_erroneous_consumer() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); @@ -558,10 +558,10 @@ fn pool_slash_e2e() { vec![ // 20 had unbonded 10 safely, and 10 got slashed by half. PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, // 21 unbonded all of it after the slash PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21 } + PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 } ] ); assert_eq!( @@ -607,7 +607,7 @@ fn pool_slash_e2e() { pool_events_since_last_call(), vec![ PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10 }, + PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, PoolsEvent::Destroyed { pool_id: 1 } ] ); diff --git a/substrate/frame/preimage/src/benchmarking.rs b/substrate/frame/preimage/src/benchmarking.rs index 2d3bec16b818331e99a5072c15da55b45b7b521e..3d0c5b900579d7f84d887d1ca8fdc686adb1f3a9 100644 --- a/substrate/frame/preimage/src/benchmarking.rs +++ b/substrate/frame/preimage/src/benchmarking.rs @@ -116,7 +116,7 @@ benchmarks! { T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, hash ) verify { - let ticket = TicketOf::::new(¬er, Footprint { count: 1, size: MAX_SIZE as u64 }).unwrap().unwrap(); + let ticket = TicketOf::::new(¬er, Footprint { count: 1, size: MAX_SIZE as u64 }).unwrap(); let s = RequestStatus::Requested { maybe_ticket: Some((noter, ticket)), count: 1, maybe_len: Some(MAX_SIZE) }; assert_eq!(RequestStatusFor::::get(&hash), Some(s)); } diff --git a/substrate/frame/preimage/src/lib.rs b/substrate/frame/preimage/src/lib.rs index 30056fc6d9a492bd6b72e44d917af573758bab9d..658e7fec534827a9f82f0756840303d20a725233 100644 --- a/substrate/frame/preimage/src/lib.rs +++ b/substrate/frame/preimage/src/lib.rs @@ -124,8 +124,6 @@ pub mod pallet { type ManagerOrigin: EnsureOrigin; /// A means of providing some cost while data is stored on-chain. - /// - /// Should never return a `None`, implying no cost for a non-empty preimage. type Consideration: Consideration; } @@ -162,8 +160,6 @@ pub mod pallet { TooMany, /// Too few hashes were requested to be upgraded (i.e. zero). TooFew, - /// No ticket with a cost was returned by [`Config::Consideration`] to store the preimage. - NoCost, } /// A reason for this pallet placing a hold on funds. @@ -274,10 +270,10 @@ impl Pallet { // unreserve deposit T::Currency::unreserve(&who, amount); // take consideration - let Ok(Some(ticket)) = + let Ok(ticket) = T::Consideration::new(&who, Footprint::from_parts(1, len as usize)) + .defensive_proof("Unexpected inability to take deposit after unreserved") else { - defensive!("None ticket or inability to take deposit after unreserved"); return true }; RequestStatus::Unrequested { ticket: (who, ticket), len } @@ -288,10 +284,12 @@ impl Pallet { T::Currency::unreserve(&who, deposit); // take consideration if let Some(len) = maybe_len { - let Ok(Some(ticket)) = + let Ok(ticket) = T::Consideration::new(&who, Footprint::from_parts(1, len as usize)) + .defensive_proof( + "Unexpected inability to take deposit after unreserved", + ) else { - defensive!("None ticket or inability to take deposit after unreserved"); return true }; Some((who, ticket)) @@ -351,8 +349,7 @@ impl Pallet { RequestStatus::Requested { maybe_ticket: None, count: 1, maybe_len: Some(len) }, (None, Some(depositor)) => { let ticket = - T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))? - .ok_or(Error::::NoCost)?; + T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))?; RequestStatus::Unrequested { ticket: (depositor.clone(), ticket), len } }, }; diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..667328ac2d0dd1c0029aede50414845aa8aeff9d --- /dev/null +++ b/substrate/frame/revive/Cargo.toml @@ -0,0 +1,122 @@ +[package] +name = "pallet-revive" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "FRAME pallet for PolkaVM contracts." +readme = "README.md" +include = ["CHANGELOG.md", "README.md", "build.rs", "src/**/*"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +environmental = { workspace = true } +paste = { workspace = true } +polkavm = { version = "0.10.0", default-features = false } +bitflags = { workspace = true } +codec = { features = [ + "derive", + "max-encoded-len", +], workspace = true } +scale-info = { features = ["derive"], workspace = true } +log = { workspace = true } +serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +impl-trait-for-tuples = { workspace = true } +rlp = { workspace = true } + +# Polkadot SDK Dependencies +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-balances = { optional = true, workspace = true } +pallet-revive-fixtures = { workspace = true, default-features = false } +pallet-revive-uapi = { workspace = true, default-features = true } +pallet-revive-proc-macro = { workspace = true, default-features = true } +sp-api = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } + +[dev-dependencies] +array-bytes = { workspace = true, default-features = true } +assert_matches = { workspace = true } +pretty_assertions = { workspace = true } +wat = { workspace = true } +pallet-revive-fixtures = { workspace = true, default-features = true } + +# Polkadot SDK Dependencies +pallet-balances = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +pallet-message-queue = { workspace = true, default-features = true } +pallet-utility = { workspace = true, default-features = true } +pallet-assets = { workspace = true, default-features = true } +pallet-proxy = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } +sp-tracing = { workspace = true, default-features = true } +xcm-builder = { workspace = true, default-features = true } + +[features] +default = ["std"] +# enabling this feature will require having a riscv toolchain installed +# if no tests are ran and runtime benchmarks will not work +# apart from this the pallet will stay functional +riscv = ["pallet-revive-fixtures/riscv"] +std = [ + "codec/std", + "environmental/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances?/std", + "pallet-proxy/std", + "pallet-revive-fixtures/std", + "pallet-timestamp/std", + "pallet-utility/std", + "polkavm/std", + "rlp/std", + "scale-info/std", + "serde", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-keystore/std", + "sp-runtime/std", + "sp-std/std", + "xcm-builder/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-proxy/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-utility/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/revive/README.md b/substrate/frame/revive/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5352e636c2524c4fbb477880f61c64c37a640f5d --- /dev/null +++ b/substrate/frame/revive/README.md @@ -0,0 +1,104 @@ +# Revive Pallet + +This is an **experimental** module that provides functionality for the runtime to deploy and execute PolkaVM +smart-contracts. It is a heavily modified `pallet_contracts` fork. + +## Overview + +This module extends accounts based on the [`frame_support::traits::fungible`] traits to have smart-contract +functionality. It can be used with other modules that implement accounts based on [`frame_support::traits::fungible`]. +These "smart-contract accounts" have the ability to instantiate smart-contracts and make calls to other contract and +non-contract accounts. + +The smart-contract code is stored once, and later retrievable via its `code_hash`. This means that multiple +smart-contracts can be instantiated from the same `code`, without replicating the code each time. + +When a smart-contract is called, its associated code is retrieved via the code hash and gets executed. This call can +alter the storage entries of the smart-contract account, instantiate new smart-contracts, or call other smart-contracts. + +Finally, when an account is reaped, its associated code and storage of the smart-contract account will also be deleted. + +### Weight + +Senders must specify a [`Weight`](https://paritytech.github.io/substrate/master/sp_weights/struct.Weight.html) limit +with every call, as all instructions invoked by the smart-contract require weight. Unused weight is refunded after the +call, regardless of the execution outcome. + +If the weight limit is reached, then all calls and state changes (including balance transfers) are only reverted at the +current call's contract level. For example, if contract A calls B and B runs out of weight mid-call, then all of B's +calls are reverted. Assuming correct error handling by contract A, A's other calls and state changes still persist. + +One `ref_time` `Weight` is defined as one picosecond of execution time on the runtime's reference machine. + +### Revert Behaviour + +Contract call failures are not cascading. When failures occur in a sub-call, they do not "bubble up", and the call will +only revert at the specific contract level. For example, if contract A calls contract B, and B fails, A can decide how +to handle that failure, either proceeding or reverting A's changes. + +## Interface + +### Dispatchable functions + +Those are documented in the [reference +documentation](https://paritytech.github.io/substrate/master/pallet_revive/index.html#dispatchable-functions). + +## Usage + +This module executes PolkaVM smart contracts. These can potentially be written in any language that compiles to +RISC-V. For now, the only officially supported languages are Solidity (via [`revive`](https://github.com/xermicus/revive)) +and Rust (check the `fixtures` directory for Rust examples). + +## Debugging + +Contracts can emit messages to the client when called as RPC through the +[`debug_message`](https://paritytech.github.io/substrate/master/pallet_revive/trait.SyscallDocs.html#tymethod.debug_message) +API. + +Those messages are gathered into an internal buffer and sent to the RPC client. It is up to the individual client if +and how those messages are presented to the user. + +This buffer is also printed as a debug message. In order to see these messages on the node console the log level for the +`runtime::revive` target needs to be raised to at least the `debug` level. However, those messages are easy to +overlook because of the noise generated by block production. A good starting point for observing them on the console is +using this command line in the root directory of the Substrate repository: + +```bash +cargo run --release -- --dev -lerror,runtime::revive=debug +``` + +This raises the log level of `runtime::revive` to `debug` and all other targets to `error` in order to prevent them +from spamming the console. + +`--dev`: Use a dev chain spec `--tmp`: Use temporary storage for chain data (the chain state is deleted on exit) + +## Host function tracing + +For contract authors, it can be a helpful debugging tool to see which host functions are called, with which arguments, +and what the result was. + +In order to see these messages on the node console, the log level for the `runtime::revive::strace` target needs to +be raised to the `trace` level. + +Example: + +```bash +cargo run --release -- --dev -lerror,runtime::revive::strace=trace,runtime::revive=debug +``` + +## Unstable Interfaces + +Driven by the desire to have an iterative approach in developing new contract interfaces this pallet contains the +concept of an unstable interface. Akin to the rust nightly compiler it allows us to add new interfaces but mark them as +unstable so that contract languages can experiment with them and give feedback before we stabilize those. + +In order to access interfaces which don't have a stable `#[api_version(x)]` in [`runtime.rs`](src/wasm/runtime.rs) +one need to set `pallet_revive::Config::UnsafeUnstableInterface` to `ConstU32`. +**It should be obvious that any production runtime should never be compiled with this feature: In addition to be +subject to change or removal those interfaces might not have proper weights associated with them and are therefore +considered unsafe**. + +New interfaces are generally added as unstable and might go through several iterations before they are promoted to a +stable interface. + +License: Apache-2.0 diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..903298d2df210cead191290335930c0247abae17 --- /dev/null +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "pallet-revive-fixtures" +publish = true +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Fixtures for testing and benchmarking" + +[lints] +workspace = true + +[dependencies] +frame-system = { workspace = true, default-features = true, optional = true } +sp-core = { workspace = true, default-features = true, optional = true } +sp-io = { workspace = true, default-features = true, optional = true } +sp-runtime = { workspace = true, default-features = true, optional = true } +anyhow = { workspace = true, default-features = true, optional = true } +log = { workspace = true } + +[build-dependencies] +parity-wasm = { workspace = true } +tempfile = { workspace = true } +toml = { workspace = true } +polkavm-linker = { version = "0.10.0" } +anyhow = { workspace = true, default-features = true } + +[features] +default = ["std"] +# only if the feature is set we are building the test fixtures +# this is because it requires a custom toolchain supporting polkavm +# we will remove this once there is an upstream toolchain +riscv = [] +# only when std is enabled all fixtures are available +std = [ + "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 new file mode 100644 index 0000000000000000000000000000000000000000..944ae246c1b85535ccc83ffe63d986ce0fd05230 --- /dev/null +++ b/substrate/frame/revive/fixtures/build.rs @@ -0,0 +1,215 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Compile text fixtures to PolkaVM binaries. +use anyhow::Result; + +fn main() -> Result<()> { + build::run() +} + +#[cfg(feature = "riscv")] +mod build { + use super::Result; + use anyhow::{bail, Context}; + use std::{ + cfg, env, fs, + path::{Path, PathBuf}, + process::Command, + }; + + /// A contract entry. + struct Entry { + /// The path to the contract source file. + path: PathBuf, + } + + impl Entry { + /// Create a new contract entry from the given path. + fn new(path: PathBuf) -> Self { + Self { path } + } + + /// Return the path to the contract source file. + fn path(&self) -> &str { + self.path.to_str().expect("path is valid unicode; qed") + } + + /// Return the name of the contract. + fn name(&self) -> &str { + self.path + .file_stem() + .expect("file exits; qed") + .to_str() + .expect("name is valid unicode; qed") + } + + /// Return the name of the polkavm file. + fn out_filename(&self) -> String { + format!("{}.polkavm", self.name()) + } + } + + /// Collect all contract entries from the given source directory. + /// Contracts that have already been compiled are filtered out. + fn collect_entries(contracts_dir: &Path) -> Vec { + fs::read_dir(contracts_dir) + .expect("src dir exists; qed") + .filter_map(|file| { + let path = file.expect("file exists; qed").path(); + if path.extension().map_or(true, |ext| ext != "rs") { + return None + } + + Some(Entry::new(path)) + }) + .collect::>() + } + + /// Create a `Cargo.toml` to compile the given contract entries. + fn create_cargo_toml<'a>( + fixtures_dir: &Path, + entries: impl Iterator, + output_dir: &Path, + ) -> Result<()> { + let mut cargo_toml: toml::Value = toml::from_str(include_str!("./build/Cargo.toml"))?; + let mut set_dep = |name, path| -> Result<()> { + cargo_toml["dependencies"][name]["path"] = toml::Value::String( + fixtures_dir.join(path).canonicalize()?.to_str().unwrap().to_string(), + ); + Ok(()) + }; + set_dep("uapi", "../uapi")?; + set_dep("common", "./contracts/common")?; + + cargo_toml["bin"] = toml::Value::Array( + entries + .map(|entry| { + let name = entry.name(); + let path = entry.path(); + toml::Value::Table(toml::toml! { + name = name + path = path + }) + }) + .collect::>(), + ); + + let cargo_toml = toml::to_string_pretty(&cargo_toml)?; + fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into) + } + + fn invoke_build(current_dir: &Path) -> Result<()> { + let encoded_rustflags = [ + "-Crelocation-model=pie", + "-Clink-arg=--emit-relocs", + "-Clink-arg=--export-dynamic-symbol=__polkavm_symbol_export_hack__*", + ] + .join("\x1f"); + + let build_res = Command::new(env::var("CARGO")?) + .current_dir(current_dir) + .env_clear() + .env("PATH", env::var("PATH").unwrap_or_default()) + .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) + .env("RUSTUP_TOOLCHAIN", "rve-nightly") + .env("RUSTC_BOOTSTRAP", "1") + .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) + .args([ + "build", + "--release", + "--target=riscv32ema-unknown-none-elf", + "-Zbuild-std=core", + "-Zbuild-std-features=panic_immediate_abort", + ]) + .output() + .expect("failed to execute process"); + + if build_res.status.success() { + return Ok(()) + } + + let stderr = String::from_utf8_lossy(&build_res.stderr); + + if stderr.contains("'rve-nightly' is not installed") { + eprintln!("RISC-V toolchain is not installed.\nDownload and install toolchain from https://github.com/paritytech/rustc-rv32e-toolchain."); + eprintln!("{}", stderr); + } else { + eprintln!("{}", stderr); + } + + bail!("Failed to build contracts"); + } + + /// Post-process the compiled code. + fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { + let mut config = polkavm_linker::Config::default(); + config.set_strip(true); + config.set_optimize(false); + let orig = + fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; + let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) + .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; + fs::write(output_path, linked).map_err(Into::into) + } + + /// Write the compiled contracts to the given output directory. + fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { + for entry in entries { + post_process( + &build_dir.join("target/riscv32ema-unknown-none-elf/release").join(entry.name()), + &out_dir.join(entry.out_filename()), + )?; + } + + Ok(()) + } + + pub fn run() -> Result<()> { + let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); + let contracts_dir = fixtures_dir.join("contracts"); + let uapi_dir = fixtures_dir.parent().expect("uapi dir exits; qed").join("uapi"); + let out_dir: PathBuf = env::var("OUT_DIR")?.into(); + + // the fixtures have a dependency on the uapi crate + println!("cargo::rerun-if-changed={}", fixtures_dir.display()); + println!("cargo::rerun-if-changed={}", uapi_dir.display()); + + let entries = collect_entries(&contracts_dir); + if entries.is_empty() { + return Ok(()) + } + + let tmp_dir = tempfile::tempdir()?; + let tmp_dir_path = tmp_dir.path(); + + create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?; + invoke_build(tmp_dir_path)?; + + write_output(tmp_dir_path, &out_dir, entries)?; + Ok(()) + } +} + +#[cfg(not(feature = "riscv"))] +mod build { + use super::Result; + + pub fn run() -> Result<()> { + Ok(()) + } +} diff --git a/substrate/frame/revive/fixtures/build/Cargo.toml b/substrate/frame/revive/fixtures/build/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..7dead51b23060846a63563ad7161f7bf5b212a12 --- /dev/null +++ b/substrate/frame/revive/fixtures/build/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "contracts" +publish = false +version = "1.0.0" +edition = "2021" + +# Binary targets are injected dynamically by the build script. +[[bin]] + +# All paths are injected dynamically by the build script. +[dependencies] +uapi = { package = 'pallet-revive-uapi', path = "", default-features = false } +common = { package = 'pallet-revive-fixtures-common', path = "" } +polkavm-derive = { version = "0.10.0" } + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 diff --git a/substrate/frame/revive/fixtures/contracts/balance.rs b/substrate/frame/revive/fixtures/contracts/balance.rs new file mode 100644 index 0000000000000000000000000000000000000000..4606135d9807acf0e5979ad78c6b0ece7a678077 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/balance.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::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() { + 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/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs new file mode 100644 index 0000000000000000000000000000000000000000..ee51548879d9de9479d5d9f1c695f6c6d4bc5889 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + callee_input: [u8; 4], + callee_addr: &[u8; 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. + &[0u8; 32], // Value transferred to the contract. + callee_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs new file mode 100644 index 0000000000000000000000000000000000000000..d0d7c1bee2a5f9a65a226dd76f767ac44b463b3d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -0,0 +1,57 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls the supplied dest and transfers 100 balance during this call and copies +//! the return code of this call to the output buffer. +//! It also forwards its input to the callee. +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 100, + callee_addr: &[u8; 20], + value: &[u8; 32], + input: [u8], + ); + + // Call the callee + let err_code = match api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + value, // Value transferred to the contract. + input, + None, + ) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime.rs b/substrate/frame/revive/fixtures/contracts/call_runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..2b132398fb680babf2900de95351efcf12a535c4 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_runtime.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This passes its input to `call_runtime` and returns the return value to its caller. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + // Fixture calls should fit into 100 bytes. + input!(100, call: [u8], ); + + // Use the call passed as input to call the runtime. + let err_code = match api::call_runtime(call) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs new file mode 100644 index 0000000000000000000000000000000000000000..8c8aee9628498e4cbc67e8e9f304106c717a486f --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 512, + callee_input: [u8; 4], + callee_addr: &[u8; 20], + call: [u8], + ); + + // Use the call passed as input to call the runtime. + api::call_runtime(call).unwrap(); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. + callee_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs new file mode 100644 index 0000000000000000000000000000000000000000..330393e706e98fea7f7e6b04816885f37ac21073 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -0,0 +1,51 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls the account_id with the flags and value. +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + callee_addr: &[u8; 20], + flags: u32, + value: u64, + forwarded_input: [u8], + ); + + api::call( + uapi::CallFlags::from_bits(flags).unwrap(), + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &u256_bytes(value), // Value transferred to the contract. + forwarded_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ab892a6b7ae8416d923215df0a13b9ee13faf0c --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -0,0 +1,52 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls the account_id with the 2D Weight limit. +//! It returns the result of the call as output data. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + callee_addr: &[u8; 20], + ref_time: u64, + proof_size: u64, + forwarded_input: [u8], + ); + + api::call( + uapi::CallFlags::empty(), + callee_addr, + ref_time, + proof_size, + None, // No deposit limit. + &[0u8; 32], // value transferred to the contract. + forwarded_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs new file mode 100644 index 0000000000000000000000000000000000000000..f9a30b87df47e071ae808b4530caed8e4eebea55 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -0,0 +1,153 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: &[u8; 32],); + + // The value to transfer on instantiation and calls. Chosen to be greater than existential + // deposit. + 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]; + let reverted_input = [1u8, 34, 51, 68, 85, 102, 119]; + + // Fail to deploy the contract since it returns a non-zero exit status. + let res = api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &reverted_input, + None, + None, + 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 + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &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. + 1u64, // Too little proof_size weight + None, // No deposit limit. + &value, + &input, + None, + None, + Some(&salt), + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Deploy the contract successfully. + let mut callee = [0u8; 20]; + + api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &input, + Some(&mut callee), + None, + Some(&salt), + ) + .unwrap(); + + // Call the new contract and expect it to return failing exit code. + let res = api::call( + uapi::CallFlags::empty(), + &callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &reverted_input, + None, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeReverted))); + + // Fail to call the contract due to insufficient ref_time weight. + let res = api::call( + uapi::CallFlags::empty(), + &callee, + 1u64, // Too little ref_time weight. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &input, + None, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Fail to call the contract due to insufficient proof_size weight. + let res = api::call( + uapi::CallFlags::empty(), + &callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 1u64, // too little proof_size weight + None, // No deposit limit. + &value, + &input, + None, + ); + assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); + + // Call the contract successfully. + let mut output = [0u8; 4]; + api::call( + uapi::CallFlags::empty(), + &callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &value, + &input, + Some(&mut &mut output[..]), + ) + .unwrap(); + assert_eq!(&output, &input[4..]) +} diff --git a/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs b/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd6f59802fa08408105070f37ae64569a32d38a7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls caller_is_origin `n` times. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(n: u32, ); + + for _ in 0..n { + let _ = api::caller_is_origin(); + } +} diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension.rs b/substrate/frame/revive/fixtures/contracts/chain_extension.rs new file mode 100644 index 0000000000000000000000000000000000000000..474df00d69129cd062d0c257b8f935f4b29c4990 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/chain_extension.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Call chain extension by passing through input and output of this contract. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(input, 8, func_id: u32,); + + // the chain extension passes through the input and returns it as output + let mut output_buffer = [0u8; 32]; + let output = &mut &mut output_buffer[0..input.len()]; + + let ret_id = api::call_chain_extension(func_id, input, Some(output)); + assert_eq!(ret_id, func_id); + + api::return_value(uapi::ReturnFlags::empty(), output); +} diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..22d6c5b548d87acd2bffc17401f42a682d328dd7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs @@ -0,0 +1,66 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Call chain extension two times with the specified func_ids +//! It then calls itself once +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + input, + func_id1: u32, + func_id2: u32, + stop_recurse: u8, + ); + + api::call_chain_extension(func_id1, input, None); + api::call_chain_extension(func_id2, input, None); + + if stop_recurse == 0 { + // Setup next call + input[0..4].copy_from_slice(&((3 << 16) | 2u32).to_le_bytes()); + input[4..8].copy_from_slice(&((3 << 16) | 3u32).to_le_bytes()); + input[8] = 1u8; + + // Read the contract address. + 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. + &[0u8; 32], // Value transferred to the contract. + input, + None, + ) + .unwrap(); + } +} diff --git a/substrate/frame/revive/fixtures/contracts/common/Cargo.toml b/substrate/frame/revive/fixtures/contracts/common/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..7dadb7b821a2e18aae61309213df6f43bf94b8c1 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/common/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pallet-revive-fixtures-common" +publish = false +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Common utilities for pallet-revive-fixtures." + +[dependencies] +uapi = { package = 'pallet-revive-uapi', path = "../../../uapi", default-features = false } diff --git a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..abfba282bec14254f76797f776bc83b0a7f5e315 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs @@ -0,0 +1,196 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] + +pub use uapi::{HostFn, HostFnImpl as api}; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } +} + +/// Utility macro to read input passed to a contract. +/// +/// Example: +/// +/// ``` +/// input$!( +/// var1: u32, // [0, 4) var1 decoded as u32 +/// var2: [u8; 32], // [4, 36) var2 decoded as a [u8] slice +/// var3: u8, // [36, 37) var3 decoded as a u8 +/// ); +/// +/// // Input and size can be specified as well: +/// input$!( +/// input, // input buffer (optional) +/// 512, // input size (optional) +/// var4: u32, // [0, 4) var4 decoded as u32 +/// var5: [u8], // [4, ..) var5 decoded as a [u8] slice +/// ); +/// ``` +#[macro_export] +macro_rules! input { + (@inner $input:expr, $cursor:expr,) => {}; + (@size $size:expr, ) => { $size }; + + // Match a u8 variable. + // e.g input!(var1: u8, ); + (@inner $input:expr, $cursor:expr, $var:ident: u8, $($rest:tt)*) => { + let $var = $input[$cursor]; + input!(@inner $input, $cursor + 1, $($rest)*); + }; + + // Size of u8 variable. + (@size $size:expr, $var:ident: u8, $($rest:tt)*) => { + input!(@size $size + 1, $($rest)*) + }; + + // Match a u64 variable. + // e.g input!(var1: u64, ); + (@inner $input:expr, $cursor:expr, $var:ident: u64, $($rest:tt)*) => { + let $var = u64::from_le_bytes($input[$cursor..$cursor + 8].try_into().unwrap()); + input!(@inner $input, $cursor + 8, $($rest)*); + }; + + // Size of u64 variable. + (@size $size:expr, $var:ident: u64, $($rest:tt)*) => { + input!(@size $size + 8, $($rest)*) + }; + + // Match a u32 variable. + // e.g input!(var1: u32, ); + (@inner $input:expr, $cursor:expr, $var:ident: u32, $($rest:tt)*) => { + let $var = u32::from_le_bytes($input[$cursor..$cursor + 4].try_into().unwrap()); + input!(@inner $input, $cursor + 4, $($rest)*); + }; + + // Size of u32 variable. + (@size $size:expr, $var:ident: u32, $($rest:tt)*) => { + input!(@size $size + 4, $($rest)*) + }; + + // Match a u8 slice with the remaining bytes. + // e.g input!(512, var1: [u8; 32], var2: [u8], ); + (@inner $input:expr, $cursor:expr, $var:ident: [u8],) => { + let $var = &$input[$cursor..]; + }; + + // Match a u8 slice of the given size. + // e.g input!(var1: [u8; 32], ); + (@inner $input:expr, $cursor:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => { + let $var = &$input[$cursor..$cursor+$n]; + input!(@inner $input, $cursor + $n, $($rest)*); + }; + + // 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)*) => { + let mut $buffer = [0u8; $size]; + let $buffer = &mut &mut $buffer[..]; + $crate::api::input($buffer); + input!(@inner $buffer, 0, $($rest)*); + }; + + // Entry point, with the name of the buffer specified and size of the input buffer computed. + // e.g input!(buffer, var1: u32, var2: u64, ); + ($buffer: ident, $($rest:tt)*) => { + input!($buffer, input!(@size 0, $($rest)*), $($rest)*); + }; + + // Entry point, with the size of the input buffer computed. + // e.g input!(var1: u32, var2: u64, ); + ($($rest:tt)*) => { + input!(buffer, $($rest)*); + }; +} + +/// Utility macro to invoke a host function that expect a `output: &mut &mut [u8]` as last argument. +/// +/// Example: +/// ``` +/// // call `api::caller` and store the output in `caller` +/// output!(caller, [0u8; 32], api::caller,); +/// +/// // call `api::get_storage` and store the output in `address` +/// output!(address, [0u8; 32], api::get_storage, &[1u8; 32]); +/// ``` +#[macro_export] +macro_rules! output { + ($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => { + let mut $output = $buffer; + let $output = &mut &mut $output[..]; + $host_fn($($arg,)* $output); + }; +} + +/// Similar to `output!` but unwraps the result. +#[macro_export] +macro_rules! unwrap_output { + ($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => { + let mut $output = $buffer; + let $output = &mut &mut $output[..]; + $host_fn($($arg,)* $output).unwrap(); + }; +} + +/// 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..644777aff9935d26141e06e69f07495a75446710 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: &[u8; 32],); + + let mut value = [0; 32]; + api::value_transferred(&mut value); + + // Deploy the contract with no salt (equivalent to create1). + let ret = api::instantiate(code_hash, 0u64, 0u64, None, &value, &[], None, None, None); + assert!(ret.is_ok()); +} diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs new file mode 100644 index 0000000000000000000000000000000000000000..4fa2db0c8c1cf1a8758426622a5f53c46f4c9fd7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -0,0 +1,58 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. It also creates some storage. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + buffer, + input: [u8; 4], + callee: &[u8; 20], + deposit_limit: &[u8; 32], + ); + + // create 4 byte of storage before calling + api::set_storage(StorageFlags::empty(), buffer, &[1u8; 4]); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + Some(deposit_limit), + &[0u8; 32], // Value transferred to the contract. + input, + None, + ) + .unwrap(); + + // create 8 byte of storage after calling + // item of 12 bytes because we override 4 bytes + api::set_storage(StorageFlags::empty(), buffer, &[1u8; 12]); +} diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs new file mode 100644 index 0000000000000000000000000000000000000000..463706457a15c8a558a3294002d0a018998c898b --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -0,0 +1,57 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This instantiates another contract and passes some input to its constructor. +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + input: [u8; 4], + code_hash: &[u8; 32], + deposit_limit: &[u8; 32], + ); + + let value = u256_bytes(10_000u64); + let salt = [0u8; 32]; + let mut address = [0u8; 20]; + + api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + Some(deposit_limit), + &value, + input, + Some(&mut address), + None, + Some(&salt), + ) + .unwrap(); + + // Return the deployed contract address. + api::return_value(uapi::ReturnFlags::empty(), &address); +} diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs new file mode 100644 index 0000000000000000000000000000000000000000..d2efb26e5cebcc41febe22822c7a18036ae2b331 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. It also creates some transient storage. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + buffer, + len: u32, + input: [u8; 4], + callee: &[u8; 20], + ); + + let rounds = len as usize / BUFFER.len(); + let rest = len as usize / BUFFER.len(); + for i in 0..rounds { + api::set_storage(StorageFlags::TRANSIENT, &i.to_le_bytes(), &BUFFER); + } + api::set_storage(StorageFlags::TRANSIENT, &u32::MAX.to_le_bytes(), &BUFFER[..rest]); + + // Call the callee + api::call( + uapi::CallFlags::empty(), + callee, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, + &[0u8; 32], // Value transferred to the contract. + input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/crypto_hashes.rs b/substrate/frame/revive/fixtures/contracts/crypto_hashes.rs new file mode 100644 index 0000000000000000000000000000000000000000..35cc03f1e72377cd919471fb02824f1710cda6f0 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/crypto_hashes.rs @@ -0,0 +1,84 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +/// Called by the tests. +/// +/// The `call` function expects data in a certain format in the input buffer. +/// +/// 1. The first byte encodes an identifier for the crypto hash function under test. (*) +/// 2. The rest encodes the input data that is directly fed into the crypto hash function chosen in +/// 1. +/// +/// The `deploy` function then computes the chosen crypto hash function +/// given the input and puts the result into the output buffer. +/// After contract execution the test driver then asserts that the returned +/// values are equal to the expected bytes for the input and chosen hash +/// function. +/// +/// (*) The possible value for the crypto hash identifiers can be found below: +/// +/// | value | Algorithm | Bit Width | +/// |-------|-----------|-----------| +/// | 0 | SHA2 | 256 | +/// | 1 | KECCAK | 256 | +/// | 2 | BLAKE2 | 256 | +/// | 3 | BLAKE2 | 128 | +/// --------------------------------- + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + chosen_hash_fn: u8, + input: [u8], + ); + + match chosen_hash_fn { + 1 => { + let mut output = [0u8; 32]; + api::hash_sha2_256(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + 2 => { + let mut output = [0u8; 32]; + api::hash_keccak_256(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + 3 => { + let mut output = [0u8; 32]; + api::hash_blake2_256(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + 4 => { + let mut output = [0u8; 16]; + api::hash_blake2_128(input, &mut output); + api::return_value(uapi::ReturnFlags::empty(), &output); + }, + _ => panic!("unknown crypto hash function identifier"), + } +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs b/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs new file mode 100644 index 0000000000000000000000000000000000000000..6c850a9ec66312a65e183ce04e7acdc6166093fb --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Emit a debug message with an invalid utf-8 code. +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::debug_message(b"\xFC").unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs b/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs new file mode 100644 index 0000000000000000000000000000000000000000..0ce2b6b5628da348415e5a39161576161b39146b --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Emit a "Hello World!" debug message but assume that logging is disabled. +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + assert_eq!(api::debug_message(b"Hello World!"), Err(ReturnErrorCode::LoggingDisabled)); +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_works.rs b/substrate/frame/revive/fixtures/contracts/debug_message_works.rs new file mode 100644 index 0000000000000000000000000000000000000000..3a2509509d8f156300a256d1a5b0494ea961f5ef --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/debug_message_works.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Emit a "Hello World!" debug message. +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::debug_message(b"Hello World!").unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call.rs b/substrate/frame/revive/fixtures/contracts/delegate_call.rs new file mode 100644 index 0000000000000000000000000000000000000000..9fd155408af3df0030a0c432df507f506aa9d655 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/delegate_call.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: &[u8; 32],); + + let mut key = [0u8; 32]; + key[0] = 1u8; + + let mut value = [0u8; 32]; + let value = &mut &mut value[..]; + value[0] = 2u8; + + api::set_storage(StorageFlags::empty(), &key, value); + api::get_storage(StorageFlags::empty(), &key, value).unwrap(); + assert!(value[0] == 2u8); + + let input = [0u8; 0]; + api::delegate_call(uapi::CallFlags::empty(), code_hash, &input, None).unwrap(); + + api::get_storage(StorageFlags::empty(), &key, value).unwrap(); + assert!(value[0] == 1u8); +} diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..95c1bd2aa6cd666669079076251804c373039486 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::u64_output; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut key = [0u8; 32]; + key[0] = 1u8; + + // Place a value in storage. + let mut value = [0u8; 32]; + let value = &mut &mut value[..]; + value[0] = 1u8; + api::set_storage(StorageFlags::empty(), &key, value); + + // Assert that `value_transferred` is equal to the value + // passed to the `caller` contract: 1337. + let value = u64_output!(api::value_transferred,); + assert_eq!(value, 1337); + + // Assert that ALICE is the caller of the contract. + let mut caller = [0u8; 20]; + 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 new file mode 100644 index 0000000000000000000000000000000000000000..20f8ec3364ee27b12077a60b80c7332f749d27d7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: &[u8; 32],); + + // Delegate call into passed code hash. + let input = [0u8; 0]; + api::delegate_call(uapi::CallFlags::empty(), code_hash, &input, None).unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs new file mode 100644 index 0000000000000000000000000000000000000000..8342f4acf95296d621c855408aacc5b8457a4234 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -0,0 +1,86 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +const ADDRESS_KEY: [u8; 32] = [0u8; 32]; +const VALUE: [u8; 32] = u256_bytes(65536); + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(code_hash: &[u8; 32],); + + let input = [0u8; 0]; + let mut address = [0u8; 20]; + let salt = [47u8; 32]; + + api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &VALUE, + &input, + Some(&mut address), + None, + Some(&salt), + ) + .unwrap(); + + // Return the deployed contract address. + api::set_storage(StorageFlags::empty(), &ADDRESS_KEY, &address); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut callee_addr = [0u8; 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, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &VALUE, + &[0u8; 1], + None, + ); + assert!(matches!(res, Err(uapi::ReturnErrorCode::CalleeTrapped))); + + // Call the destination contract regularly, forcing it to self-destruct. + api::call( + uapi::CallFlags::empty(), + &callee_addr, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, // How much proof_size weight to devote for the execution. 0 = all. + None, // No deposit limit. + &VALUE, + &[0u8; 0], + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs new file mode 100644 index 0000000000000000000000000000000000000000..0d644a4238c4c44d9f70d9379d6249bebfd483f3 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -0,0 +1,41 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{u256_bytes, 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() { + let balance = u64_output!(api::balance,); + let minimum_balance = u64_output!(api::minimum_balance,); + + // Make the transferred value exceed the balance by adding the minimum balance. + let balance = balance + minimum_balance; + + // Try to self-destruct by sending more balance to the 0 address. + // The call will fail because a contract transfer has a keep alive requirement. + let res = api::transfer(&[0u8; 20], &u256_bytes(balance)); + assert!(matches!(res, Err(uapi::ReturnErrorCode::TransferFailed))); +} diff --git a/substrate/frame/revive/fixtures/contracts/dummy.rs b/substrate/frame/revive/fixtures/contracts/dummy.rs new file mode 100644 index 0000000000000000000000000000000000000000..c7294e99139e89e359fd9e0c117e34c9644396e2 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/dummy.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +// Export that is never called. We can put code here that should be in the binary +// but is never supposed to be run. +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call_never() { + // Make sure the 0xDEADBEEF pattern appears in the binary by + // making it opaque to the optimizer. The benchmarking code will + // just find and replace this pattern to make the code unique when + // necessary. + api::return_value(ReturnFlags::empty(), &[0xDE, 0xAD, 0xBE, 0xEF]); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs b/substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs new file mode 100644 index 0000000000000000000000000000000000000000..0f28ca2c819804ead9621cf05243f62c23289a77 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/ecdsa_recover.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + signature: [u8; 65], + hash: [u8; 32], + ); + + let mut output = [0u8; 33]; + api::ecdsa_recover( + &signature[..].try_into().unwrap(), + &hash[..].try_into().unwrap(), + &mut output, + ) + .unwrap(); + api::return_value(uapi::ReturnFlags::empty(), &output); +} diff --git a/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs new file mode 100644 index 0000000000000000000000000000000000000000..5c438c1a75a13566ba05a48658c4bf56c45d0460 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.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}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + let buffer = [1u8, 2, 3, 4]; + let topics = [[42u8; 32]; 1]; + api::deposit_event(&topics, &buffer); + api::return_value(uapi::ReturnFlags::empty(), &buffer); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + unreachable!() +} diff --git a/substrate/frame/revive/fixtures/contracts/event_size.rs b/substrate/frame/revive/fixtures/contracts/event_size.rs new file mode 100644 index 0000000000000000000000000000000000000000..7f04ae42765aa5a81a27a9fa24969ba28d9a8d00 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/event_size.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}; + +static BUFFER: [u8; 16 * 1024 + 1] = [0u8; 16 * 1024 + 1]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32,); + + let data = &BUFFER[..len as usize]; + let topics = [[0u8; 32]; 0]; + + api::deposit_event(&topics, data); +} diff --git a/substrate/frame/revive/fixtures/contracts/float_instruction.rs b/substrate/frame/revive/fixtures/contracts/float_instruction.rs new file mode 100644 index 0000000000000000000000000000000000000000..b1eaaf8543c67b6cc25f5bd6a4a44a2b47362c68 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/float_instruction.rs @@ -0,0 +1,34 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} + +#[no_mangle] +pub extern "C" fn add(a: f32, b: f32) -> f32 { + a + b +} diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs new file mode 100644 index 0000000000000000000000000000000000000000..9764859c619b90452eeacbb337a7b94a2cd7d86c --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -0,0 +1,52 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(buffer, 36, code_hash: &[u8; 32],); + let input = &buffer[32..]; + + let err_code = match api::instantiate( + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. + 0u64, /* How much proof_size weight to devote for the execution. 0 = + * all. */ + None, // No deposit limit. + &u256_bytes(10_000u64), // Value to transfer. + input, + None, + None, + Some(&[0u8; 32]), // Salt. + ) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + // Exit with success and take transfer return code to the output buffer. + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs b/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs new file mode 100644 index 0000000000000000000000000000000000000000..c5fb382c32767602593ba4998d6a1008f04636c5 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs @@ -0,0 +1,41 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] +#![no_main] + +extern crate common; + +use common::input; +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(rounds: u32, start: u32, div: u32, mult: u32, add: u32, ); + + let mut acc = start; + + for _ in 0..rounds { + acc = acc / div * mult + add; + } + + api::return_value(ReturnFlags::empty(), start.to_le_bytes().as_ref()); +} diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs new file mode 100644 index 0000000000000000000000000000000000000000..2efacb4e683ffedbe1dce29b07130fd2b26fed87 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -0,0 +1,68 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This contract tests the behavior of locking / unlocking delegate_dependencies when delegate +//! calling into a contract. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +const 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], + ); + + match action { + // 1 = Lock delegate dependency + 1 => { + api::lock_delegate_dependency(code_hash); + }, + // 2 = Unlock delegate dependency + 2 => { + api::unlock_delegate_dependency(code_hash); + }, + // 3 = Terminate + 3 => { + api::terminate(Ð_ALICE); + }, + // Everything else is a noop + _ => {}, + } + + if delegate_call { + api::delegate_call(uapi::CallFlags::empty(), code_hash, &[], None).unwrap(); + } +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + load_input(false); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + load_input(true); +} diff --git a/substrate/frame/revive/fixtures/contracts/multi_store.rs b/substrate/frame/revive/fixtures/contracts/multi_store.rs new file mode 100644 index 0000000000000000000000000000000000000000..079a4548e78d710472f4c6cae155dff48c365ca1 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/multi_store.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Does two stores to two separate storage items +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + size1: u32, + size2: u32, + ); + + // Place a values in storage sizes are specified in the input buffer. + // We don't care about the contents of the storage item. + api::set_storage(StorageFlags::empty(), &[1u8; 32], &BUFFER[0..size1 as _]); + api::set_storage(StorageFlags::empty(), &[2u8; 32], &BUFFER[0..size2 as _]); +} diff --git a/substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs b/substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a59b6e33d89a1a07e3a66f71b070ac5169a77b1 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/new_set_code_hash_contract.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::return_value(uapi::ReturnFlags::empty(), &2u32.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/noop.rs b/substrate/frame/revive/fixtures/contracts/noop.rs new file mode 100644 index 0000000000000000000000000000000000000000..48d8a6896d62b1022aeb29ca484deae2be0c1035 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/noop.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] +#![no_main] + +extern crate common; + +use common::input; +use uapi::HostFn; + +#[polkavm_derive::polkavm_import] +extern "C" { + pub fn noop(); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(rounds: u32, ); + + for _ in 0..rounds { + unsafe { + noop(); + } + } +} diff --git a/substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs b/substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs new file mode 100644 index 0000000000000000000000000000000000000000..55115f8642f3ec5c8ef3319b62b60f67cc528864 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/ok_trap_revert.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + ok_trap_revert(); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + ok_trap_revert(); +} + +#[no_mangle] +fn ok_trap_revert() { + input!(buffer, 4,); + match buffer.first().unwrap_or(&0) { + 1 => api::return_value(uapi::ReturnFlags::REVERT, &[0u8; 0]), + 2 => panic!(), + _ => {}, + }; +} diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs new file mode 100644 index 0000000000000000000000000000000000000000..ea74d56867f5efd1dd2f671a7b015418f7b1d7b7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -0,0 +1,50 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This fixture tests if read-only call works as expected. +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 256, + callee_addr: &[u8; 20], + callee_input: [u8], + ); + + // Call the callee + api::call( + uapi::CallFlags::READ_ONLY, + callee_addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. + callee_input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs new file mode 100644 index 0000000000000000000000000000000000000000..2e70d67d8c738c700f35d281a51605e7b9c76170 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/recurse.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls itself as many times as passed as argument. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(calls_left: u32, ); + + // own address + let mut addr = [0u8; 20]; + api::address(&mut addr); + + if calls_left == 0 { + return + } + + api::call( + uapi::CallFlags::ALLOW_REENTRY, + &addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much deposit_limit to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. + &(calls_left - 1).to_le_bytes(), + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/return_with_data.rs b/substrate/frame/revive/fixtures/contracts/return_with_data.rs new file mode 100644 index 0000000000000000000000000000000000000000..47a1cc9111928ec7cb93983b36dce1b3e02a4936 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/return_with_data.rs @@ -0,0 +1,47 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + call(); +} + +/// Reads the first byte as the exit status and copy all but the first 4 bytes of the input as +/// output data. +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + input, 128, + exit_status: [u8; 4], + output: [u8], + ); + + // Burn some PoV, clear_storage consumes some PoV as in order to clear the storage we need to we + // need to read its size first. + api::clear_storage(StorageFlags::empty(), b""); + + let exit_status = uapi::ReturnFlags::from_bits(exit_status[0] as u32).unwrap(); + api::return_value(exit_status, output); +} diff --git a/substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs b/substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs new file mode 100644 index 0000000000000000000000000000000000000000..11eaaa7c86247bc34060f6f701a3b773a55923bc --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/run_out_of_gas.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + #[allow(clippy::empty_loop)] + loop {} +} diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs new file mode 100644 index 0000000000000000000000000000000000000000..524979991ec7ab1020073d48022d773ca809c3b8 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -0,0 +1,57 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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}; + +const ETH_DJANGO: [u8; 20] = [4u8; 20]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + // If the input data is not empty, then recursively call self with empty input data. + // This should trap instead of self-destructing since a contract cannot be removed, while it's + // in the execution stack. If the recursive call traps, then trap here as well. + input!(input, 4,); + + if !input.is_empty() { + 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. + &[0u8; 32], // Value to transfer. + &[0u8; 0], + None, + ) + .unwrap(); + } else { + // Try to terminate and give balance to django. + api::terminate(Ð_DJANGO); + } +} diff --git a/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs new file mode 100644 index 0000000000000000000000000000000000000000..3285aecbe780971ebb2af792529c62efd275b4b7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + api::terminate(&[0u8; 20]); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/set_code_hash.rs b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs new file mode 100644 index 0000000000000000000000000000000000000000..75995d7bb8a2809d02d4796576efc1d0e29a79d0 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(addr: &[u8; 32],); + api::set_code_hash(addr).unwrap(); + + // we return 1 after setting new code_hash + // next `call` will NOT return this value, because contract code has been changed + api::return_value(uapi::ReturnFlags::empty(), &1u32.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/set_empty_storage.rs b/substrate/frame/revive/fixtures/contracts/set_empty_storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..f8bbfe3faa5beef64b80e131c8a6269be5b301ab --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/set_empty_storage.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::set_storage(StorageFlags::empty(), &[0u8; 32], &[0u8; 4]); +} diff --git a/substrate/frame/revive/fixtures/contracts/set_transient_storage.rs b/substrate/frame/revive/fixtures/contracts/set_transient_storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..a8a1fbd651489b490a389d824183c86d2919fa8a --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/set_transient_storage.rs @@ -0,0 +1,41 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32, ); + + let rounds = len as usize / BUFFER.len(); + let rest = len as usize / BUFFER.len(); + for i in 0..rounds { + api::set_storage(StorageFlags::TRANSIENT, &i.to_le_bytes(), &BUFFER); + } + api::set_storage(StorageFlags::TRANSIENT, &u32::MAX.to_le_bytes(), &BUFFER[..rest]); +} diff --git a/substrate/frame/revive/fixtures/contracts/sr25519_verify.rs b/substrate/frame/revive/fixtures/contracts/sr25519_verify.rs new file mode 100644 index 0000000000000000000000000000000000000000..8920ce0d4f6c249e701cae22c9490bfcfe72fdbc --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/sr25519_verify.rs @@ -0,0 +1,48 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + signature: [u8; 64], + pub_key: [u8; 32], + msg: [u8; 11], + ); + + let exit_status = match api::sr25519_verify( + &signature.try_into().unwrap(), + msg, + &pub_key.try_into().unwrap(), + ) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + // Exit with success and take transfer return code to the output buffer. + api::return_value(uapi::ReturnFlags::empty(), &exit_status.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/storage.rs b/substrate/frame/revive/fixtures/contracts/storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc21e322466cc216896c7ef7b2764560d3481544 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/storage.rs @@ -0,0 +1,63 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This contract tests the storage APIs. It sets and clears storage values using the different +//! versions of the storage APIs. +#![no_std] +#![no_main] + +use common::unwrap_output; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + const KEY: [u8; 32] = [1u8; 32]; + const VALUE_1: [u8; 4] = [1u8; 4]; + const VALUE_2: [u8; 4] = [2u8; 4]; + const VALUE_3: [u8; 4] = [3u8; 4]; + + api::set_storage(StorageFlags::empty(), &KEY, &VALUE_1); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 4], api::get_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_1); + + let existing = api::set_storage(StorageFlags::empty(), &KEY, &VALUE_2); + assert_eq!(existing, Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 4], api::get_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_2); + + api::clear_storage(StorageFlags::empty(), &KEY); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), None); + + let existing = api::set_storage(StorageFlags::empty(), &KEY, &VALUE_3); + assert_eq!(existing, None); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_3); + + api::clear_storage(StorageFlags::empty(), &KEY); + assert_eq!(api::contains_storage(StorageFlags::empty(), &KEY), None); + let existing = api::set_storage(StorageFlags::empty(), &KEY, &VALUE_3); + assert_eq!(existing, None); + unwrap_output!(val, [0u8; 32], api::take_storage, StorageFlags::empty(), &KEY); + assert_eq!(**val, VALUE_3); +} diff --git a/substrate/frame/revive/fixtures/contracts/storage_size.rs b/substrate/frame/revive/fixtures/contracts/storage_size.rs new file mode 100644 index 0000000000000000000000000000000000000000..617e8d2ea79ffcbd3be5aad9bc6f7bc1beb56054 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/storage_size.rs @@ -0,0 +1,50 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static mut BUFFER: [u8; 16 * 1024 + 1] = [0u8; 16 * 1024 + 1]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32, ); + + let data = unsafe { + &BUFFER[..len as usize] + }; + + // Place a garbage value in storage, the size of which is specified by the call input. + let mut key = [0u8; 32]; + key[0] = 1; + + api::set_storage(StorageFlags::empty(), &key, data); + + let data = unsafe { + &mut &mut BUFFER[..] + }; + api::get_storage(StorageFlags::empty(), &key, data).unwrap(); + assert_eq!(data.len(), len as usize); +} diff --git a/substrate/frame/revive/fixtures/contracts/store_call.rs b/substrate/frame/revive/fixtures/contracts/store_call.rs new file mode 100644 index 0000000000000000000000000000000000000000..b08d445191e574859b425b16d045eed0de9162c7 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/store_call.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 512] = [0u8; 512]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(len: u32, ); + + let data = &BUFFER[..len as usize]; + + // Place a garbage value in storage, the size of which is specified by the call input. + let mut key = [0u8; 32]; + key[0] = 1; + + api::set_storage(StorageFlags::empty(), &key, data); +} diff --git a/substrate/frame/revive/fixtures/contracts/store_deploy.rs b/substrate/frame/revive/fixtures/contracts/store_deploy.rs new file mode 100644 index 0000000000000000000000000000000000000000..e08c79d78f3ba07e95d5d5cb17b0ecbea5b639e3 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/store_deploy.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +static BUFFER: [u8; 16 * 1024 + 1] = [0u8; 16 * 1024 + 1]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(len: u32, ); + + let data = &BUFFER[..len as usize]; + + // place a garbage value in storage, the size of which is specified by the call input. + let mut key = [0u8; 32]; + key[0] = 1; + + api::set_storage(StorageFlags::empty(), &key, data); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs new file mode 100644 index 0000000000000000000000000000000000000000..bfeca9b8b4a42d0276285cf7332ad6a9ddb77e09 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::u256_bytes; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let ret_code = match api::transfer(&[0u8; 20], &u256_bytes(100u64)) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + // Exit with success and take transfer return code to the output buffer. + api::return_value(uapi::ReturnFlags::empty(), &ret_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/transient_storage.rs b/substrate/frame/revive/fixtures/contracts/transient_storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..aa0a69435a694587075eb660caf9991612aa9e81 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/transient_storage.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This contract tests the transient storage APIs. +#![no_std] +#![no_main] + +use common::unwrap_output; +use uapi::{HostFn, HostFnImpl as api, StorageFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + const KEY: [u8; 32] = [1u8; 32]; + const VALUE_1: [u8; 4] = [1u8; 4]; + const VALUE_2: [u8; 5] = [2u8; 5]; + const VALUE_3: [u8; 6] = [3u8; 6]; + + let existing = api::set_storage(StorageFlags::TRANSIENT, &KEY, &VALUE_1); + assert_eq!(existing, None); + assert_eq!(api::contains_storage(StorageFlags::TRANSIENT, &KEY), Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::TRANSIENT, &KEY); + assert_eq!(**val, VALUE_1); + + let existing = api::set_storage(StorageFlags::TRANSIENT, &KEY, &VALUE_2); + assert_eq!(existing, Some(VALUE_1.len() as _)); + unwrap_output!(val, [0u8; 32], api::get_storage, StorageFlags::TRANSIENT, &KEY); + assert_eq!(**val, VALUE_2); + + assert_eq!(api::clear_storage(StorageFlags::TRANSIENT, &KEY), Some(VALUE_2.len() as _)); + assert_eq!(api::contains_storage(StorageFlags::TRANSIENT, &KEY), None); + + let existing = api::set_storage(StorageFlags::TRANSIENT, &KEY, &VALUE_3); + assert_eq!(existing, None); + unwrap_output!(val, [0u8; 128], api::take_storage, StorageFlags::TRANSIENT, &KEY); + assert_eq!(**val, VALUE_3); +} diff --git a/substrate/frame/revive/fixtures/contracts/xcm_execute.rs b/substrate/frame/revive/fixtures/contracts/xcm_execute.rs new file mode 100644 index 0000000000000000000000000000000000000000..1d570ffead71845c77d664bf5b911bf66794e907 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/xcm_execute.rs @@ -0,0 +1,40 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(512, msg: [u8],); + + #[allow(deprecated)] + let err_code = match api::xcm_execute(msg) { + Ok(_) => 0u32, + Err(code) => code as u32, + }; + + api::return_value(uapi::ReturnFlags::empty(), &err_code.to_le_bytes()); +} diff --git a/substrate/frame/revive/fixtures/contracts/xcm_send.rs b/substrate/frame/revive/fixtures/contracts/xcm_send.rs new file mode 100644 index 0000000000000000000000000000000000000000..6d4629e748a76d961506f82602681de7587467e9 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/xcm_send.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 512, + dest: [u8; 3], + msg: [u8], + ); + + let mut message_id = [0u8; 32]; + + #[allow(deprecated)] + api::xcm_send(dest, msg, &mut message_id).unwrap(); + api::return_value(uapi::ReturnFlags::empty(), &message_id); +} diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5548dca66d0753fdbee76eaaa92f392a39f8f64a --- /dev/null +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -0,0 +1,74 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +/// Load a given wasm module and returns a wasm binary contents along with it's hash. +#[cfg(feature = "std")] +pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); + log::debug!("Loading fixture from {fixture_path:?}"); + let binary = std::fs::read(fixture_path)?; + let code_hash = sp_io::hashing::keccak_256(&binary); + Ok((binary, sp_core::H256(code_hash))) +} + +/// Fixtures used in runtime benchmarks. +/// +/// We explicitly include those fixtures into the binary to make them +/// available in no-std environments (runtime benchmarks). +pub mod bench { + use alloc::vec::Vec; + + #[cfg(feature = "riscv")] + macro_rules! fixture { + ($name: literal) => { + include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".polkavm")) + }; + } + #[cfg(not(feature = "riscv"))] + macro_rules! fixture { + ($name: literal) => { + &[] + }; + } + pub const DUMMY: &[u8] = fixture!("dummy"); + pub const NOOP: &[u8] = fixture!("noop"); + pub const INSTR: &[u8] = fixture!("instr_benchmark"); + + pub fn dummy_unique(replace_with: u32) -> Vec { + let mut dummy = DUMMY.to_vec(); + let idx = dummy + .windows(4) + .position(|w| w == &[0xDE, 0xAD, 0xBE, 0xEF]) + .expect("Benchmark fixture contains this pattern; qed"); + dummy[idx..idx + 4].copy_from_slice(&replace_with.to_le_bytes()); + dummy + } +} + +#[cfg(test)] +mod test { + #[test] + fn out_dir_should_have_compiled_mocks() { + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + assert!(out_dir.join("dummy.polkavm").exists()); + } +} diff --git a/substrate/frame/revive/mock-network/Cargo.toml b/substrate/frame/revive/mock-network/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..85656a57b49ccf22c2f6bdd0e97f5f4dc557ff54 --- /dev/null +++ b/substrate/frame/revive/mock-network/Cargo.toml @@ -0,0 +1,89 @@ +[package] +name = "pallet-revive-mock-network" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "A mock network for testing pallet-revive." + +[lints] +workspace = true + +[dependencies] +codec = { features = ["derive", "max-encoded-len"], workspace = true } + +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-assets = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-revive = { workspace = true, default-features = true } +pallet-revive-uapi = { workspace = true } +pallet-revive-proc-macro = { workspace = true, default-features = true } +pallet-message-queue = { workspace = true, default-features = true } +pallet-proxy = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +pallet-utility = { workspace = true, default-features = true } +pallet-xcm = { workspace = true } +polkadot-parachain-primitives = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } +polkadot-runtime-parachains = { workspace = true, default-features = true } +scale-info = { features = ["derive"], workspace = true } +sp-api = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-keystore = { workspace = true, default-features = true } +sp-runtime = { workspace = true } +sp-tracing = { workspace = true, default-features = true } +xcm = { workspace = true } +xcm-builder = { workspace = true, default-features = true } +xcm-executor = { workspace = true } +xcm-simulator = { workspace = true, default-features = true } + +[dev-dependencies] +assert_matches = { workspace = true } +pretty_assertions = { workspace = true } +pallet-revive-fixtures = { workspace = true } + +[features] +default = ["std"] +riscv = ["pallet-revive-fixtures/riscv"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-proxy/std", + "pallet-revive-fixtures/std", + "pallet-revive/std", + "pallet-timestamp/std", + "pallet-utility/std", + "pallet-xcm/std", + "scale-info/std", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-keystore/std", + "sp-runtime/std", + "xcm-executor/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-revive/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] diff --git a/substrate/frame/revive/mock-network/src/lib.rs b/substrate/frame/revive/mock-network/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..2e4f273a010319eead81b8ba74acce457be99cf9 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/lib.rs @@ -0,0 +1,152 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +pub mod mocks; +pub mod parachain; +pub mod primitives; +pub mod relay_chain; + +#[cfg(all(test, feature = "riscv"))] +mod tests; + +use crate::primitives::{AccountId, UNITS}; +pub use pallet_revive::test_utils::{ALICE, BOB}; +use sp_runtime::BuildStorage; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; +pub use xcm_simulator::TestExt; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; + +// Accounts +pub const ADMIN: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); + +// Balances +pub const INITIAL_BALANCE: u128 = 1_000_000_000 * UNITS; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + ], + } +} + +pub fn relay_sovereign_account_id() -> AccountId { + let location: Location = (Parent,).into(); + parachain::SovereignAccountOf::convert_location(&location).unwrap() +} + +pub fn parachain_sovereign_account_id(para: u32) -> AccountId { + let location: Location = (Parachain(para),).into(); + relay_chain::SovereignAccountOf::convert_location(&location).unwrap() +} + +pub fn parachain_account_sovereign_account_id( + para: u32, + who: sp_runtime::AccountId32, +) -> AccountId { + let location: Location = ( + Parachain(para), + AccountId32 { network: Some(relay_chain::RelayNetwork::get()), id: who.into() }, + ) + .into(); + relay_chain::SovereignAccountOf::convert_location(&location).unwrap() +} + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (relay_sovereign_account_id(), INITIAL_BALANCE), + (BOB, INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![ + (0u128, ADMIN, false, 1u128), // Create derivative asset for relay's native token + ], + metadata: Default::default(), + accounts: vec![ + (0u128, ALICE, INITIAL_BALANCE), + (0u128, relay_sovereign_account_id(), INITIAL_BALANCE), + ], + next_asset_id: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + sp_tracing::try_init_simple(); + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext() -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (parachain_sovereign_account_id(1), INITIAL_BALANCE), + (parachain_account_sovereign_account_id(1, ALICE), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub type ParachainPalletXcm = pallet_xcm::Pallet; +pub type ParachainBalances = pallet_balances::Pallet; diff --git a/substrate/frame/revive/mock-network/src/mocks.rs b/substrate/frame/revive/mock-network/src/mocks.rs new file mode 100644 index 0000000000000000000000000000000000000000..bf3baec7a524abc0a9c5eee8e637b4b9a9cf3e04 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/mocks.rs @@ -0,0 +1,18 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +pub mod msg_queue; +pub mod relay_message_queue; diff --git a/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs b/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs new file mode 100644 index 0000000000000000000000000000000000000000..6e922c16c2982b8399978511b9dd5558ba2c0ce8 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs @@ -0,0 +1,186 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Parachain runtime mock. + +use codec::{Decode, Encode}; + +use frame_support::weights::Weight; +use polkadot_parachain_primitives::primitives::{ + DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler, +}; +use polkadot_primitives::BlockNumber as RelayBlockNumber; +use sp_runtime::traits::{Get, Hash}; + +use xcm::{latest::prelude::*, VersionedXcm}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + #[pallet::storage] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + ParachainId::::get() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + pub fn parachain_id() -> ParaId { + ParachainId::::get() + } + + pub fn received_dmp() -> Vec> { + ReceivedDmp::::get() + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut message_hash, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)), + Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete { used, error } => + (Ok(used), Event::Fail(Some(hash), error)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = data_ref; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = + VersionedXcm::::decode(&mut remaining_fragments) + { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_versioned = VersionedXcm::::decode(&mut &data[..]); + match maybe_versioned { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(versioned) => match Xcm::try_from(versioned) { + Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), + Ok(x) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x.clone(), + &mut id, + limit, + Weight::zero(), + ); + ReceivedDmp::::append(x); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + }, + } + } + limit + } + } +} diff --git a/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs b/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs new file mode 100644 index 0000000000000000000000000000000000000000..14099965e3f173d7f06b86c08b722ceeded26418 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs @@ -0,0 +1,52 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use frame_support::{parameter_types, weights::Weight}; +use xcm::latest::prelude::*; +use xcm_simulator::{ + AggregateMessageOrigin, ProcessMessage, ProcessMessageError, UmpQueueId, WeightMeter, +}; + +use crate::relay_chain::{RuntimeCall, XcmConfig}; + +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter, id) + } +} diff --git a/substrate/frame/revive/mock-network/src/parachain.rs b/substrate/frame/revive/mock-network/src/parachain.rs new file mode 100644 index 0000000000000000000000000000000000000000..3def48cca96b829a91b29379ffb17cea95e12e22 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/parachain.rs @@ -0,0 +1,346 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Parachain runtime mock. + +mod contracts_config; +use crate::{ + mocks::msg_queue::pallet as mock_msg_queue, + primitives::{AccountId, AssetIdForAssets, Balance}, +}; +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, EverythingBut, Nothing}, + weights::{ + constants::{WEIGHT_PROOF_SIZE_PER_MB, WEIGHT_REF_TIME_PER_SECOND}, + Weight, + }, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +use pallet_xcm::XcmPassthrough; +use sp_core::{ConstU32, ConstU64, H256}; +use sp_runtime::traits::{Get, IdentityLookup, MaybeEquivalence}; + +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, IsConcrete, NativeAsset, + NoChecking, ParentAsSuperuser, ParentIsPreset, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, WithComputedOrigin, +}; +use xcm_executor::{traits::JustTry, Config, XcmExecutor}; + +pub type SovereignAccountOf = + (AccountId32Aliases, ParentIsPreset); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Block = Block; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeEvent = RuntimeEvent; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type WeightInfo = (); +} + +parameter_types! { + pub const AssetDeposit: u128 = 1_000_000; + pub const MetadataDepositBase: u128 = 1_000_000; + pub const MetadataDepositPerByte: u128 = 100_000; + pub const AssetAccountDeposit: u128 = 1_000_000; + pub const ApprovalDeposit: u128 = 1_000_000; + pub const AssetsStringLimit: u32 = 50; + pub const RemoveItemsLimit: u32 = 50; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetIdForAssets; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type AssetAccountDeposit = AssetAccountDeposit; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); + type RemoveItemsLimit = RemoveItemsLimit; + type AssetIdParameter = AssetIdForAssets; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const TokenLocation: Location = Here.into_location(); + pub const RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); +} + +pub type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + ParentAsSuperuser, + SignedAccountId32AsNative, + XcmPassthrough, +); + +parameter_types! { + pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); + pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1_000_000_000_000, 1024 * 1024); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub ForeignPrefix: Location = (Parent,).into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub TrustedLockPairs: (Location, AssetFilter) = + (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); +} + +pub fn estimate_message_fee(number_of_instructions: u64) -> u128 { + let weight = estimate_weight(number_of_instructions); + + estimate_fee_for_weight(weight) +} + +pub fn estimate_weight(number_of_instructions: u64) -> Weight { + XcmInstructionWeight::get().saturating_mul(number_of_instructions) +} + +pub fn estimate_fee_for_weight(weight: Weight) -> u128 { + let (_, units_per_second, units_per_mb) = TokensPerSecondPerMegabyte::get(); + + units_per_second * (weight.ref_time() as u128) / (WEIGHT_REF_TIME_PER_SECOND as u128) + + units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128) +} + +pub type LocalBalancesTransactor = + FungibleAdapter, SovereignAccountOf, AccountId, ()>; + +pub struct FromLocationToAsset(PhantomData<(Location, AssetId)>); +impl MaybeEquivalence + for FromLocationToAsset +{ + fn convert(value: &Location) -> Option { + match value.unpack() { + (1, []) => Some(0 as AssetIdForAssets), + (1, [Parachain(para_id)]) => Some(*para_id as AssetIdForAssets), + _ => None, + } + } + + fn convert_back(_id: &AssetIdForAssets) -> Option { + None + } +} + +pub type ForeignAssetsTransactor = FungiblesAdapter< + Assets, + ConvertedConcreteId< + AssetIdForAssets, + Balance, + FromLocationToAsset, + JustTry, + >, + SovereignAccountOf, + AccountId, + NoChecking, + CheckingAccount, +>; + +/// Means for transacting assets on this chain +pub type AssetTransactors = (LocalBalancesTransactor, ForeignAssetsTransactor); + +pub struct ParentRelay; +impl Contains for ParentRelay { + fn contains(location: &Location) -> bool { + location.contains_parents_only(1) + } +} +pub struct ThisParachain; +impl Contains for ThisParachain { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, [Junction::AccountId32 { .. }])) + } +} + +pub type XcmRouter = crate::ParachainXcmRouter; + +pub type Barrier = ( + xcm_builder::AllowUnpaidExecutionFrom, + WithComputedOrigin< + (AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom), + UniversalLocation, + ConstU32<1>, + >, +); + +parameter_types! { + pub NftCollectionOne: AssetFilter + = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); + pub NftCollectionOneForRelay: (AssetFilter, Location) + = (NftCollectionOne::get(), Parent.into()); + pub RelayNativeAsset: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId((Parent, Here).into()) }); + pub RelayNativeAssetForRelay: (AssetFilter, Location) = (RelayNativeAsset::get(), Parent.into()); +} +pub type TrustedTeleporters = + (xcm_builder::Case, xcm_builder::Case); +pub type TrustedReserves = EverythingBut>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = (NativeAsset, TrustedReserves); + type IsTeleporter = TrustedTeleporters; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetLocker = PolkadotXcm; + type AssetExchanger = (); + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type FeeManager = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +pub struct TrustedLockerCase(PhantomData); +impl> ContainsPair for TrustedLockerCase { + fn contains(origin: &Location, asset: &Asset) -> bool { + let (o, a) = T::get(); + a.matches(asset) && &o == origin + } +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = TrustedLockerCase; + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; +} + +type Block = frame_system::mocking::MockBlock; + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime + { + System: frame_system, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + MsgQueue: mock_msg_queue, + PolkadotXcm: pallet_xcm, + Contracts: pallet_revive, + Assets: pallet_assets, + } +); diff --git a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs new file mode 100644 index 0000000000000000000000000000000000000000..678e7a44490045f827adc1cfb014be0d80684108 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs @@ -0,0 +1,27 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use super::{Balances, Runtime, RuntimeCall, RuntimeEvent}; +use crate::parachain::RuntimeHoldReason; +use frame_support::derive_impl; + +#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] +impl pallet_revive::Config for Runtime { + type AddressMapper = pallet_revive::DefaultAddressMapper; + type Currency = Balances; + type Time = super::Timestamp; + type Xcm = pallet_xcm::Pallet; +} diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/substrate/frame/revive/mock-network/src/primitives.rs similarity index 52% rename from bridges/bin/runtime-common/src/extensions/mod.rs rename to substrate/frame/revive/mock-network/src/primitives.rs index 3f1b506aaae3ef66fe6f44379258356a2074464c..efc42772f88ade5868492964b1b383c26ee689ac 100644 --- a/bridges/bin/runtime-common/src/extensions/mod.rs +++ b/substrate/frame/revive/mock-network/src/primitives.rs @@ -1,21 +1,23 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. +// This file is part of Polkadot. -// Parity Bridges Common 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. -// Parity Bridges Common 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 Parity Bridges Common. If not, see . +// along with Polkadot. If not, see . -//! Bridge-specific transaction extensions. +pub type Balance = u128; -pub mod check_obsolete_extension; -pub mod priority_calculator; -pub mod refund_relayer_extension; +pub const UNITS: Balance = 10_000_000_000; +pub const CENTS: Balance = UNITS / 100; // 100_000_000 + +pub type AccountId = sp_runtime::AccountId32; +pub type AssetIdForAssets = u128; diff --git a/substrate/frame/revive/mock-network/src/relay_chain.rs b/substrate/frame/revive/mock-network/src/relay_chain.rs new file mode 100644 index 0000000000000000000000000000000000000000..8829fff3d0436d95b3e44d87b2258d13c167efc1 --- /dev/null +++ b/substrate/frame/revive/mock-network/src/relay_chain.rs @@ -0,0 +1,239 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Relay chain runtime mock. + +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{Contains, Everything, Nothing}, + weights::Weight, +}; + +use frame_system::EnsureRoot; +use sp_core::{ConstU32, H256}; +use sp_runtime::traits::IdentityLookup; + +use polkadot_parachain_primitives::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, DescribeAllTerminal, DescribeFamily, FixedRateOfFungible, + FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, +}; +use xcm_executor::{Config, XcmExecutor}; + +use super::{ + mocks::relay_message_queue::*, + primitives::{AccountId, Balance}, +}; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Block = Block; + type Nonce = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +parameter_types! { + pub RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub const TokenLocation: Location = Here.into_location(); + pub UniversalLocation: InteriorLocation = RelayNetwork::get().into(); + pub UnitWeightCost: u64 = 1_000; +} + +pub type SovereignAccountOf = ( + HashedDescription>, + AccountId32Aliases, + ChildParachainConvertsVia, +); + +pub type LocalBalancesTransactor = + FungibleAdapter, SovereignAccountOf, AccountId, ()>; + +pub type AssetTransactors = LocalBalancesTransactor; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); + pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = + (AssetId(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub struct ChildrenParachains; +impl Contains for ChildrenParachains { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, [Parachain(_)])) + } +} + +pub type XcmRouter = crate::RelayChainXcmRouter; +pub type Barrier = WithComputedOrigin< + ( + AllowExplicitUnpaidExecutionFrom, + AllowTopLevelPaidExecutionFrom, + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<1>, +>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = XcmPallet; + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type FeeManager = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type TrustedLockers = (); + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot; +} + +impl origin::Config for Runtime {} + +type Block = frame_system::mocking::MockBlock; + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Size = u32; + type HeapSize = MessageQueueHeapSize; + type MaxStale = MessageQueueMaxStale; + type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = (); + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type WeightInfo = (); + type QueuePausedQuery = (); +} + +construct_runtime!( + pub enum Runtime { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + XcmPallet: pallet_xcm, + MessageQueue: pallet_message_queue, + } +); diff --git a/substrate/frame/revive/mock-network/src/tests.rs b/substrate/frame/revive/mock-network/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd05726a1a45f505e5105c673a7d0944f0cb26cb --- /dev/null +++ b/substrate/frame/revive/mock-network/src/tests.rs @@ -0,0 +1,204 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + parachain, 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}; +use frame_system::RawOrigin; +use pallet_revive::{ + test_utils::{self, builder::*}, + Code, +}; +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; + +macro_rules! assert_return_code { + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); + }}; +} + +fn bare_call(dest: 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) -> Contract { + let (wasm, _) = compile_module(name).unwrap(); + + // Instantiate contract. + let contract = ParaA::execute_with(|| { + BareInstantiateBuilder::::bare_instantiate( + RawOrigin::Signed(ALICE).into(), + Code::Upload(wasm), + ) + .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.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.account_id.clone()); + relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE); + }); + + contract +} + +#[test] +fn test_xcm_execute() { + MockNet::reset(); + + let Contract { addr, account_id } = instantiate_test_contract("xcm_execute"); + + // Execute XCM instructions through the contract. + ParaA::execute_with(|| { + let amount: u128 = 10 * CENTS; + let assets: Asset = (Here, amount).into(); + let beneficiary = AccountId32 { network: None, id: BOB.clone().into() }; + + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm::builder_unsafe() + .withdraw_asset(assets.clone()) + .deposit_asset(assets, beneficiary) + .build(); + + let result = bare_call(addr).data(VersionedXcm::V4(message).encode()).build(); + + assert_eq!(result.gas_consumed, result.gas_required); + assert_return_code!(&result.result.unwrap(), ReturnErrorCode::Success); + + // Check if the funds are subtracted from the account of Alice and added to the account of + // Bob. + let initial = INITIAL_BALANCE; + assert_eq!(ParachainBalances::free_balance(BOB), initial + amount); + assert_eq!(ParachainBalances::free_balance(&account_id), initial - amount); + }); +} + +#[test] +fn test_xcm_execute_incomplete() { + MockNet::reset(); + + let Contract { addr, account_id } = instantiate_test_contract("xcm_execute"); + let amount = 10 * CENTS; + + // Execute XCM instructions through the contract. + ParaA::execute_with(|| { + let assets: Asset = (Here, amount).into(); + let beneficiary = AccountId32 { network: None, id: BOB.clone().into() }; + + // The XCM used to transfer funds to Bob. + let message: Xcm<()> = Xcm::builder_unsafe() + .withdraw_asset(assets.clone()) + // This will fail as the contract does not have enough balance to complete both + // withdrawals. + .withdraw_asset((Here, INITIAL_BALANCE)) + .buy_execution(assets.clone(), Unlimited) + .deposit_asset(assets, beneficiary) + .build(); + + let result = bare_call(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(&account_id), INITIAL_BALANCE - amount); + }); +} + +#[test] +fn test_xcm_execute_reentrant_call() { + MockNet::reset(); + + let Contract { addr, .. } = instantiate_test_contract("xcm_execute"); + + ParaA::execute_with(|| { + let transact_call = parachain::RuntimeCall::Contracts(pallet_revive::Call::call { + dest: addr, + gas_limit: 1_000_000.into(), + storage_deposit_limit: test_utils::deposit_limit::(), + data: vec![], + value: 0u128, + }); + + // The XCM used to transfer funds to Bob. + let message: Xcm = Xcm::builder_unsafe() + .transact(OriginKind::Native, 1_000_000_000, transact_call.encode()) + .expect_transact_status(MaybeErrorCode::Success) + .build(); + + let result = bare_call(addr) + .data(VersionedXcm::V4(message).encode()) + .build_and_unwrap_result(); + + assert_return_code!(&result, ReturnErrorCode::XcmExecutionFailed); + + // Funds should not change hands as the XCM transact failed. + assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); + }); +} + +#[test] +fn test_xcm_send() { + MockNet::reset(); + let Contract { addr, 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` + + // Send XCM instructions through the contract, to transfer some funds from the contract + // derivative account to Alice on the relay chain. + ParaA::execute_with(|| { + let dest = VersionedLocation::V4(Parent.into()); + let assets: Asset = (Here, amount).into(); + let beneficiary = AccountId32 { network: None, id: ALICE.clone().into() }; + + let message: Xcm<()> = Xcm::builder() + .withdraw_asset(assets.clone()) + .buy_execution((Here, fee), Unlimited) + .deposit_asset(assets, beneficiary) + .build(); + + let result = bare_call(addr) + .data((dest, VersionedXcm::V4(message)).encode()) + .build_and_unwrap_result(); + + let mut data = &result.data[..]; + XcmHash::decode(&mut data).expect("Failed to decode xcm_send message_id"); + }); + + Relay::execute_with(|| { + let derived_contract_addr = ¶chain_account_sovereign_account_id(1, account_id); + assert_eq!( + INITIAL_BALANCE - amount, + relay_chain::Balances::free_balance(derived_contract_addr) + ); + assert_eq!(INITIAL_BALANCE + amount - fee, relay_chain::Balances::free_balance(ALICE)); + }); +} diff --git a/substrate/frame/revive/proc-macro/Cargo.toml b/substrate/frame/revive/proc-macro/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..7b47d6053504c1a98b9bfda67c367137997059fc --- /dev/null +++ b/substrate/frame/revive/proc-macro/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pallet-revive-proc-macro" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "Procedural macros used in pallet_revive" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { workspace = true } +quote = { workspace = true } +syn = { features = ["full"], workspace = true } diff --git a/substrate/frame/revive/proc-macro/src/lib.rs b/substrate/frame/revive/proc-macro/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..95f4110a2d76d903785b261caade68661397e186 --- /dev/null +++ b/substrate/frame/revive/proc-macro/src/lib.rs @@ -0,0 +1,616 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Procedural macros used in the contracts module. +//! +//! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides +//! boilerplate of defining external environment for a wasm module. + +use proc_macro::TokenStream; +use proc_macro2::{Literal, Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, FnArg, Ident}; + +/// Defines a host functions set that can be imported by contract wasm code. +/// +/// **NB**: Be advised that all functions defined by this macro +/// will panic if called with unexpected arguments. +/// +/// It's up to you as the user of this macro to check signatures of wasm code to be executed +/// and reject the code if any imported function has a mismatched signature. +/// +/// ## Example +/// +/// ```nocompile +/// #[define_env] +/// pub mod some_env { +/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// } +/// ``` +/// This example will expand to the `foo()` defined in the wasm module named `seal0`. This is +/// because the module `seal0` is the default when no module is specified. +/// +/// To define a host function in `seal2` and `seal3` modules, it should be annotated with the +/// appropriate attribute as follows: +/// +/// ## Example +/// +/// ```nocompile +/// #[define_env] +/// pub mod some_env { +/// #[version(2)] +/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// +/// #[version(3)] +/// #[unstable] +/// fn bar(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// } +/// ``` +/// The function `bar` is additionally annotated with `unstable` which removes it from the stable +/// interface. Check out the README to learn about unstable functions. +/// +/// +/// In this example, the following host functions will be generated by the macro: +/// - `foo()` in module `seal1`, +/// - `seal_foo()` in module `seal1`, +/// - `bar()` in module `seal42`. +/// +/// Only following return types are allowed for the host functions defined with the macro: +/// - `Result<(), TrapReason>`, +/// - `Result`, +/// - `Result`. +/// +/// The macro expands to `pub struct Env` declaration, with the following traits implementations: +/// - `pallet_revive::wasm::Environment> where E: Ext` +/// - `pallet_revive::wasm::Environment<()>` +/// +/// The implementation on `()` can be used in places where no `Ext` exists, yet. This is useful +/// when only checking whether a code can be instantiated without actually executing any code. +/// +/// +/// To build up these docs, run: +/// +/// ```nocompile +/// cargo doc +/// ``` +#[proc_macro_attribute] +pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { + if !attr.is_empty() { + let msg = r#"Invalid `define_env` attribute macro: expected no attributes: + - `#[define_env]`"#; + let span = TokenStream2::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + + match EnvDef::try_from(item) { + Ok(mut def) => expand_env(&mut def).into(), + Err(e) => e.to_compile_error().into(), + } +} + +/// Parsed environment definition. +struct EnvDef { + host_funcs: Vec, +} + +/// Parsed host function definition. +struct HostFn { + item: syn::ItemFn, + api_version: Option, + name: String, + returns: HostFnReturn, + cfg: Option, +} + +enum HostFnReturn { + Unit, + U32, + ReturnCode, +} + +impl HostFnReturn { + fn map_output(&self) -> TokenStream2 { + match self { + Self::Unit => quote! { |_| None }, + Self::U32 => quote! { |ret_val| Some(ret_val) }, + Self::ReturnCode => quote! { |ret_code| Some(ret_code.into()) }, + } + } + + fn success_type(&self) -> syn::ReturnType { + match self { + Self::Unit => syn::ReturnType::Default, + Self::U32 => parse_quote! { -> u32 }, + Self::ReturnCode => parse_quote! { -> ReturnErrorCode }, + } + } +} + +impl EnvDef { + pub fn try_from(item: syn::ItemMod) -> syn::Result { + let span = item.span(); + let err = |msg| syn::Error::new(span, msg); + let items = &item + .content + .as_ref() + .ok_or(err("Invalid environment definition, expected `mod` to be inlined."))? + .1; + + let extract_fn = |i: &syn::Item| match i { + syn::Item::Fn(i_fn) => Some(i_fn.clone()), + _ => None, + }; + + let host_funcs = items + .iter() + .filter_map(extract_fn) + .map(HostFn::try_from) + .collect::, _>>()?; + + Ok(Self { host_funcs }) + } +} + +impl HostFn { + pub fn try_from(mut item: syn::ItemFn) -> syn::Result { + let err = |span, msg| { + let msg = format!("Invalid host function definition.\n{}", msg); + syn::Error::new(span, msg) + }; + + // process attributes + let msg = "Only #[api_version()], #[cfg] and #[mutating] attributes are allowed."; + let span = item.span(); + let mut attrs = item.attrs.clone(); + attrs.retain(|a| !a.path().is_ident("doc")); + let mut api_version = None; + let mut mutating = false; + let mut cfg = None; + while let Some(attr) = attrs.pop() { + let ident = attr.path().get_ident().ok_or(err(span, msg))?.to_string(); + match ident.as_str() { + "api_version" => { + if api_version.is_some() { + return Err(err(span, "#[api_version] can only be specified once")) + } + api_version = + Some(attr.parse_args::().and_then(|lit| lit.base10_parse())?); + }, + "mutating" => { + if mutating { + return Err(err(span, "#[mutating] can only be specified once")) + } + mutating = true; + }, + "cfg" => { + if cfg.is_some() { + return Err(err(span, "#[cfg] can only be specified once")) + } + cfg = Some(attr); + }, + id => return Err(err(span, &format!("Unsupported attribute \"{id}\". {msg}"))), + } + } + + if mutating { + let stmt = syn::parse_quote! { + if self.ext().is_read_only() { + return Err(Error::::StateChangeDenied.into()); + } + }; + item.block.stmts.insert(0, stmt); + } + + let name = item.sig.ident.to_string(); + + let msg = "Every function must start with these two parameters: &mut self, memory: &mut M"; + let special_args = item + .sig + .inputs + .iter() + .take(2) + .enumerate() + .map(|(i, arg)| is_valid_special_arg(i, arg)) + .fold(0u32, |acc, valid| if valid { acc + 1 } else { acc }); + + if special_args != 2 { + return Err(err(span, msg)) + } + + // process return type + let msg = r#"Should return one of the following: + - Result<(), TrapReason>, + - Result, + - Result"#; + let ret_ty = match item.clone().sig.output { + syn::ReturnType::Type(_, ty) => Ok(ty.clone()), + _ => Err(err(span, &msg)), + }?; + match *ret_ty { + syn::Type::Path(tp) => { + let result = &tp.path.segments.last().ok_or(err(span, &msg))?; + let (id, span) = (result.ident.to_string(), result.ident.span()); + id.eq(&"Result".to_string()).then_some(()).ok_or(err(span, &msg))?; + + match &result.arguments { + syn::PathArguments::AngleBracketed(group) => { + if group.args.len() != 2 { + return Err(err(span, &msg)) + }; + + let arg2 = group.args.last().ok_or(err(span, &msg))?; + + let err_ty = match arg2 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err(arg2.span(), &msg)), + }?; + + match err_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err(arg2.span(), &msg))? + .ident + .to_string()), + _ => Err(err(tp.span(), &msg)), + }? + .eq("TrapReason") + .then_some(()) + .ok_or(err(span, &msg))?; + + let arg1 = group.args.first().ok_or(err(span, &msg))?; + let ok_ty = match arg1 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err(arg1.span(), &msg)), + }?; + let ok_ty_str = match ok_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err(arg1.span(), &msg))? + .ident + .to_string()), + syn::Type::Tuple(tt) => { + if !tt.elems.is_empty() { + return Err(err(arg1.span(), &msg)) + }; + Ok("()".to_string()) + }, + _ => Err(err(ok_ty.span(), &msg)), + }?; + let returns = match ok_ty_str.as_str() { + "()" => Ok(HostFnReturn::Unit), + "u32" => Ok(HostFnReturn::U32), + "ReturnErrorCode" => Ok(HostFnReturn::ReturnCode), + _ => Err(err(arg1.span(), &msg)), + }?; + + Ok(Self { item, api_version, name, returns, cfg }) + }, + _ => Err(err(span, &msg)), + } + }, + _ => Err(err(span, &msg)), + } + } +} + +fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { + match (idx, arg) { + (0, FnArg::Receiver(rec)) => rec.reference.is_some() && rec.mutability.is_some(), + (1, FnArg::Typed(pat)) => { + let ident = + if let syn::Pat::Ident(ref ident) = *pat.pat { &ident.ident } else { return false }; + if !(ident == "memory" || ident == "_memory") { + return false + } + matches!(*pat.ty, syn::Type::Reference(_)) + }, + _ => false, + } +} + +fn arg_decoder<'a, P, I>(param_names: P, param_types: I) -> TokenStream2 +where + P: Iterator> + Clone, + I: Iterator> + Clone, +{ + const ALLOWED_REGISTERS: u32 = 6; + let mut registers_used = 0; + let mut bindings = vec![]; + for (idx, (name, ty)) in param_names.clone().zip(param_types.clone()).enumerate() { + let syn::Type::Path(path) = &**ty else { + panic!("Type needs to be path"); + }; + let Some(ident) = path.path.get_ident() else { + panic!("Type needs to be ident"); + }; + let size = + if ident == "i8" || + ident == "i16" || ident == "i32" || + ident == "u8" || ident == "u16" || + ident == "u32" + { + 1 + } else if ident == "i64" || ident == "u64" { + 2 + } else { + panic!("Pass by value only supports primitives"); + }; + registers_used += size; + if registers_used > ALLOWED_REGISTERS { + return quote! { + let (#( #param_names, )*): (#( #param_types, )*) = memory.read_as(__a0__)?; + } + } + let this_reg = quote::format_ident!("__a{}__", idx); + let next_reg = quote::format_ident!("__a{}__", idx + 1); + let binding = if size == 1 { + quote! { + let #name = #this_reg as #ty; + } + } else { + quote! { + let #name = (#this_reg as #ty) | ((#next_reg as #ty) << 32); + } + }; + bindings.push(binding); + } + quote! { + #( #bindings )* + } +} + +/// Expands environment definition. +/// Should generate source code for: +/// - implementations of the host functions to be added to the wasm runtime environment (see +/// `expand_impls()`). +fn expand_env(def: &EnvDef) -> TokenStream2 { + let impls = expand_functions(def); + let bench_impls = expand_bench_functions(def); + let docs = expand_func_doc(def); + let highest_api_version = + def.host_funcs.iter().filter_map(|f| f.api_version).max().unwrap_or_default(); + + quote! { + #[cfg(test)] + pub const HIGHEST_API_VERSION: u16 = #highest_api_version; + + impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { + fn handle_ecall( + &mut self, + memory: &mut M, + __syscall_symbol__: &[u8], + __available_api_version__: ApiVersion, + ) -> Result, TrapReason> + { + #impls + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { + #bench_impls + } + + /// Documentation of the syscalls (host functions) available to contracts. + /// + /// Each of the functions in this trait represent a function that is callable + /// by the contract. Guests use the function name as the import symbol. + /// + /// # Note + /// + /// This module is not meant to be used by any code. Rather, it is meant to be + /// consumed by humans through rustdoc. + #[cfg(doc)] + pub trait SyscallDoc { + #docs + } + } +} + +fn expand_functions(def: &EnvDef) -> TokenStream2 { + let impls = def.host_funcs.iter().map(|f| { + // skip the self and memory argument + let params = f.item.sig.inputs.iter().skip(2); + let param_names = params.clone().filter_map(|arg| { + let FnArg::Typed(arg) = arg else { + return None; + }; + Some(&arg.pat) + }); + let param_types = params.clone().filter_map(|arg| { + let FnArg::Typed(arg) = arg else { + return None; + }; + Some(&arg.ty) + }); + let arg_decoder = arg_decoder(param_names, param_types); + let cfg = &f.cfg; + let name = &f.name; + let syscall_symbol = Literal::byte_string(name.as_bytes()); + let body = &f.item.block; + let map_output = f.returns.map_output(); + let output = &f.item.sig.output; + let api_version = match f.api_version { + Some(version) => quote! { Some(#version) }, + None => quote! { None }, + }; + + // wrapped host function body call with host function traces + // see https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/contracts#host-function-tracing + let wrapped_body_with_trace = { + let trace_fmt_args = params.clone().filter_map(|arg| match arg { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(p) => match *p.pat.clone() { + syn::Pat::Ident(ref pat_ident) => Some(pat_ident.ident.clone()), + _ => None, + }, + }); + + let params_fmt_str = trace_fmt_args + .clone() + .map(|s| format!("{s}: {{:?}}")) + .collect::>() + .join(", "); + let trace_fmt_str = format!("{}({}) = {{:?}}\n", name, params_fmt_str); + + quote! { + // wrap body in closure to make sure the tracing is always executed + let result = (|| #body)(); + if ::log::log_enabled!(target: "runtime::revive::strace", ::log::Level::Trace) { + use core::fmt::Write; + let mut w = sp_std::Writer::default(); + let _ = core::write!(&mut w, #trace_fmt_str, #( #trace_fmt_args, )* result); + let msg = core::str::from_utf8(&w.inner()).unwrap_or_default(); + self.ext().append_debug_buffer(msg); + } + result + } + }; + + quote! { + #cfg + #syscall_symbol if __is_available__(#api_version) => { + // closure is needed so that "?" can infere the correct type + (|| #output { + #arg_decoder + #wrapped_body_with_trace + })().map(#map_output) + }, + } + }); + + quote! { + // Write gas from polkavm into pallet-revive before entering the host function. + let __gas_left_before__ = self + .ext + .gas_meter_mut() + .sync_from_executor(memory.gas()) + .map_err(TrapReason::from)?; + + // This is the overhead to call an empty syscall that always needs to be charged. + self.charge_gas(crate::wasm::RuntimeCosts::HostFn).map_err(TrapReason::from)?; + + // Not all APIs are available depending on configuration or when the code was deployed. + // This closure will be used by syscall specific code to perform this check. + let __is_available__ = |syscall_version: Option| { + match __available_api_version__ { + ApiVersion::UnsafeNewest => true, + ApiVersion::Versioned(max_available_version) => + syscall_version + .map(|required_version| max_available_version >= required_version) + .unwrap_or(false), + } + }; + + // They will be mapped to variable names by the syscall specific code. + let (__a0__, __a1__, __a2__, __a3__, __a4__, __a5__) = memory.read_input_regs(); + + // Execute the syscall specific logic in a closure so that the gas metering code is always executed. + let result = (|| match __syscall_symbol__ { + #( #impls )* + _ => Err(TrapReason::SupervisorError(Error::::InvalidSyscall.into())) + })(); + + // Write gas from pallet-revive into polkavm after leaving the host function. + let gas = self.ext.gas_meter_mut().sync_to_executor(__gas_left_before__).map_err(TrapReason::from)?; + memory.set_gas(gas.into()); + result + } +} + +fn expand_bench_functions(def: &EnvDef) -> TokenStream2 { + let impls = def.host_funcs.iter().map(|f| { + // skip the context and memory argument + let params = f.item.sig.inputs.iter().skip(2); + let cfg = &f.cfg; + let name = &f.name; + let body = &f.item.block; + let output = &f.item.sig.output; + + let name = Ident::new(&format!("bench_{name}"), Span::call_site()); + quote! { + #cfg + pub fn #name(&mut self, memory: &mut M, #(#params),*) #output { + #body + } + } + }); + + quote! { + #( #impls )* + } +} + +fn expand_func_doc(def: &EnvDef) -> TokenStream2 { + let docs = def.host_funcs.iter().map(|func| { + // Remove auxiliary args: `ctx: _` and `memory: _` + let func_decl = { + let mut sig = func.item.sig.clone(); + sig.inputs = sig + .inputs + .iter() + .skip(2) + .map(|p| p.clone()) + .collect::>(); + sig.output = func.returns.success_type(); + sig.to_token_stream() + }; + let func_doc = { + let func_docs = { + let docs = func.item.attrs.iter().filter(|a| a.path().is_ident("doc")).map(|d| { + let docs = d.to_token_stream(); + quote! { #docs } + }); + quote! { #( #docs )* } + }; + let availability = if let Some(version) = func.api_version { + let info = format!( + "\n# Required API version\nThis API was added in version **{version}**.", + ); + quote! { #[doc = #info] } + } else { + let info = + "\n# Unstable API\nThis API is not standardized and only available for testing."; + quote! { #[doc = #info] } + }; + quote! { + #func_docs + #availability + } + }; + quote! { + #func_doc + #func_decl; + } + }); + + quote! { + #( #docs )* + } +} diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs new file mode 100644 index 0000000000000000000000000000000000000000..c51940ba771e6ba81cb8fadb27a23a4eed655bd7 --- /dev/null +++ b/substrate/frame/revive/src/address.rs @@ -0,0 +1,128 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Functions that deal contract addresses. + +use alloc::vec::Vec; +use sp_core::H160; +use sp_io::hashing::keccak_256; +use sp_runtime::AccountId32; + +/// Map between the native chain account id `T` and an Ethereum [`H160`]. +/// +/// 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. +/// +/// 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. + /// + /// 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; +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..8a859a3a5089933c4aa8c7420a54c315f106babc --- /dev/null +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -0,0 +1,222 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use 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, MomentOf, Origin, WasmBlob, Weight, +}; +use alloc::{vec, vec::Vec}; +use frame_benchmarking::benchmarking; +use sp_core::U256; + +type StackExt<'a, T> = Stack<'a, T, WasmBlob>; + +/// A builder used to prepare a contract call. +pub struct CallSetup { + contract: Contract, + dest: T::AccountId, + origin: Origin, + gas_meter: GasMeter, + storage_meter: Meter, + value: BalanceOf, + debug_message: Option, + data: Vec, + transient_storage_size: u32, +} + +impl Default for CallSetup +where + T: Config + pallet_balances::Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, +{ + fn default() -> Self { + Self::new(WasmModule::dummy()) + } +} + +impl CallSetup +where + T: Config + pallet_balances::Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, +{ + /// Setup a new call for the given module. + pub fn new(module: WasmModule) -> Self { + let contract = Contract::::new(module.clone(), vec![]).unwrap(); + let dest = contract.account_id.clone(); + let origin = Origin::from_account_id(contract.caller.clone()); + + let storage_meter = Meter::new(&origin, default_deposit_limit::(), 0u32.into()).unwrap(); + + // Whitelist contract account, as it is already accounted for in the call benchmark + benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&contract.account_id).into(), + ); + + // Whitelist the contract's contractInfo as it is already accounted for in the call + // benchmark + benchmarking::add_to_whitelist( + crate::ContractInfoOf::::hashed_key_for(&T::AddressMapper::to_address( + &contract.account_id, + )) + .into(), + ); + + Self { + contract, + dest, + origin, + gas_meter: GasMeter::new(Weight::MAX), + storage_meter, + value: 0u32.into(), + debug_message: None, + data: vec![], + transient_storage_size: 0, + } + } + + /// Set the meter's storage deposit limit. + pub fn set_storage_deposit_limit(&mut self, balance: BalanceOf) { + self.storage_meter = Meter::new(&self.origin, balance, 0u32.into()).unwrap(); + } + + /// Set the call's origin. + pub fn set_origin(&mut self, origin: Origin) { + self.origin = origin; + } + + /// Set the contract's balance. + pub fn set_balance(&mut self, value: BalanceOf) { + self.contract.set_balance(value); + } + + /// Set the call's input data. + pub fn set_data(&mut self, value: Vec) { + self.data = value; + } + + /// Set the transient storage size. + pub fn set_transient_storage_size(&mut self, size: u32) { + self.transient_storage_size = size; + } + + /// Set the debug message. + pub fn enable_debug_message(&mut self) { + self.debug_message = Some(Default::default()); + } + + /// Get the debug message. + pub fn debug_message(&self) -> Option { + self.debug_message.clone() + } + + /// Get the call's input data. + pub fn data(&self) -> Vec { + self.data.clone() + } + + /// Get the call's contract. + pub fn contract(&self) -> Contract { + self.contract.clone() + } + + /// Build the call stack. + pub fn ext(&mut self) -> (StackExt<'_, T>, WasmBlob) { + let mut ext = StackExt::bench_new_call( + T::AddressMapper::to_address(&self.dest), + self.origin.clone(), + &mut self.gas_meter, + &mut self.storage_meter, + self.value, + self.debug_message.as_mut(), + ); + if self.transient_storage_size > 0 { + Self::with_transient_storage(&mut ext.0, self.transient_storage_size).unwrap(); + } + ext + } + + /// Prepare a call to the module. + pub fn prepare_call<'a>( + ext: &'a mut StackExt<'a, T>, + module: WasmBlob, + input: Vec, + ) -> PreparedCall<'a, StackExt<'a, T>> { + module + .prepare_call( + Runtime::new(ext, input), + ExportedFunction::Call, + ApiVersion::UnsafeNewest, + ) + .unwrap() + } + + /// Add transient_storage + fn with_transient_storage(ext: &mut StackExt, size: u32) -> Result<(), &'static str> { + let &MeterEntry { amount, limit } = ext.transient_storage().meter().current(); + ext.transient_storage().meter().current_mut().limit = size; + for i in 1u32.. { + let mut key_data = i.to_le_bytes().to_vec(); + while key_data.last() == Some(&0) { + key_data.pop(); + } + let key = Key::try_from_var(key_data).unwrap(); + if let Err(e) = ext.set_transient_storage(&key, Some(Vec::new()), false) { + // Restore previous settings. + ext.transient_storage().meter().current_mut().limit = limit; + ext.transient_storage().meter().current_mut().amount = amount; + if e == Error::::OutOfTransientStorage.into() { + break; + } else { + return Err("Initialization of the transient storage failed"); + } + } + } + Ok(()) + } +} + +#[macro_export] +macro_rules! memory( + ($($bytes:expr,)*) => {{ + vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::>() + }}; +); + +#[macro_export] +macro_rules! build_runtime( + ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => { + $crate::build_runtime!($runtime, _contract, $memory: [$($segment,)*]); + }; + ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => { + $crate::build_runtime!($runtime, $contract); + let mut $memory = $crate::memory!($($bytes,)*); + }; + ($runtime:ident, $contract:ident) => { + let mut setup = CallSetup::::default(); + let $contract = setup.contract(); + let input = setup.data(); + let (mut ext, _) = setup.ext(); + let mut $runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); + }; +); diff --git a/substrate/frame/revive/src/benchmarking/code.rs b/substrate/frame/revive/src/benchmarking/code.rs new file mode 100644 index 0000000000000000000000000000000000000000..ede3bb69b11bbcf1e631384fdaec6e02a3e5561f --- /dev/null +++ b/substrate/frame/revive/src/benchmarking/code.rs @@ -0,0 +1,69 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Functions to procedurally construct contract code used for benchmarking. +//! +//! In order to be able to benchmark events that are triggered by contract execution +//! (API calls into seal, individual instructions), we need to generate contracts that +//! perform those events. Because those contracts can get very big we cannot simply define +//! them as text (.wat) as this will be too slow and consume too much memory. Therefore +//! we define this simple definition of a contract that can be passed to `create_code` that +//! compiles it down into a `WasmModule` that can be used as a contract's code. + +use alloc::vec::Vec; +use pallet_revive_fixtures::bench as bench_fixtures; +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 code: Vec, + pub hash: H256, +} + +impl WasmModule { + /// Return a contract code that does nothing. + pub fn dummy() -> Self { + Self::new(bench_fixtures::DUMMY.to_vec()) + } + + /// Same as [`Self::dummy`] but uses `replace_with` to make the code unique. + pub fn dummy_unique(replace_with: u32) -> Self { + Self::new(bench_fixtures::dummy_unique(replace_with)) + } + + /// A contract code of specified sizte that does nothing. + pub fn sized(_size: u32) -> Self { + Self::dummy() + } + + /// A contract code that calls the "noop" host function in a loop depending in the input. + pub fn noop() -> Self { + Self::new(bench_fixtures::NOOP.to_vec()) + } + + /// A contract code that executes some ALU instructions in a loop. + pub fn instr() -> Self { + Self::new(bench_fixtures::INSTR.to_vec()) + } + + fn new(code: Vec) -> Self { + let hash = 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 new file mode 100644 index 0000000000000000000000000000000000000000..332c425d714ed13a96a61c9ee1d50d305522e52a --- /dev/null +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -0,0 +1,1746 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarks for the contracts pallet + +#![cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] + +mod call_builder; +mod code; +use self::{call_builder::CallSetup, code::WasmModule}; +use crate::{ + exec::{Key, MomentOf}, + limits, + storage::WriteOutcome, + Pallet as Contracts, *, +}; +use alloc::{vec, vec::Vec}; +use codec::{Encode, MaxEncodedLen}; +use frame_benchmarking::v2::*; +use frame_support::{ + self, assert_ok, + storage::child, + traits::{fungible::InspectHold, Currency}, + weights::{Weight, WeightMeter}, +}; +use frame_system::RawOrigin; +use pallet_balances; +use pallet_revive_uapi::{CallFlags, ReturnErrorCode, StorageFlags}; +use sp_runtime::traits::{Bounded, Hash}; + +/// How many runs we do per API benchmark. +/// +/// This is picked more or less arbitrary. We experimented with different numbers until +/// the results appeared to be stable. Reducing the number would speed up the benchmarks +/// but might make the results less precise. +const API_BENCHMARK_RUNS: u32 = 1600; + +/// How many runs we do per instruction benchmark. +/// +/// Same rationale as for [`API_BENCHMARK_RUNS`]. The number is bigger because instruction +/// benchmarks are faster. +const INSTR_BENCHMARK_RUNS: u32 = 5000; + +/// Number of layers in a Radix16 unbalanced trie. +const UNBALANCED_TRIE_LAYERS: u32 = 20; + +/// An instantiated and deployed contract. +#[derive(Clone)] +struct Contract { + caller: T::AccountId, + account_id: T::AccountId, +} + +impl Contract +where + T: Config + pallet_balances::Config, + 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> { + Self::with_index(0, module, data) + } + + /// Create new contract and use an account id derived from the supplied index as instantiator. + fn with_index( + index: u32, + module: WasmModule, + data: Vec, + ) -> Result, &'static str> { + Self::with_caller(account("instantiator", index, 0), module, data) + } + + /// Create new contract and use the supplied `caller` as instantiator. + fn with_caller( + caller: T::AccountId, + module: WasmModule, + data: Vec, + ) -> Result, &'static str> { + T::Currency::set_balance(&caller, caller_funding::()); + let salt = Some([0xffu8; 32]); + + let outcome = Contracts::::bare_instantiate( + RawOrigin::Signed(caller.clone()).into(), + 0u32.into(), + Weight::MAX, + default_deposit_limit::(), + Code::Upload(module.code), + data, + salt, + DebugInfo::Skip, + CollectEvents::Skip, + ); + + let 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(&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 { + let contract = Contract::::new(code, vec![])?; + let storage_items = (0..stor_num) + .map(|i| { + let hash = T::Hashing::hash_of(&i) + .as_ref() + .try_into() + .map_err(|_| "Hash too big for storage key")?; + Ok((hash, vec![42u8; stor_size as usize])) + }) + .collect::, &'static str>>()?; + contract.store(&storage_items)?; + Ok(contract) + } + + /// Store the supplied storage items into this contracts storage. + fn store(&self, items: &Vec<([u8; 32], Vec)>) -> Result<(), &'static str> { + let info = self.info()?; + for item in items { + info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) + .map_err(|_| "Failed to write storage to restoration dest")?; + } + >::insert(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 { + if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { + return Err("Key size too small to create the specified trie"); + } + + let value = vec![16u8; limits::PAYLOAD_BYTES as usize]; + let contract = Contract::::new(code, vec![])?; + let info = contract.info()?; + let child_trie_info = info.child_trie_info(); + child::put_raw(&child_trie_info, &key, &value); + for l in 0..UNBALANCED_TRIE_LAYERS { + let pos = l as usize / 2; + let mut key_new = key.to_vec(); + for i in 0u8..16 { + key_new[pos] = if l % 2 == 0 { + (key_new[pos] & 0xF0) | i + } else { + (key_new[pos] & 0x0F) | (i << 4) + }; + + if key == &key_new { + continue + } + child::put_raw(&child_trie_info, &key_new, &value); + } + } + Ok(contract) + } + + /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. + fn address_info(addr: &T::AccountId) -> Result, &'static str> { + ContractInfoOf::::get(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. + fn info(&self) -> Result, &'static str> { + Self::address_info(&self.account_id) + } + + /// Set the balance of the contract to the supplied amount. + fn set_balance(&self, balance: BalanceOf) { + T::Currency::set_balance(&self.account_id, balance); + } + + /// Returns `true` iff all storage entries related to code storage exist. + fn code_exists(hash: &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: &sp_core::H256) -> bool { + !>::contains_key(hash) && !>::contains_key(&hash) + } +} + +/// The funding that each account that either calls or instantiates contracts is funded with. +fn caller_funding() -> BalanceOf { + // Minting can overflow, so we can't abuse of the funding. This value happens to be big enough, + // but not too big to make the total supply overflow. + BalanceOf::::max_value() / 10_000u32.into() +} + +/// The deposit limit we use for benchmarks. +fn default_deposit_limit() -> BalanceOf { + (T::DepositPerByte::get() * 1024u32.into() * 1024u32.into()) + + T::DepositPerItem::get() * 1024u32.into() +} + +#[benchmarks( + where + BalanceOf: Into + TryFrom, + T: Config + pallet_balances::Config, + MomentOf: Into, + ::RuntimeEvent: From>, + as Currency>::Balance: From>, +)] +mod benchmarks { + use super::*; + + // The base weight consumed on processing contracts deletion queue. + #[benchmark(pov_mode = Measured)] + fn on_process_deletion_queue_batch() { + #[block] + { + ContractInfo::::process_deletion_queue_batch(&mut WeightMeter::new()) + } + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn on_initialize_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> { + let instance = Contract::::with_storage(WasmModule::dummy(), k, limits::PAYLOAD_BYTES)?; + instance.info()?.queue_trie_for_deletion(); + + #[block] + { + ContractInfo::::process_deletion_queue_batch(&mut WeightMeter::new()) + } + + Ok(()) + } + + // This benchmarks the overhead of loading a code of size `c` byte from storage and into + // the execution engine. This does **not** include the actual execution for which the gas meter + // is responsible. This is achieved by generating all code to the `deploy` function + // which is in the wasm module but not executed on `call`. + // The results are supposed to be used as `call_with_code_per_byte(c) - + // call_with_code_per_byte(0)`. + #[benchmark(pov_mode = Measured)] + fn call_with_code_per_byte( + c: Linear<0, { T::MaxCodeLen::get() }>, + ) -> Result<(), BenchmarkError> { + let instance = + Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; + let value = Pallet::::min_balance(); + let callee = T::AddressMapper::to_address(&instance.account_id); + let storage_deposit = default_deposit_limit::(); + + #[extrinsic_call] + call( + RawOrigin::Signed(instance.caller.clone()), + callee, + value, + Weight::MAX, + storage_deposit, + vec![], + ); + + Ok(()) + } + + // `c`: Size of the code in bytes. + // `i`: Size of the input in bytes. + #[benchmark(pov_mode = Measured)] + fn instantiate_with_code( + c: Linear<0, { T::MaxCodeLen::get() }>, + i: Linear<0, { limits::MEMORY_BYTES }>, + ) { + let input = vec![42u8; i 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, .. } = WasmModule::sized(c); + let origin = RawOrigin::Signed(caller.clone()); + 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, Some(salt)); + + let deposit = + 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); + assert_eq!( + T::Currency::balance(&caller), + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + ); + // contract has the full value + assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); + } + + // `i`: Size of the input in bytes. + // `s`: Size of e salt in bytes. + #[benchmark(pov_mode = Measured)] + fn instantiate(i: Linear<0, { limits::MEMORY_BYTES }>) -> Result<(), BenchmarkError> { + let input = vec![42u8; i as usize]; + 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 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 account_id = T::AddressMapper::to_account_id_contract(&addr); + + #[extrinsic_call] + _( + RawOrigin::Signed(caller.clone()), + value, + Weight::MAX, + storage_deposit, + hash, + input, + Some(salt), + ); + + let deposit = + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); + let code_deposit = + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &account_id); + // value was removed from the caller + assert_eq!( + T::Currency::total_balance(&caller), + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + ); + // contract has the full value + assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); + + Ok(()) + } + + // We just call a dummy contract to measure the overhead of the call extrinsic. + // The size of the data has no influence on the costs of this extrinsic as long as the contract + // won't call `seal_input` in its constructor to copy the data to contract memory. + // The dummy contract used here does not do this. The costs for the data copy is billed as + // part of `seal_input`. The costs for invoking a contract of a specific size are not part + // of this benchmark because we cannot know the size of the contract when issuing a call + // transaction. See `call_with_code_per_byte` for this. + #[benchmark(pov_mode = Measured)] + fn call() -> Result<(), BenchmarkError> { + let data = vec![42u8; 1024]; + let instance = + Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; + let value = Pallet::::min_balance(); + let origin = RawOrigin::Signed(instance.caller.clone()); + let callee = T::AddressMapper::to_address(&instance.account_id); + let before = T::Currency::balance(&instance.account_id); + let storage_deposit = default_deposit_limit::(); + #[extrinsic_call] + _(origin, callee, value, Weight::MAX, storage_deposit, data); + let deposit = T::Currency::balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &instance.account_id, + ); + let code_deposit = T::Currency::balance_on_hold( + &HoldReason::CodeUploadDepositReserve.into(), + &instance.caller, + ); + // value and value transferred via call should be removed from the caller + assert_eq!( + T::Currency::balance(&instance.caller), + caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + ); + // contract should have received the value + assert_eq!(T::Currency::balance(&instance.account_id), before + value); + // contract should still exist + instance.info()?; + + Ok(()) + } + + // This constructs a contract that is maximal expensive to instrument. + // It creates a maximum number of metering blocks per byte. + // `c`: Size of the code in bytes. + #[benchmark(pov_mode = Measured)] + fn upload_code(c: Linear<0, { T::MaxCodeLen::get() }>) { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::sized(c); + let origin = RawOrigin::Signed(caller.clone()); + let storage_deposit = default_deposit_limit::(); + #[extrinsic_call] + _(origin, code, storage_deposit); + // uploading the code reserves some balance in the callers account + assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); + assert!(>::code_exists(&hash)); + } + + // Removing code does not depend on the size of the contract because all the information + // needed to verify the removal claim (refcount, owner) is stored in a separate storage + // item (`CodeInfoOf`). + #[benchmark(pov_mode = Measured)] + fn remove_code() -> Result<(), BenchmarkError> { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let WasmModule { code, hash, .. } = WasmModule::dummy(); + let origin = RawOrigin::Signed(caller.clone()); + let storage_deposit = default_deposit_limit::(); + let uploaded = + >::bare_upload_code(origin.clone().into(), code, storage_deposit)?; + assert_eq!(uploaded.code_hash, hash); + assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&caller)); + assert!(>::code_exists(&hash)); + #[extrinsic_call] + _(origin, hash); + // removing the code should have unreserved the deposit + assert_eq!(T::Currency::total_balance_on_hold(&caller), 0u32.into()); + assert!(>::code_removed(&hash)); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn set_code() -> Result<(), BenchmarkError> { + let instance = + >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; + // we just add some bytes so that the code hash is different + let WasmModule { code, .. } = 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 = T::AddressMapper::to_address(&instance.account_id); + assert_ne!(instance.info()?.code_hash, hash); + #[extrinsic_call] + _(RawOrigin::Root, callee, hash); + assert_eq!(instance.info()?.code_hash, hash); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) { + let mut setup = CallSetup::::new(WasmModule::noop()); + let (mut ext, module) = setup.ext(); + let prepared = CallSetup::::prepare_call(&mut ext, module, r.encode()); + #[block] + { + prepared.call().unwrap(); + } + } + + #[benchmark(pov_mode = Measured)] + fn seal_caller() { + let len = H160::len_bytes(); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_caller(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[..]).unwrap(), + T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap()) + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_is_contract() { + let Contract { account_id, .. } = + Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); + + build_runtime!(runtime, memory: [account_id.encode(), ]); + + let result; + #[block] + { + result = runtime.bench_is_contract(memory.as_mut_slice(), 0); + } + + assert_eq!(result.unwrap(), 1); + } + + #[benchmark(pov_mode = Measured)] + fn seal_code_hash() { + let contract = Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); + let len = ::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(), len, 0); + } + + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[..]).unwrap(), + contract.info().unwrap().code_hash + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_own_code_hash() { + 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(), 0); + } + + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[..]).unwrap(), + contract.info().unwrap().code_hash + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_caller_is_origin() { + build_runtime!(runtime, memory: []); + + let result; + #[block] + { + result = runtime.bench_caller_is_origin(memory.as_mut_slice()); + } + assert_eq!(result.unwrap(), 1u32); + } + + #[benchmark(pov_mode = Measured)] + fn seal_caller_is_root() { + let mut setup = CallSetup::::default(); + setup.set_origin(Origin::Root); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]); + + let result; + #[block] + { + result = runtime.bench_caller_is_root([0u8; 0].as_mut_slice()); + } + assert_eq!(result.unwrap(), 1u32); + } + + #[benchmark(pov_mode = Measured)] + fn seal_address() { + let len = H160::len_bytes(); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_address(memory.as_mut_slice(), 0); + } + assert_ok!(result); + assert_eq!(::decode(&mut &memory[..]).unwrap(), runtime.ext().address()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_weight_left() { + // use correct max_encoded_len when new version of parity-scale-codec is released + let len = 18u32; + assert!(::max_encoded_len() as u32 != len); + build_runtime!(runtime, memory: [32u32.to_le_bytes(), vec![0u8; len as _], ]); + + let result; + #[block] + { + result = runtime.bench_weight_left(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!( + ::decode(&mut &memory[4..]).unwrap(), + runtime.ext().gas_meter().gas_left() + ); + } + + #[benchmark(pov_mode = Measured)] + fn seal_balance() { + build_runtime!(runtime, memory: [[0u8;32], ]); + let result; + #[block] + { + result = runtime.bench_balance(memory.as_mut_slice(), 0); + } + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().balance()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_balance_of() { + let len = ::max_encoded_len(); + let account = account::("target", 0, 0); + let address = T::AddressMapper::to_address(&account); + let balance = Pallet::::min_balance() * 2u32.into(); + T::Currency::set_balance(&account, balance); + + build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]); + + let result; + #[block] + { + result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0); + } + + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..len]), runtime.ext().balance_of(&address)); + } + + #[benchmark(pov_mode = Measured)] + fn seal_value_transferred() { + build_runtime!(runtime, memory: [[0u8;32], ]); + 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() { + build_runtime!(runtime, memory: [[0u8;32], ]); + let result; + #[block] + { + result = runtime.bench_minimum_balance(memory.as_mut_slice(), 0); + } + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().minimum_balance()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_block_number() { + build_runtime!(runtime, memory: [[0u8;32], ]); + let result; + #[block] + { + result = runtime.bench_block_number(memory.as_mut_slice(), 0); + } + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_now() { + build_runtime!(runtime, memory: [[0u8;32], ]); + let result; + #[block] + { + result = runtime.bench_now(memory.as_mut_slice(), 0); + } + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_weight_to_fee() { + build_runtime!(runtime, memory: [[0u8;32], ]); + let weight = Weight::from_parts(500_000, 300_000); + let result; + #[block] + { + result = runtime.bench_weight_to_fee( + memory.as_mut_slice(), + weight.ref_time(), + weight.proof_size(), + 0, + ); + } + assert_ok!(result); + 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 }>) { + let mut setup = CallSetup::::default(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![42u8; n as usize]); + let mut memory = memory!(n.to_le_bytes(), vec![0u8; n as usize],); + let result; + #[block] + { + result = runtime.bench_input(memory.as_mut_slice(), 4, 0); + } + assert_ok!(result); + assert_eq!(&memory[4..], &vec![42u8; n as usize]); + } + + #[benchmark(pov_mode = Measured)] + fn seal_return(n: Linear<0, { limits::MEMORY_BYTES - 4 }>) { + build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n); + } + + assert!(matches!( + result, + Err(crate::wasm::TrapReason::Return(crate::wasm::ReturnData { .. })) + )); + } + + #[benchmark(pov_mode = Measured)] + fn seal_terminate( + n: Linear<0, { limits::DELEGATE_DEPENDENCIES }>, + ) -> Result<(), BenchmarkError> { + let beneficiary = account::("beneficiary", 0, 0); + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller); + let storage_deposit = default_deposit_limit::(); + + build_runtime!(runtime, memory: [beneficiary.encode(),]); + + (0..n).for_each(|i| { + let new_code = WasmModule::dummy_unique(65 + i); + Contracts::::bare_upload_code(origin.clone().into(), new_code.code, storage_deposit) + .unwrap(); + runtime.ext().lock_delegate_dependency(new_code.hash).unwrap(); + }); + + let result; + #[block] + { + result = runtime.bench_terminate(memory.as_mut_slice(), 0); + } + + assert!(matches!(result, Err(crate::wasm::TrapReason::Termination))); + + Ok(()) + } + + // Benchmark the overhead that topics generate. + // `t`: Number of topics + // `n`: Size of event payload in bytes + #[benchmark(pov_mode = Measured)] + fn seal_deposit_event( + t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>, + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) { + let 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(), + 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 + // Whereas this function is used in RPC mode only, it still should be secured + // against an excessive use. + // + // i: size of input in bytes up to maximum allowed contract memory or maximum allowed debug + // buffer size, whichever is less. + #[benchmark] + fn seal_debug_message( + i: Linear<0, { (limits::MEMORY_BYTES).min(limits::DEBUG_BUFFER_BYTES) }>, + ) { + let mut setup = CallSetup::::default(); + setup.enable_debug_message(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + // Fill memory with printable ASCII bytes. + let mut memory = (0..i).zip((32..127).cycle()).map(|i| i.1).collect::>(); + + let result; + #[block] + { + result = runtime.bench_debug_message(memory.as_mut_slice(), 0, i); + } + assert_ok!(result); + assert_eq!(setup.debug_message().unwrap().len() as u32, i); + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn get_storage_empty() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES as usize; + let value = vec![1u8; max_value_len]; + + let instance = Contract::::new(WasmModule::dummy(), vec![])?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = child::get_raw(&child_trie_info, &key); + } + + assert_eq!(result, Some(value)); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn get_storage_full() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES; + let value = vec![1u8; max_value_len as usize]; + + let instance = Contract::::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = child::get_raw(&child_trie_info, &key); + } + + assert_eq!(result, Some(value)); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn set_storage_empty() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES as usize; + let value = vec![1u8; max_value_len]; + + let instance = Contract::::new(WasmModule::dummy(), vec![])?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let val = Some(value.clone()); + let result; + #[block] + { + result = info.bench_write_raw(&key, val, true); + } + + assert_ok!(result); + assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn set_storage_full() -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = vec![0u8; max_key_len as usize]; + let max_value_len = limits::PAYLOAD_BYTES; + let value = vec![1u8; max_value_len as usize]; + + let instance = Contract::::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?; + let info = instance.info()?; + let child_trie_info = info.child_trie_info(); + info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let val = Some(value.clone()); + let result; + #[block] + { + result = info.bench_write_raw(&key, val, true); + } + + assert_ok!(result); + assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value); + Ok(()) + } + + // n: new byte size + // o: old byte size + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_set_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + o: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = vec![1u8; n as usize]; + + build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; o as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_set_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, // key_ptr + max_key_len, // key_len + max_key_len, // value_ptr + n, // value_len + ); + } + + assert_ok!(result); + assert_eq!(info.read(&key).unwrap(), value); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_clear_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; n as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_clear_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, + max_key_len, + ); + } + + assert_ok!(result); + assert!(info.read(&key).is_none()); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_get_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; n as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_get_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_contains_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), ]); + let info = instance.info()?; + + info.write(&key, Some(vec![42u8; n as usize]), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_contains_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, + max_key_len, + ); + } + + assert_eq!(result.unwrap(), n); + Ok(()) + } + + #[benchmark(skip_meta, pov_mode = Measured)] + fn seal_take_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + let info = instance.info()?; + + let value = vec![42u8; n as usize]; + info.write(&key, Some(value.clone()), None, false) + .map_err(|_| "Failed to write to storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_take_storage( + memory.as_mut_slice(), + StorageFlags::empty().bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert!(&info.read(&key).is_none()); + assert_eq!(&value, &memory[out_ptr as usize..]); + Ok(()) + } + + // We use both full and empty benchmarks here instead of benchmarking transient_storage + // (BTreeMap) directly. This approach is necessary because benchmarking this BTreeMap is very + // slow. Additionally, we use linear regression for our benchmarks, and the BTreeMap's log(n) + // complexity can introduce approximation errors. + #[benchmark(pov_mode = Ignored)] + fn set_transient_storage_empty() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = Some(vec![42u8; max_value_len as _]); + let mut setup = CallSetup::::default(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + let result; + #[block] + { + result = runtime.ext().set_transient_storage(&key, value, false); + } + + assert_eq!(result, Ok(WriteOutcome::New)); + assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + #[benchmark(pov_mode = Ignored)] + fn set_transient_storage_full() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = Some(vec![42u8; max_value_len as _]); + let mut setup = CallSetup::::default(); + setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + let result; + #[block] + { + result = runtime.ext().set_transient_storage(&key, value, false); + } + + assert_eq!(result, Ok(WriteOutcome::New)); + assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + #[benchmark(pov_mode = Ignored)] + fn get_transient_storage_empty() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + + let mut setup = CallSetup::::default(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + let result; + #[block] + { + result = runtime.ext().get_transient_storage(&key); + } + + assert_eq!(result, Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + #[benchmark(pov_mode = Ignored)] + fn get_transient_storage_full() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + + let mut setup = CallSetup::::default(); + setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + let result; + #[block] + { + result = runtime.ext().get_transient_storage(&key); + } + + assert_eq!(result, Some(vec![42u8; max_value_len as _])); + Ok(()) + } + + // The weight of journal rollbacks should be taken into account when setting storage. + #[benchmark(pov_mode = Ignored)] + fn rollback_transient_storage() -> Result<(), BenchmarkError> { + let max_value_len = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + + let mut setup = CallSetup::::default(); + setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime.ext().transient_storage().start_transaction(); + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + #[block] + { + runtime.ext().transient_storage().rollback_transaction(); + } + + assert_eq!(runtime.ext().get_transient_storage(&key), None); + Ok(()) + } + + // n: new byte size + // o: old byte size + #[benchmark(pov_mode = Measured)] + fn seal_set_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + o: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + let value = vec![1u8; n as usize]; + build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; o as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_set_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, // key_ptr + max_key_len, // key_len + max_key_len, // value_ptr + n, // value_len + ); + } + + assert_ok!(result); + assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_clear_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; n as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_clear_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, + max_key_len, + ); + } + + assert_ok!(result); + assert!(runtime.ext().get_transient_storage(&key).is_none()); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_get_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; n as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_get_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert_eq!( + &runtime.ext().get_transient_storage(&key).unwrap(), + &memory[out_ptr as usize..] + ); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_contains_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + runtime + .ext() + .set_transient_storage(&key, Some(vec![42u8; n as usize]), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let result; + #[block] + { + result = runtime.bench_contains_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, + max_key_len, + ); + } + + assert_eq!(result.unwrap(), n); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn seal_take_transient_storage( + n: Linear<0, { limits::PAYLOAD_BYTES }>, + ) -> Result<(), BenchmarkError> { + let n = limits::PAYLOAD_BYTES; + let max_key_len = limits::STORAGE_KEY_BYTES; + let key = Key::try_from_var(vec![0u8; max_key_len as usize]) + .map_err(|_| "Key has wrong length")?; + build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]); + runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX; + let value = vec![42u8; n as usize]; + runtime + .ext() + .set_transient_storage(&key, Some(value.clone()), false) + .map_err(|_| "Failed to write to transient storage during setup.")?; + + let out_ptr = max_key_len + 4; + let result; + #[block] + { + result = runtime.bench_take_storage( + memory.as_mut_slice(), + StorageFlags::TRANSIENT.bits(), + 0, // key_ptr + max_key_len, // key_len + out_ptr, // out_ptr + max_key_len, // out_len_ptr + ); + } + + assert_ok!(result); + assert!(&runtime.ext().get_transient_storage(&key).is_none()); + assert_eq!(&value, &memory[out_ptr as usize..]); + Ok(()) + } + + // We transfer to unique accounts. + #[benchmark(pov_mode = Measured)] + fn seal_transfer() { + let account = account::("receiver", 0, 0); + let value = Pallet::::min_balance(); + assert!(value > 0u32.into()); + + let mut setup = CallSetup::::default(); + setup.set_balance(value); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + + let account_bytes = account.encode(); + let account_len = account_bytes.len() as u32; + let value_bytes = Into::::into(value).encode(); + let mut memory = memory!(account_bytes, value_bytes,); + + let result; + #[block] + { + result = runtime.bench_transfer( + memory.as_mut_slice(), + 0, // account_ptr + account_len, // value_ptr + ); + } + + assert_ok!(result); + } + + // t: with or without some value to transfer + // i: size of the input data + #[benchmark(pov_mode = Measured)] + fn seal_call(t: Linear<0, 1>, i: Linear<0, { limits::MEMORY_BYTES }>) { + let Contract { account_id: callee, .. } = + Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); + let callee_bytes = callee.encode(); + let callee_len = callee_bytes.len() as u32; + + let value: BalanceOf = t.into(); + let value_bytes = Into::::into(value).encode(); + + let deposit: BalanceOf = (u32::MAX - 100).into(); + let deposit_bytes = Into::::into(deposit).encode(); + let deposit_len = deposit_bytes.len() as u32; + + let mut setup = CallSetup::::default(); + setup.set_storage_deposit_limit(deposit); + setup.set_data(vec![42; i as usize]); + setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); + + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,); + + let result; + #[block] + { + result = runtime.bench_call( + memory.as_mut_slice(), + CallFlags::CLONE_INPUT.bits(), // flags + 0, // callee_ptr + 0, // ref_time_limit + 0, // proof_size_limit + callee_len, // deposit_ptr + callee_len + deposit_len, // value_ptr + 0, // input_data_ptr + 0, // input_data_len + SENTINEL, // output_ptr + 0, // output_len_ptr + ); + } + + assert_ok!(result); + } + + #[benchmark(pov_mode = Measured)] + fn seal_delegate_call() -> Result<(), BenchmarkError> { + let hash = Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; + + let mut setup = CallSetup::::default(); + setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); + + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + let mut memory = memory!(hash.encode(),); + + let result; + #[block] + { + result = runtime.bench_delegate_call( + memory.as_mut_slice(), + 0, // flags + 0, // code_hash_ptr + 0, // input_data_ptr + 0, // input_data_len + SENTINEL, // output_ptr + 0, + ); + } + + assert_ok!(result); + Ok(()) + } + + // t: value to transfer + // i: size of input in bytes + #[benchmark(pov_mode = Measured)] + fn seal_instantiate(i: Linear<0, { limits::MEMORY_BYTES }>) -> Result<(), BenchmarkError> { + let code = WasmModule::dummy(); + let hash = Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; + let hash_bytes = hash.encode(); + let hash_len = hash_bytes.len() as u32; + + let value: BalanceOf = 1u32.into(); + let value_bytes = Into::::into(value).encode(); + let value_len = value_bytes.len() as u32; + + let deposit: BalanceOf = 0u32.into(); + let deposit_bytes = Into::::into(deposit).encode(); + let deposit_len = deposit_bytes.len() as u32; + + let mut setup = CallSetup::::default(); + setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); + setup.set_balance(value + (Pallet::::min_balance() * 2u32.into())); + + let account_id = &setup.contract().account_id.clone(); + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + + let input = vec![42u8; i as _]; + let salt = [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 = { + let mut current = 0u32; + move |after: u32| { + current += after; + current + } + }; + + assert!(ContractInfoOf::::get(&addr).is_none()); + + let result; + #[block] + { + result = runtime.bench_instantiate( + memory.as_mut_slice(), + 0, // code_hash_ptr + 0, // ref_time_limit + 0, // proof_size_limit + offset(hash_len), // deposit_ptr + offset(deposit_len), // value_ptr + offset(value_len), // input_data_ptr + i, // input_data_len + SENTINEL, // address_ptr + SENTINEL, // output_ptr + 0, // output_len_ptr + offset(i), // salt_ptr + ); + } + + assert_ok!(result); + assert!(ContractInfoOf::::get(&addr).is_some()); + 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 }>) { + build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_sha2_256(memory.as_mut_slice(), 32, n, 0); + } + assert_eq!(sp_io::hashing::sha2_256(&memory[32..]), &memory[0..32]); + assert_ok!(result); + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_keccak_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0); + } + assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]); + assert_ok!(result); + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_blake2_256(memory.as_mut_slice(), 32, n, 0); + } + assert_eq!(sp_io::hashing::blake2_256(&memory[32..]), &memory[0..32]); + assert_ok!(result); + } + + // `n`: Input to hash in bytes + #[benchmark(pov_mode = Measured)] + fn seal_hash_blake2_128(n: Linear<0, { limits::MEMORY_BYTES }>) { + build_runtime!(runtime, memory: [[0u8; 16], vec![0u8; n as usize], ]); + + let result; + #[block] + { + result = runtime.bench_hash_blake2_128(memory.as_mut_slice(), 16, n, 0); + } + assert_eq!(sp_io::hashing::blake2_128(&memory[16..]), &memory[0..16]); + assert_ok!(result); + } + + // `n`: Message input length to verify in bytes. + // need some buffer so the code size does not exceed the max code size. + #[benchmark(pov_mode = Measured)] + fn seal_sr25519_verify(n: Linear<0, { T::MaxCodeLen::get() - 255 }>) { + let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::>(); + let message_len = message.len() as u32; + + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let pub_key = sp_io::crypto::sr25519_generate(key_type, None); + let sig = + sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature"); + let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec(); + let sig_len = sig.len() as u32; + + build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]); + + let result; + #[block] + { + result = runtime.bench_sr25519_verify( + memory.as_mut_slice(), + 0, // signature_ptr + sig_len, // pub_key_ptr + message_len, // message_len + sig_len + pub_key.len() as u32, // message_ptr + ); + } + + assert_eq!(result.unwrap(), ReturnErrorCode::Success); + } + + #[benchmark(pov_mode = Measured)] + fn seal_ecdsa_recover() { + let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes()); + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let signature = { + let pub_key = sp_io::crypto::ecdsa_generate(key_type, None); + let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash) + .expect("Generates signature"); + AsRef::<[u8; 65]>::as_ref(&sig).to_vec() + }; + + build_runtime!(runtime, memory: [signature, message_hash, [0u8; 33], ]); + + let result; + #[block] + { + result = runtime.bench_ecdsa_recover( + memory.as_mut_slice(), + 0, // signature_ptr + 65, // message_hash_ptr + 65 + 32, // output_ptr + ); + } + + assert_eq!(result.unwrap(), ReturnErrorCode::Success); + } + + // Only calling the function itself for the list of + // generated different ECDSA keys. + // This is a slow call: We reduce the number of runs. + #[benchmark(pov_mode = Measured)] + fn seal_ecdsa_to_eth_address() { + let key_type = sp_core::crypto::KeyTypeId(*b"code"); + let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0; + build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]); + + let result; + #[block] + { + result = runtime.bench_ecdsa_to_eth_address( + memory.as_mut_slice(), + 20, // key_ptr + 0, // output_ptr + ); + } + + assert_ok!(result); + assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_set_code_hash() -> Result<(), BenchmarkError> { + let code_hash = + Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; + + build_runtime!(runtime, memory: [ code_hash.encode(),]); + + let result; + #[block] + { + result = runtime.bench_set_code_hash(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + Ok(()) + } + + #[benchmark(pov_mode = Measured)] + fn lock_delegate_dependency() -> Result<(), BenchmarkError> { + let code_hash = Contract::::with_index(1, WasmModule::dummy_unique(1), vec![])? + .info()? + .code_hash; + + build_runtime!(runtime, memory: [ code_hash.encode(),]); + + let result; + #[block] + { + result = runtime.bench_lock_delegate_dependency(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + Ok(()) + } + + #[benchmark] + fn unlock_delegate_dependency() -> Result<(), BenchmarkError> { + let code_hash = Contract::::with_index(1, WasmModule::dummy_unique(1), vec![])? + .info()? + .code_hash; + + build_runtime!(runtime, memory: [ code_hash.encode(),]); + runtime.bench_lock_delegate_dependency(memory.as_mut_slice(), 0).unwrap(); + + let result; + #[block] + { + result = runtime.bench_unlock_delegate_dependency(memory.as_mut_slice(), 0); + } + + assert_ok!(result); + Ok(()) + } + + // Benchmark the execution of instructions. + #[benchmark(pov_mode = Ignored)] + fn instr(r: Linear<0, INSTR_BENCHMARK_RUNS>) { + // (round, start, div, mult, add) + let input = (r, 1_000u32, 2u32, 3u32, 100u32).encode(); + let mut setup = CallSetup::::new(WasmModule::instr()); + let (mut ext, module) = setup.ext(); + let prepared = CallSetup::::prepare_call(&mut ext, module, input); + #[block] + { + prepared.call().unwrap(); + } + } + + impl_benchmark_test_suite!( + Contracts, + crate::tests::ExtBuilder::default().build(), + crate::tests::Test, + ); +} diff --git a/substrate/frame/revive/src/benchmarking_dummy.rs b/substrate/frame/revive/src/benchmarking_dummy.rs new file mode 100644 index 0000000000000000000000000000000000000000..6bb4679112721516b4db4bf57ffdd40625230a6c --- /dev/null +++ b/substrate/frame/revive/src/benchmarking_dummy.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +//! Defines a dummy benchmarking suite so that the build doesn't fail in case +//! no RISC-V toolchain is available. + +#![cfg(feature = "runtime-benchmarks")] +#![cfg(not(feature = "riscv"))] + +use crate::{Config, *}; +use frame_benchmarking::v2::*; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark(pov_mode = Ignored)] + fn enable_riscv_feature_to_unlock_benchmarks() { + #[block] + {} + } +} diff --git a/substrate/frame/revive/src/chain_extension.rs b/substrate/frame/revive/src/chain_extension.rs new file mode 100644 index 0000000000000000000000000000000000000000..ccea1294505418f4930524c22de216071361baed --- /dev/null +++ b/substrate/frame/revive/src/chain_extension.rs @@ -0,0 +1,358 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A mechanism for runtime authors to augment the functionality of contracts. +//! +//! The runtime is able to call into any contract and retrieve the result using +//! [`bare_call`](crate::Pallet::bare_call). This already allows customization of runtime +//! behaviour by user generated code (contracts). However, often it is more straightforward +//! to allow the reverse behaviour: The contract calls into the runtime. We call the latter +//! one a "chain extension" because it allows the chain to extend the set of functions that are +//! callable by a contract. +//! +//! In order to create a chain extension the runtime author implements the [`ChainExtension`] +//! trait and declares it in this pallet's [configuration Trait](Config). All types +//! required for this endeavour are defined or re-exported in this module. There is an +//! implementation on `()` which can be used to signal that no chain extension is available. +//! +//! # Using multiple chain extensions +//! +//! Often there is a need for having multiple chain extensions. This is often the case when +//! some generally useful off-the-shelf extensions should be included. To have multiple chain +//! extensions they can be put into a tuple which is then passed to [`Config::ChainExtension`] like +//! this `type Extensions = (ExtensionA, ExtensionB)`. +//! +//! However, only extensions implementing [`RegisteredChainExtension`] can be put into a tuple. +//! This is because the [`RegisteredChainExtension::ID`] is used to decide which of those extensions +//! should be used when the contract calls a chain extensions. Extensions which are generally +//! useful should claim their `ID` with [the registry](https://github.com/paritytech/chainextension-registry) +//! so that no collisions with other vendors will occur. +//! +//! **Chain specific extensions must use the reserved `ID = 0` so that they can't be registered with +//! the registry.** +//! +//! # Security +//! +//! The chain author alone is responsible for the security of the chain extension. +//! This includes avoiding the exposure of exploitable functions and charging the +//! appropriate amount of weight. In order to do so benchmarks must be written and the +//! [`charge_weight`](Environment::charge_weight) function must be called **before** +//! carrying out any action that causes the consumption of the chargeable weight. +//! It cannot be overstated how delicate of a process the creation of a chain extension +//! is. Check whether using [`bare_call`](crate::Pallet::bare_call) suffices for the +//! use case at hand. +//! +//! # Benchmarking +//! +//! The builtin contract callable functions that pallet-revive provides all have +//! benchmarks that determine the correct weight that an invocation of these functions +//! induces. In order to be able to charge the correct weight for the functions defined +//! by a chain extension benchmarks must be written, too. In the near future this crate +//! will provide the means for easier creation of those specialized benchmarks. +//! +//! # Example +//! +//! The ink-examples repository maintains an +//! [end-to-end example](https://github.com/paritytech/ink-examples/tree/main/rand-extension) +//! on how to use a chain extension in order to provide new features to ink! contracts. + +use crate::{ + wasm::{Memory, Runtime, RuntimeCosts}, + Error, +}; +use alloc::vec::Vec; +use codec::{Decode, MaxEncodedLen}; +use frame_support::weights::Weight; +use sp_runtime::DispatchError; + +pub use crate::{exec::Ext, gas::ChargedAmount, storage::meter::Diff, Config}; +pub use frame_system::Config as SysConfig; +pub use pallet_revive_uapi::ReturnFlags; + +/// Result that returns a [`DispatchError`] on error. +pub type Result = core::result::Result; + +/// A trait used to extend the set of contract callable functions. +/// +/// In order to create a custom chain extension this trait must be implemented and supplied +/// to the pallet contracts configuration trait as the associated type of the same name. +/// Consult the [module documentation](self) for a general explanation of chain extensions. +/// +/// # Lifetime +/// +/// The extension will be [`Default`] initialized at the beginning of each call +/// (**not** per call stack) and dropped afterwards. Hence any value held inside the extension +/// can be used as a per-call scratch buffer. +pub trait ChainExtension { + /// Call the chain extension logic. + /// + /// This is the only function that needs to be implemented in order to write a + /// chain extensions. It is called whenever a contract calls the `seal_call_chain_extension` + /// imported wasm function. + /// + /// # Parameters + /// - `env`: Access to the remaining arguments and the execution environment. + /// + /// # Return + /// + /// In case of `Err` the contract execution is immediately suspended and the passed error + /// is returned to the caller. Otherwise the value of [`RetVal`] determines the exit + /// behaviour. + /// + /// # Note + /// + /// The [`Self::call`] can be invoked within a read-only context, where any state-changing calls + /// are disallowed. This information can be obtained using `env.ext().is_read_only()`. It is + /// crucial for the implementer to handle this scenario appropriately. + fn call, M: ?Sized + Memory>( + &mut self, + env: Environment, + ) -> Result; + + /// Determines whether chain extensions are enabled for this chain. + /// + /// The default implementation returns `true`. Therefore it is not necessary to overwrite + /// this function when implementing a chain extension. In case of `false` the deployment of + /// a contract that references `seal_call_chain_extension` will be denied and calling this + /// function will return [`NoChainExtension`](Error::NoChainExtension) without first calling + /// into [`call`](Self::call). + fn enabled() -> bool { + true + } +} + +/// A [`ChainExtension`] that can be composed with other extensions using a tuple. +/// +/// An extension that implements this trait can be put in a tuple in order to have multiple +/// extensions available. The tuple implementation routes requests based on the first two +/// most significant bytes of the `id` passed to `call`. +/// +/// If this extensions is to be used by multiple runtimes consider +/// [registering it](https://github.com/paritytech/chainextension-registry) to ensure that there +/// are no collisions with other vendors. +/// +/// # Note +/// +/// Currently, we support tuples of up to ten registered chain extensions. If more chain extensions +/// are needed consider opening an issue. +pub trait RegisteredChainExtension: ChainExtension { + /// The extensions globally unique identifier. + const ID: u16; +} + +#[impl_trait_for_tuples::impl_for_tuples(10)] +#[tuple_types_custom_trait_bound(RegisteredChainExtension)] +impl ChainExtension for Tuple { + fn call, M: ?Sized + Memory>( + &mut self, + mut env: Environment, + ) -> Result { + for_tuples!( + #( + if (Tuple::ID == env.ext_id()) && Tuple::enabled() { + return Tuple.call(env); + } + )* + ); + Err(Error::::NoChainExtension.into()) + } + + fn enabled() -> bool { + for_tuples!( + #( + if Tuple::enabled() { + return true; + } + )* + ); + false + } +} + +/// Determines the exit behaviour and return value of a chain extension. +pub enum RetVal { + /// The chain extensions returns the supplied value to its calling contract. + Converging(u32), + /// The control does **not** return to the calling contract. + /// + /// Use this to stop the execution of the contract when the chain extension returns. + /// The semantic is the same as for calling `seal_return`: The control returns to + /// the caller of the currently executing contract yielding the supplied buffer and + /// flags. + Diverging { flags: ReturnFlags, data: Vec }, +} + +/// Grants the chain extension access to its parameters and execution environment. +pub struct Environment<'a, 'b, E: Ext, M: ?Sized> { + /// The runtime contains all necessary functions to interact with the running contract. + runtime: &'a mut Runtime<'b, E, M>, + /// Reference to the contract's memory. + memory: &'a mut M, + /// Verbatim argument passed to `seal_call_chain_extension`. + id: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + input_ptr: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + input_len: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + output_ptr: u32, + /// Verbatim argument passed to `seal_call_chain_extension`. + output_len_ptr: u32, +} + +/// Functions that are available in every state of this type. +impl<'a, 'b, E: Ext, M: ?Sized + Memory> Environment<'a, 'b, E, M> { + /// Creates a new environment for consumption by a chain extension. + pub fn new( + runtime: &'a mut Runtime<'b, E, M>, + memory: &'a mut M, + id: u32, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Self { + Self { runtime, memory, id, input_ptr, input_len, output_ptr, output_len_ptr } + } + + /// The function id within the `id` passed by a contract. + /// + /// It returns the two least significant bytes of the `id` passed by a contract as the other + /// two bytes represent the chain extension itself (the code which is calling this function). + pub fn func_id(&self) -> u16 { + (self.id & 0x0000FFFF) as u16 + } + + /// The chain extension id within the `id` passed by a contract. + /// + /// It returns the two most significant bytes of the `id` passed by a contract which represent + /// the chain extension itself (the code which is calling this function). + pub fn ext_id(&self) -> u16 { + (self.id >> 16) as u16 + } + + /// Charge the passed `amount` of weight from the overall limit. + /// + /// It returns `Ok` when there the remaining weight budget is larger than the passed + /// `weight`. It returns `Err` otherwise. In this case the chain extension should + /// abort the execution and pass through the error. + /// + /// The returned value can be used to with [`Self::adjust_weight`]. Other than that + /// it has no purpose. + /// + /// # Note + /// + /// Weight is synonymous with gas in substrate. + pub fn charge_weight(&mut self, amount: Weight) -> Result { + self.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) { + self.runtime.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight)) + } + + /// Grants access to the execution environment of the current contract call. + /// + /// Consult the functions on the returned type before re-implementing those functions. + pub fn ext(&mut self) -> &mut E { + self.runtime.ext() + } + + /// Reads `min(max_len, in_len)` from contract memory. + /// + /// This does **not** charge any weight. The caller must make sure that the an + /// appropriate amount of weight is charged **before** reading from contract memory. + /// The reason for that is that usually the costs for reading data and processing + /// said data cannot be separated in a benchmark. Therefore a chain extension would + /// charge the overall costs either using `max_len` (worst case approximation) or using + /// [`in_len()`](Self::in_len). + pub fn read(&self, max_len: u32) -> Result> { + self.memory.read(self.input_ptr, self.input_len.min(max_len)) + } + + /// Reads `min(buffer.len(), in_len) from contract memory. + /// + /// This takes a mutable pointer to a buffer fills it with data and shrinks it to + /// the size of the actual data. Apart from supporting pre-allocated buffers it is + /// equivalent to to [`read()`](Self::read). + pub fn read_into(&self, buffer: &mut &mut [u8]) -> Result<()> { + let len = buffer.len(); + let sliced = { + let buffer = core::mem::take(buffer); + &mut buffer[..len.min(self.input_len as usize)] + }; + self.memory.read_into_buf(self.input_ptr, sliced)?; + *buffer = sliced; + Ok(()) + } + + /// Reads and decodes a type with a size fixed at compile time from contract memory. + /// + /// This function is secure and recommended for all input types of fixed size + /// as long as the cost of reading the memory is included in the overall already charged + /// weight of the chain extension. This should usually be the case when fixed input types + /// are used. + pub fn read_as(&mut self) -> Result { + self.memory.read_as(self.input_ptr) + } + + /// Reads and decodes a type with a dynamic size from contract memory. + /// + /// Make sure to include `len` in your weight calculations. + pub fn read_as_unbounded(&mut self, len: u32) -> Result { + self.memory.read_as_unbounded(self.input_ptr, len) + } + + /// The length of the input as passed in as `input_len`. + /// + /// A chain extension would use this value to calculate the dynamic part of its + /// weight. For example a chain extension that calculates the hash of some passed in + /// bytes would use `in_len` to charge the costs of hashing that amount of bytes. + /// This also subsumes the act of copying those bytes as a benchmarks measures both. + pub fn in_len(&self) -> u32 { + self.input_len + } + + /// Write the supplied buffer to contract memory. + /// + /// If the contract supplied buffer is smaller than the passed `buffer` an `Err` is returned. + /// If `allow_skip` is set to true the contract is allowed to skip the copying of the buffer + /// by supplying the guard value of `pallet-revive::SENTINEL` as `out_ptr`. The + /// `weight_per_byte` is only charged when the write actually happens and is not skipped or + /// failed due to a too small output buffer. + pub fn write( + &mut self, + buffer: &[u8], + allow_skip: bool, + weight_per_byte: Option, + ) -> Result<()> { + self.runtime.write_sandbox_output( + self.memory, + self.output_ptr, + self.output_len_ptr, + buffer, + allow_skip, + |len| { + weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into()))) + }, + ) + } +} diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs new file mode 100644 index 0000000000000000000000000000000000000000..d1fc0823e03dff6719018ee60f0c0668aca37d7f --- /dev/null +++ b/substrate/frame/revive/src/debug.rs @@ -0,0 +1,109 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use crate::{ + exec::{ExecResult, ExportedFunction}, + primitives::ExecReturnValue, +}; +use crate::{Config, LOG_TARGET}; +use sp_core::H160; + +/// Umbrella trait for all interfaces that serves for debugging. +pub trait Debugger: Tracing + CallInterceptor {} + +impl Debugger for V where V: Tracing + CallInterceptor {} + +/// Defines methods to capture contract calls, enabling external observers to +/// measure, trace, and react to contract interactions. +pub trait Tracing { + /// The type of [`CallSpan`] that is created by this trait. + type CallSpan: CallSpan; + + /// Creates a new call span to encompass the upcoming contract execution. + /// + /// This method should be invoked just before the execution of a contract and + /// marks the beginning of a traceable span of execution. + /// + /// # Arguments + /// + /// * `contract_address` - The address of the contract that is about to be executed. + /// * `entry_point` - Describes whether the call is the constructor or a regular call. + /// * `input_data` - The raw input data of the call. + fn new_call_span( + contract_address: &H160, + entry_point: ExportedFunction, + input_data: &[u8], + ) -> Self::CallSpan; +} + +/// Defines a span of execution for a contract call. +pub trait CallSpan { + /// Called just after the execution of a contract. + /// + /// # Arguments + /// + /// * `output` - The raw output of the call. + fn after_call(self, output: &ExecReturnValue); +} + +impl Tracing for () { + type CallSpan = (); + + fn new_call_span(contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8]) { + log::trace!(target: LOG_TARGET, "call {entry_point:?} address: {contract_address:?}, input_data: {input_data:?}") + } +} + +impl CallSpan for () { + fn after_call(self, output: &ExecReturnValue) { + log::trace!(target: LOG_TARGET, "call result {output:?}") + } +} + +/// Provides an interface for intercepting contract calls. +pub trait CallInterceptor { + /// Allows to intercept contract calls and decide whether they should be executed or not. + /// If the call is intercepted, the mocked result of the call is returned. + /// + /// # Arguments + /// + /// * `contract_address` - The address of the contract that is about to be executed. + /// * `entry_point` - Describes whether the call is the constructor or a regular call. + /// * `input_data` - The raw input data of the call. + /// + /// # Expected behavior + /// + /// This method should return: + /// * `Some(ExecResult)` - if the call should be intercepted and the mocked result of the call + /// is returned. + /// * `None` - otherwise, i.e. the call should be executed normally. + fn intercept_call( + contract_address: &H160, + entry_point: ExportedFunction, + input_data: &[u8], + ) -> Option; +} + +impl CallInterceptor for () { + fn intercept_call( + _contract_address: &H160, + _entry_point: ExportedFunction, + _input_data: &[u8], + ) -> Option { + None + } +} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs new file mode 100644 index 0000000000000000000000000000000000000000..233658696c8fa968aab32972e25b1b91ce8642c4 --- /dev/null +++ b/substrate/frame/revive/src/exec.rs @@ -0,0 +1,4105 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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::{ + address::{self, AddressMapper}, + debug::{CallInterceptor, CallSpan, Tracing}, + gas::GasMeter, + limits, + primitives::{ExecReturnValue, StorageDeposit}, + runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, + storage::{self, meter::Diff, WriteOutcome}, + transient_storage::TransientStorage, + BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, Error, + Event, Pallet as Contracts, LOG_TARGET, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, marker::PhantomData, mem}; +use frame_support::{ + crypto::ecdsa::ECDSAExt, + dispatch::{DispatchResult, DispatchResultWithPostInfo}, + ensure, + storage::{with_transaction, TransactionOutcome}, + traits::{ + fungible::{Inspect, Mutate}, + tokens::{Fortitude, Preservation}, + Contains, OriginTrait, Time, + }, + weights::Weight, + Blake2_128Concat, BoundedVec, StorageHasher, +}; +use frame_system::{ + pallet_prelude::{BlockNumberFor, OriginFor}, + Pallet as System, RawOrigin, +}; +use sp_core::{ + ecdsa::Public as ECDSAPublic, + sr25519::{Public as SR25519Public, Signature as SR25519Signature}, + ConstU32, Get, H160, H256, U256, +}; +use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; +use sp_runtime::{ + traits::{BadOrigin, Convert, Dispatchable, Zero}, + DispatchError, SaturatedConversion, +}; + +pub type AccountIdOf = ::AccountId; +pub type MomentOf = <::Time as Time>::Moment; +pub type ExecResult = Result; + +/// 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. + Fix([u8; 32]), + /// Variant for variable sized keys. + Var(VarSizedKey), +} + +impl Key { + /// Reference to the raw unhashed key. + /// + /// # Note + /// + /// Only used by benchmarking in order to generate storage collisions on purpose. + #[cfg(feature = "runtime-benchmarks")] + pub fn unhashed(&self) -> &[u8] { + match self { + Key::Fix(v) => v.as_ref(), + Key::Var(v) => v.as_ref(), + } + } + + /// The hashed key that has be used as actual key to the storage trie. + pub fn hash(&self) -> Vec { + match self { + Key::Fix(v) => blake2_256(v.as_slice()).to_vec(), + Key::Var(v) => Blake2_128Concat::hash(v.as_slice()), + } + } + + pub fn from_fixed(v: [u8; 32]) -> Self { + Self::Fix(v) + } + + pub fn try_from_var(v: Vec) -> Result { + VarSizedKey::try_from(v).map(Self::Var).map_err(|_| ()) + } +} + +/// Origin of the error. +/// +/// Call or instantiate both called into other contracts and pass through errors happening +/// in those to the caller. This enum is for the caller to distinguish whether the error +/// happened during the execution of the callee or in the current execution context. +#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)] +pub enum ErrorOrigin { + /// Caller error origin. + /// + /// The error happened in the current execution context rather than in the one + /// of the contract that is called into. + Caller, + /// The error happened during execution of the called contract. + Callee, +} + +/// Error returned by contract execution. +#[derive(Copy, Clone, PartialEq, Eq, Debug, codec::Decode, codec::Encode)] +pub struct ExecError { + /// The reason why the execution failed. + pub error: DispatchError, + /// Origin of the error. + pub origin: ErrorOrigin, +} + +impl> From for ExecError { + fn from(error: T) -> Self { + Self { error: error.into(), origin: ErrorOrigin::Caller } + } +} + +/// The type of origins supported by the contracts pallet. +#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)] +pub enum Origin { + Root, + Signed(T::AccountId), +} + +impl Origin { + /// Creates a new Signed Caller from an AccountId. + pub fn from_account_id(account_id: T::AccountId) -> Self { + Origin::Signed(account_id) + } + /// Creates a new Origin from a `RuntimeOrigin`. + pub fn from_runtime_origin(o: OriginFor) -> Result { + match o.into() { + Ok(RawOrigin::Root) => Ok(Self::Root), + Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)), + _ => Err(BadOrigin.into()), + } + } + /// Returns the AccountId of a Signed Origin or an error if the origin is Root. + pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> { + match self { + Origin::Signed(id) => Ok(id), + Origin::Root => Err(DispatchError::RootNotAllowed), + } + } +} + +/// An interface that provides access to the external environment in which the +/// smart-contract is executed. +/// +/// This interface is specialized to an account of the executing code, so all +/// operations are implicitly performed on that account. +/// +/// # Note +/// +/// This trait is sealed and cannot be implemented by downstream crates. +pub trait Ext: sealing::Sealed { + type T: Config; + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// Returns the code size of the called contract. + fn call( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + to: &H160, + value: U256, + input_data: Vec, + allows_reentry: bool, + read_only: bool, + ) -> Result; + + /// Execute code in the current frame. + /// + /// Returns the code size of the called contract. + fn delegate_call( + &mut self, + code: H256, + input_data: Vec, + ) -> Result; + + /// Instantiate a contract from the given code. + /// + /// Returns the original code size of the called contract. + /// The newly created account will be associated with `code`. `value` specifies the amount of + /// value transferred from the caller to the newly created account. + fn instantiate( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + code: H256, + value: U256, + input_data: Vec, + salt: Option<&[u8; 32]>, + ) -> Result<(H160, ExecReturnValue), ExecError>; + + /// Transfer all funds to `beneficiary` and delete the contract. + /// + /// Since this function removes the self contract eagerly, if succeeded, no further actions + /// should be performed on this `Ext` instance. + /// + /// This function will fail if the same contract is present on the contract + /// call stack. + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult; + + /// Transfer some amount of funds into the specified account. + fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult; + + /// Returns the storage entry of the executing account by the given `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_storage` or + /// was deleted. + fn get_storage(&mut self, key: &Key) -> Option>; + + /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_storage` or + /// was deleted. + fn get_storage_size(&mut self, key: &Key) -> Option; + + /// Sets the storage entry by the given key to the specified value. If `value` is `None` then + /// the storage entry is deleted. + fn set_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result; + + /// Returns the transient storage entry of the executing account for the given `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_transient_storage` or + /// was deleted. + fn get_transient_storage(&self, key: &Key) -> Option>; + + /// Returns `Some(len)` (in bytes) if a transient storage item exists at `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_transient_storage` or + /// was deleted. + fn get_transient_storage_size(&self, key: &Key) -> Option; + + /// Sets the transient storage entry for the given key to the specified value. If `value` is + /// `None` then the storage entry is deleted. + fn set_transient_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result; + + /// Returns the caller. + fn caller(&self) -> Origin; + + /// Check if a contract lives at the specified `address`. + fn is_contract(&self, address: &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: &H160) -> Option; + + /// Returns the code hash of the contract being executed. + fn own_code_hash(&mut self) -> &H256; + + /// Check if the caller of the current contract is the origin of the whole call stack. + /// + /// This can be checked with `is_contract(self.caller())` as well. + /// However, this function does not require any storage lookup and therefore uses less weight. + fn caller_is_origin(&self) -> bool; + + /// Check if the caller is origin, and this origin is root. + fn caller_is_root(&self) -> bool; + + /// Returns a reference to the account id of the current contract. + fn account_id(&self) -> &AccountIdOf; + + /// Returns a reference to the [`H160`] address of the current contract. + fn address(&self) -> H160 { + ::AddressMapper::to_address(self.account_id()) + } + + /// Returns the balance of the current contract. + /// + /// The `value_transferred` is already added. + fn balance(&self) -> U256; + + /// Returns the balance of the supplied account. + /// + /// The `value_transferred` is already added. + fn balance_of(&self, address: &H160) -> U256; + + /// Returns the value transferred along with this call. + fn value_transferred(&self) -> U256; + + /// 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) -> 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); + + /// Returns the current block number. + 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) -> U256; + + /// Get an immutable reference to the nested gas meter. + fn gas_meter(&self) -> &GasMeter; + + /// Get a mutable reference to the nested gas meter. + fn gas_meter_mut(&mut self) -> &mut GasMeter; + + /// Charges `diff` from the meter. + fn charge_storage(&mut self, diff: &Diff); + + /// Append a string to the debug buffer. + /// + /// It is added as-is without any additional new line. + /// + /// This is a no-op if debug message recording is disabled which is always the case + /// when the code is executing on-chain. + /// + /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. + fn append_debug_buffer(&mut self, msg: &str) -> bool; + + /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. + fn debug_buffer_enabled(&self) -> bool; + + /// Call some dispatchable and return the result. + fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo; + + /// Recovers ECDSA compressed public key based on signature and message hash. + fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>; + + /// Verify a sr25519 signature. + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool; + + /// Returns Ethereum address from the ECDSA compressed public key. + fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()>; + + /// Tests sometimes need to modify and inspect the contract info directly. + #[cfg(any(test, feature = "runtime-benchmarks"))] + fn contract_info(&mut self) -> &mut ContractInfo; + + /// Get a mutable reference to the transient storage. + /// Useful in benchmarks when it is sometimes necessary to modify and inspect the transient + /// storage directly. + #[cfg(feature = "runtime-benchmarks")] + fn transient_storage(&mut self) -> &mut TransientStorage; + + /// Sets new code hash for existing contract. + fn set_code_hash(&mut self, hash: 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. + /// + /// # Errors + /// + /// [`Error::CodeNotFound`] is returned if no stored code found having the specified + /// `code_hash`. + fn increment_refcount(code_hash: H256) -> DispatchResult; + + /// Decrement the reference count of a stored code by one. + /// + /// # Note + /// + /// A contract whose reference count dropped to zero isn't automatically removed. A + /// `remove_code` transaction must be submitted by the original uploader to do so. + fn decrement_refcount(code_hash: H256); + + /// Adds a delegate dependency to [`ContractInfo`]'s `delegate_dependencies` field. + /// + /// This ensures that the delegated contract is not removed while it is still in use. It + /// increases the reference count of the code hash and charges a fraction (see + /// [`Config::CodeHashLockupDepositPercent`]) of the code deposit. + /// + /// # Errors + /// + /// - [`Error::MaxDelegateDependenciesReached`] + /// - [`Error::CannotAddSelfAsDelegateDependency`] + /// - [`Error::DelegateDependencyAlreadyExists`] + fn lock_delegate_dependency(&mut self, code_hash: H256) -> DispatchResult; + + /// Removes a delegate dependency from [`ContractInfo`]'s `delegate_dependencies` field. + /// + /// This is the counterpart of [`Self::lock_delegate_dependency`]. It decreases the reference + /// count and refunds the deposit that was charged by [`Self::lock_delegate_dependency`]. + /// + /// # Errors + /// + /// - [`Error::DelegateDependencyNotFound`] + fn unlock_delegate_dependency(&mut self, code_hash: &H256) -> DispatchResult; + + /// Returns the number of locked delegate dependencies. + /// + /// Note: Requires &mut self to access the contract info. + fn locked_delegate_dependencies_count(&mut self) -> usize; + + /// Check if running in read-only context. + fn is_read_only(&self) -> bool; +} + +/// Describes the different functions that can be exported by an [`Executable`]. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + sp_core::RuntimeDebug, + codec::Decode, + codec::Encode, + codec::MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ExportedFunction { + /// The constructor function which is executed on deployment of a contract. + Constructor, + /// The function which is executed when a contract is called. + Call, +} + +/// A trait that represents something that can be executed. +/// +/// In the on-chain environment this would be represented by a wasm module. This trait exists in +/// order to be able to mock the wasm logic for testing. +pub trait Executable: Sized { + /// Load the executable from storage. + /// + /// # Note + /// Charges size base load weight from the gas meter. + fn from_storage(code_hash: H256, gas_meter: &mut GasMeter) -> Result; + + /// Execute the specified exported function and return the result. + /// + /// When the specified function is `Constructor` the executable is stored and its + /// refcount incremented. + /// + /// # Note + /// + /// This functions expects to be executed in a storage transaction that rolls back + /// all of its emitted storage changes. + fn execute>( + self, + ext: &mut E, + function: ExportedFunction, + input_data: Vec, + ) -> ExecResult; + + /// The code info of the executable. + fn code_info(&self) -> &CodeInfo; + + /// The raw code of the executable. + fn code(&self) -> &[u8]; + + /// The code hash of the executable. + fn code_hash(&self) -> &H256; +} + +/// The complete call stack of a contract execution. +/// +/// The call stack is initiated by either a signed origin or one of the contract RPC calls. +/// This type implements `Ext` and by that exposes the business logic of contract execution to +/// the runtime module which interfaces with the contract (the wasm blob) itself. +pub struct Stack<'a, T: Config, E> { + /// The origin that initiated the call stack. It could either be a Signed plain account that + /// holds an account id or Root. + /// + /// # Note + /// + /// Please note that it is possible that the id of a Signed origin belongs to a contract rather + /// than a plain account when being called through one of the contract RPCs where the + /// client can freely choose the origin. This usually makes no sense but is still possible. + origin: Origin, + /// The gas meter where costs are charged to. + gas_meter: &'a mut GasMeter, + /// The storage meter makes sure that the storage deposit limit is obeyed. + storage_meter: &'a mut storage::meter::Meter, + /// The timestamp at the point of call stack instantiation. + timestamp: MomentOf, + /// The block number at the time of call stack instantiation. + block_number: BlockNumberFor, + /// The actual call stack. One entry per nested contract called/instantiated. + /// This does **not** include the [`Self::first_frame`]. + frames: BoundedVec, ConstU32<{ limits::CALL_STACK_DEPTH }>>, + /// Statically guarantee that each call stack has at least one frame. + first_frame: Frame, + /// A text buffer used to output human readable information. + /// + /// All the bytes added to this field should be valid UTF-8. The buffer has no defined + /// structure and is intended to be shown to users as-is for debugging purposes. + debug_message: Option<&'a mut DebugBuffer>, + /// Transient storage used to store data, which is kept for the duration of a transaction. + transient_storage: TransientStorage, + /// No executable is held by the struct but influences its behaviour. + _phantom: PhantomData, +} + +/// Represents one entry in the call stack. +/// +/// For each nested contract call or instantiate one frame is created. It holds specific +/// information for the said call and caches the in-storage `ContractInfo` data structure. +struct Frame { + /// The address of the executing contract. + account_id: T::AccountId, + /// The cached in-storage data of the contract. + contract_info: CachedContract, + /// The amount of balance transferred by the caller as part of the call. + value_transferred: BalanceOf, + /// Determines whether this is a call or instantiate frame. + entry_point: ExportedFunction, + /// The gas meter capped to the supplied gas limit. + nested_gas: GasMeter, + /// The storage meter for the individual call. + nested_storage: storage::meter::NestedMeter, + /// If `false` the contract enabled its defense against reentrance attacks. + allows_reentry: bool, + /// If `true` subsequent calls cannot modify storage. + read_only: bool, + /// The caller of the currently executing frame which was spawned by `delegate_call`. + delegate_caller: Option>, +} + +/// Used in a delegate call frame arguments in order to override the executable and caller. +struct DelegatedCall { + /// The executable which is run instead of the contracts own `executable`. + executable: E, + /// The caller of the contract. + caller: Origin, +} + +/// Parameter passed in when creating a new `Frame`. +/// +/// It determines whether the new frame is for a call or an instantiate. +enum FrameArgs<'a, T: Config, E> { + Call { + /// The account id of the contract that is to be called. + dest: T::AccountId, + /// If `None` the contract info needs to be reloaded from storage. + cached_info: Option>, + /// This frame was created by `seal_delegate_call` and hence uses different code than + /// what is stored at [`Self::Call::dest`]. Its caller ([`DelegatedCall::caller`]) is the + /// account which called the caller contract + delegated_call: Option>, + }, + Instantiate { + /// The contract or signed origin which instantiates the new contract. + sender: T::AccountId, + /// The executable whose `deploy` function is run. + executable: E, + /// A salt used in the contract address derivation of the new contract. + salt: Option<&'a [u8; 32]>, + /// The input data is used in the contract address derivation of the new contract. + input_data: &'a [u8], + }, +} + +/// Describes the different states of a contract as contained in a `Frame`. +enum CachedContract { + /// The cached contract is up to date with the in-storage value. + Cached(ContractInfo), + /// A recursive call into the same contract did write to the contract info. + /// + /// In this case the cached contract is stale and needs to be reloaded from storage. + Invalidated, + /// The current contract executed `terminate` and removed the contract. + /// + /// In this case a reload is neither allowed nor possible. Please note that recursive + /// calls cannot remove a contract as this is checked and denied. + Terminated, +} + +impl Frame { + /// Return the `contract_info` of the current contract. + fn contract_info(&mut self) -> &mut ContractInfo { + self.contract_info.get(&self.account_id) + } + + /// Terminate and return the `contract_info` of the current contract. + /// + /// # Note + /// + /// Under no circumstances the contract is allowed to access the `contract_info` after + /// a call to this function. This would constitute a programming error in the exec module. + fn terminate(&mut self) -> ContractInfo { + self.contract_info.terminate(&self.account_id) + } +} + +/// Extract the contract info after loading it from storage. +/// +/// This assumes that `load` was executed before calling this macro. +macro_rules! get_cached_or_panic_after_load { + ($c:expr) => {{ + if let CachedContract::Cached(contract) = $c { + contract + } else { + panic!( + "It is impossible to remove a contract that is on the call stack;\ + See implementations of terminate;\ + Therefore fetching a contract will never fail while using an account id + that is currently active on the call stack;\ + qed" + ); + } + }}; +} + +/// Same as [`Stack::top_frame`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! top_frame { + ($stack:expr) => { + $stack.frames.last().unwrap_or(&$stack.first_frame) + }; +} + +/// Same as [`Stack::top_frame_mut`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! top_frame_mut { + ($stack:expr) => { + $stack.frames.last_mut().unwrap_or(&mut $stack.first_frame) + }; +} + +impl CachedContract { + /// 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(T::AddressMapper::to_address(account_id)); + if let Some(contract) = contract { + *self = CachedContract::Cached(contract); + } + } + } + + /// Return the cached contract_info. + fn get(&mut self, account_id: &T::AccountId) -> &mut ContractInfo { + self.load(account_id); + get_cached_or_panic_after_load!(self) + } + + /// Terminate and return the contract info. + fn terminate(&mut self, account_id: &T::AccountId) -> ContractInfo { + self.load(account_id); + get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated)) + } +} + +impl<'a, T, E> Stack<'a, T, E> +where + T: Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, + E: Executable, +{ + /// Create and run a new call stack by calling into `dest`. + /// + /// # Note + /// + /// `debug_message` should only ever be set to `Some` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + /// + /// # Return Value + /// + /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)> + pub fn run_call( + origin: Origin, + dest: H160, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + input_data: Vec, + debug_message: Option<&'a mut DebugBuffer>, + ) -> 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) + } else { + Self::transfer_no_contract(&origin, &dest, value) + } + } + + /// Create and run a new call stack by instantiating a new contract. + /// + /// # Note + /// + /// `debug_message` should only ever be set to `Some` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + /// + /// # Return Value + /// + /// Result<(NewContractAccountId, ExecReturnValue), ExecError)> + pub fn run_instantiate( + origin: T::AccountId, + executable: E, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + input_data: Vec, + salt: Option<&[u8; 32]>, + debug_message: Option<&'a mut DebugBuffer>, + ) -> Result<(H160, ExecReturnValue), ExecError> { + let (mut stack, executable) = Self::new( + FrameArgs::Instantiate { + sender: origin.clone(), + executable, + salt, + input_data: input_data.as_ref(), + }, + Origin::from_account_id(origin), + gas_meter, + storage_meter, + value, + debug_message, + )? + .expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE); + let address = T::AddressMapper::to_address(&stack.top_frame().account_id); + stack.run(executable, input_data).map(|ret| (address, ret)) + } + + #[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] + pub fn bench_new_call( + dest: H160, + origin: Origin, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + debug_message: Option<&'a mut DebugBuffer>, + ) -> (Self, E) { + Self::new( + FrameArgs::Call { + dest: T::AddressMapper::to_account_id(&dest), + cached_info: None, + delegated_call: None, + }, + origin, + gas_meter, + storage_meter, + value, + 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, + gas_meter: &'a mut GasMeter, + storage_meter: &'a mut storage::meter::Meter, + value: BalanceOf, + debug_message: Option<&'a mut DebugBuffer>, + ) -> Result, ExecError> { + let Some((first_frame, executable)) = Self::new_frame( + args, + value, + gas_meter, + Weight::zero(), + storage_meter, + BalanceOf::::zero(), + false, + )? + else { + return Ok(None); + }; + + let stack = Self { + origin, + gas_meter, + storage_meter, + timestamp: T::Time::now(), + block_number: >::block_number(), + first_frame, + frames: Default::default(), + debug_message, + transient_storage: TransientStorage::new(limits::TRANSIENT_STORAGE_BYTES), + _phantom: Default::default(), + }; + + Ok(Some((stack, executable))) + } + + /// Construct a new frame. + /// + /// This does not take `self` because when constructing the first frame `self` is + /// not initialized, yet. + fn new_frame( + frame_args: FrameArgs, + value_transferred: BalanceOf, + gas_meter: &mut GasMeter, + gas_limit: Weight, + storage_meter: &mut storage::meter::GenericMeter, + deposit_limit: BalanceOf, + read_only: bool, + ) -> Result, 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 { + if let Some(contract) = + >::get(T::AddressMapper::to_address(&dest)) + { + contract + } else { + return Ok(None) + } + }; + + let (executable, delegate_caller) = + if let Some(DelegatedCall { executable, caller }) = delegated_call { + (executable, Some(caller)) + } else { + (E::from_storage(contract.code_hash, gas_meter)?, None) + }; + + (dest, contract, executable, delegate_caller, ExportedFunction::Call) + }, + FrameArgs::Instantiate { sender, executable, salt, input_data } => { + let 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( + &address, + >::account_nonce(&sender), + *executable.code_hash(), + )?; + ( + T::AddressMapper::to_account_id_contract(&address), + contract, + executable, + None, + ExportedFunction::Constructor, + ) + }, + }; + + let frame = Frame { + delegate_caller, + value_transferred, + contract_info: CachedContract::Cached(contract_info), + account_id, + entry_point, + nested_gas: gas_meter.nested(gas_limit), + nested_storage: storage_meter.nested(deposit_limit), + allows_reentry: true, + read_only, + }; + + Ok(Some((frame, executable))) + } + + /// Create a subsequent nested frame. + fn push_frame( + &mut self, + frame_args: FrameArgs, + value_transferred: BalanceOf, + gas_limit: Weight, + deposit_limit: BalanceOf, + read_only: bool, + ) -> Result, ExecError> { + if self.frames.len() as u32 == limits::CALL_STACK_DEPTH { + return Err(Error::::MaxCallDepthReached.into()); + } + + // We need to make sure that changes made to the contract info are not discarded. + // See the `in_memory_changes_not_discarded` test for more information. + // We do not store on instantiate because we do not allow to call into a contract + // from its own constructor. + let frame = self.top_frame(); + if let (CachedContract::Cached(contract), ExportedFunction::Call) = + (&frame.contract_info, frame.entry_point) + { + >::insert( + 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; + if let Some((frame, executable)) = Self::new_frame( + frame_args, + value_transferred, + nested_gas, + gas_limit, + nested_storage, + deposit_limit, + read_only, + )? { + self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; + Ok(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) -> ExecResult { + let frame = self.top_frame(); + let entry_point = frame.entry_point; + let delegated_code_hash = + if frame.delegate_caller.is_some() { Some(*executable.code_hash()) } else { None }; + + self.transient_storage.start_transaction(); + + let do_transaction = || { + 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()?; + frame.nested_storage.charge_instantiate( + origin, + &frame.account_id, + frame.contract_info.get(&frame.account_id), + executable.code_info(), + )?; + // Needs to be incremented before calling into the code so that it is visible + // in case of recursion. + >::inc_account_nonce(caller.account_id()?); + } + + // Every non delegate call or instantiate also optionally transfers the balance. + // 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 = T::AddressMapper::to_address(&top_frame!(self).account_id); + + let call_span = T::Debug::new_call_span(&contract_address, entry_point, &input_data); + + let output = T::Debug::intercept_call(&contract_address, entry_point, &input_data) + .unwrap_or_else(|| { + executable + .execute(self, entry_point, input_data) + .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee }) + })?; + + call_span.after_call(&output); + + // Avoid useless work that would be reverted anyways. + if output.did_revert() { + return Ok(output); + } + + // Storage limit is normally enforced as late as possible (when the last frame returns) + // so that the ordering of storage accesses does not matter. + // (However, if a special limit was set for a sub-call, it should be enforced right + // after the sub-call returned. See below for this case of enforcement). + if self.frames.is_empty() { + let frame = &mut self.first_frame; + frame.contract_info.load(&frame.account_id); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_limit(contract)?; + } + + let frame = self.top_frame(); + let account_id = 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()); + } + + // If a special limit was set for the sub-call, we enforce it here. + // This is needed because contract constructor might write to storage. + // The sub-call will be rolled back in case the limit is exhausted. + let frame = self.top_frame_mut(); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_subcall_limit(contract)?; + + let caller = T::AddressMapper::to_address(self.caller().account_id()?); + + // Deposit an instantiation event. + Contracts::::deposit_event(Event::Instantiated { + deployer: caller, + contract: account_id, + }); + }, + (ExportedFunction::Call, Some(code_hash)) => { + Contracts::::deposit_event(Event::DelegateCalled { + contract: account_id, + code_hash, + }); + }, + (ExportedFunction::Call, None) => { + // If a special limit was set for the sub-call, we enforce it here. + // The sub-call will be rolled back in case the limit is exhausted. + let frame = self.top_frame_mut(); + let contract = frame.contract_info.as_contract(); + frame.nested_storage.enforce_subcall_limit(contract)?; + + let caller = self.caller(); + Contracts::::deposit_event(Event::Called { + caller: caller.clone(), + contract: account_id, + }); + }, + } + + Ok(output) + }; + + // All changes performed by the contract are executed under a storage transaction. + // This allows for roll back on error. Changes to the cached contract_info are + // committed or rolled back when popping the frame. + // + // `with_transactional` may return an error caused by a limit in the + // transactional storage depth. + let transaction_outcome = + with_transaction(|| -> TransactionOutcome> { + let output = do_transaction(); + match &output { + Ok(result) if !result.did_revert() => + TransactionOutcome::Commit(Ok((true, output))), + _ => TransactionOutcome::Rollback(Ok((false, output))), + } + }); + + let (success, output) = match transaction_outcome { + // `with_transactional` executed successfully, and we have the expected output. + Ok((success, output)) => (success, output), + // `with_transactional` returned an error, and we propagate that error and note no state + // has changed. + Err(error) => (false, Err(error.into())), + }; + + if success { + self.transient_storage.commit_transaction(); + } else { + self.transient_storage.rollback_transaction(); + } + + self.pop_frame(success); + output + } + + /// Remove the current (top) frame from the stack. + /// + /// This is called after running the current frame. It commits cached values to storage + /// and invalidates all stale references to it that might exist further down the call stack. + fn pop_frame(&mut self, persist: bool) { + // Pop the current frame from the stack and return it in case it needs to interact + // with duplicates that might exist on the stack. + // A `None` means that we are returning from the `first_frame`. + let frame = self.frames.pop(); + + // Both branches do essentially the same with the exception. The difference is that + // the else branch does consume the hardcoded `first_frame`. + if let Some(mut frame) = frame { + let account_id = &frame.account_id; + let prev = top_frame_mut!(self); + + prev.nested_gas.absorb_nested(frame.nested_gas); + + // Only gas counter changes are persisted in case of a failure. + if !persist { + return; + } + + // Record the storage meter changes of the nested call into the parent meter. + // If the dropped frame's contract wasn't terminated we update the deposit counter + // in its contract info. The load is necessary to pull it from storage in case + // it was invalidated. + frame.contract_info.load(account_id); + let mut contract = frame.contract_info.into_contract(); + prev.nested_storage.absorb(frame.nested_storage, account_id, contract.as_mut()); + + // In case the contract wasn't terminated we need to persist changes made to it. + if let Some(contract) = contract { + // optimization: Predecessor is the same contract. + // We can just copy the contract into the predecessor without a storage write. + // This is possible when there is no other contract in-between that could + // trigger a rollback. + if prev.account_id == *account_id { + prev.contract_info = CachedContract::Cached(contract); + return; + } + + // Predecessor is a different contract: We persist the info and invalidate the first + // stale cache we find. This triggers a reload from storage on next use. We skip(1) + // because that case is already handled by the optimization above. Only the first + // cache needs to be invalidated because that one will invalidate the next cache + // when it is popped from the stack. + >::insert(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; + } + } + } else { + if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) { + log::debug!( + target: LOG_TARGET, + "Execution finished with debug buffer: {}", + core::str::from_utf8(msg).unwrap_or(""), + ); + } + self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas)); + if !persist { + return; + } + let mut contract = self.first_frame.contract_info.as_contract(); + self.storage_meter.absorb( + mem::take(&mut self.first_frame.nested_storage), + &self.first_frame.account_id, + contract.as_deref_mut(), + ); + if let Some(contract) = contract { + >::insert( + T::AddressMapper::to_address(&self.first_frame.account_id), + contract, + ); + } + } + } + + /// Transfer some funds from `from` to `to`. + 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(()) + } + + /// 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(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. + fn top_frame(&self) -> &Frame { + top_frame!(self) + } + + /// Mutable reference to the current (top) frame. + fn top_frame_mut(&mut self) -> &mut Frame { + top_frame_mut!(self) + } + + /// Iterator over all frames. + /// + /// The iterator starts with the top frame and ends with the root frame. + fn frames(&self) -> impl Iterator> { + core::iter::once(&self.first_frame).chain(&self.frames).rev() + } + + /// Same as `frames` but with a mutable reference as iterator item. + fn frames_mut(&mut self) -> impl Iterator> { + core::iter::once(&mut self.first_frame).chain(&mut self.frames).rev() + } + + /// Returns whether the current contract is on the stack multiple times. + fn is_recursive(&self) -> bool { + let account_id = &self.top_frame().account_id; + self.frames().skip(1).any(|f| &f.account_id == account_id) + } + + /// Returns whether the specified contract allows to be reentered right now. + fn allows_reentry(&self, id: &T::AccountId) -> bool { + !self.frames().any(|f| &f.account_id == id && !f.allows_reentry) + } + + /// Returns the *free* balance of the supplied AccountId. + fn account_balance(&self, who: &T::AccountId) -> U256 { + T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Polite).into() + } +} + +impl<'a, T, E> Ext for Stack<'a, T, E> +where + T: Config, + E: Executable, + BalanceOf: Into + TryFrom, + MomentOf: Into, +{ + type T = T; + + fn call( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + dest: &H160, + value: U256, + input_data: Vec, + allows_reentry: bool, + read_only: bool, + ) -> ExecResult { + // 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(&dest) { + return Err(>::ReentranceDenied.into()); + } + + // We ignore instantiate frames in our search for a cached contract. + // Otherwise it would be possible to recursively call a contract from its own + // constructor: We disallow calling not fully constructed contracts. + let cached_info = self + .frames() + .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == dest) + .and_then(|f| match &f.contract_info { + CachedContract::Cached(contract) => Some(contract.clone()), + _ => None, + }); + if let Some(executable) = self.push_frame( + FrameArgs::Call { dest: dest.clone(), cached_info, delegated_call: None }, + value, + gas_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) + } else { + Self::transfer_no_contract( + &Origin::from_account_id(self.account_id().clone()), + &dest, + value, + ) + } + }; + + // We need to make sure to reset `allows_reentry` even on failure. + let result = try_call(); + + // Protection is on a per call basis. + self.top_frame_mut().allows_reentry = true; + + result + } + + fn delegate_call( + &mut self, + code_hash: H256, + input_data: Vec, + ) -> Result { + let executable = E::from_storage(code_hash, self.gas_meter_mut())?; + let top_frame = self.top_frame_mut(); + let contract_info = top_frame.contract_info().clone(); + let account_id = top_frame.account_id.clone(); + let value = top_frame.value_transferred; + let executable = self.push_frame( + FrameArgs::Call { + dest: account_id, + cached_info: Some(contract_info), + delegated_call: Some(DelegatedCall { executable, caller: self.caller().clone() }), + }, + value, + Weight::zero(), + BalanceOf::::zero(), + self.is_read_only(), + )?; + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) + } + + fn instantiate( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + code_hash: H256, + value: U256, + input_data: Vec, + salt: Option<&[u8; 32]>, + ) -> Result<(H160, ExecReturnValue), ExecError> { + let executable = E::from_storage(code_hash, self.gas_meter_mut())?; + let sender = &self.top_frame().account_id; + let executable = self.push_frame( + FrameArgs::Instantiate { + sender: sender.clone(), + executable, + salt, + input_data: input_data.as_ref(), + }, + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, + gas_limit, + deposit_limit.try_into().map_err(|_| Error::::BalanceConversionFailed)?, + self.is_read_only(), + )?; + let address = T::AddressMapper::to_address(&self.top_frame().account_id); + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) + .map(|ret| (address, ret)) + } + + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult { + if self.is_recursive() { + return Err(Error::::TerminatedWhileReentrant.into()); + } + let frame = self.top_frame_mut(); + let info = frame.terminate(); + let beneficiary_account = T::AddressMapper::to_account_id(beneficiary); + frame.nested_storage.terminate(&info, beneficiary_account); + + info.queue_trie_for_deletion(); + let account_address = T::AddressMapper::to_address(&frame.account_id); + ContractInfoOf::::remove(&account_address); + Self::decrement_refcount(info.code_hash); + + for (code_hash, deposit) in info.delegate_dependencies() { + Self::decrement_refcount(*code_hash); + frame + .nested_storage + .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(*deposit)); + } + + Contracts::::deposit_event(Event::Terminated { + contract: account_address, + beneficiary: *beneficiary, + }); + Ok(()) + } + + 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> { + self.top_frame_mut().contract_info().read(key) + } + + fn get_storage_size(&mut self, key: &Key) -> Option { + self.top_frame_mut().contract_info().size(key.into()) + } + + fn set_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result { + let frame = self.top_frame_mut(); + frame.contract_info.get(&frame.account_id).write( + key.into(), + value, + Some(&mut frame.nested_storage), + take_old, + ) + } + + fn get_transient_storage(&self, key: &Key) -> Option> { + self.transient_storage.read(self.account_id(), key) + } + + fn get_transient_storage_size(&self, key: &Key) -> Option { + self.transient_storage + .read(self.account_id(), key) + .map(|value| value.len() as _) + } + + fn set_transient_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result { + let account_id = self.account_id().clone(); + self.transient_storage.write(&account_id, key, value, take_old) + } + + fn account_id(&self) -> &T::AccountId { + &self.top_frame().account_id + } + + fn caller(&self) -> Origin { + if let Some(caller) = &self.top_frame().delegate_caller { + caller.clone() + } else { + self.frames() + .nth(1) + .map(|f| Origin::from_account_id(f.account_id.clone())) + .unwrap_or(self.origin.clone()) + } + } + + fn is_contract(&self, address: &H160) -> bool { + ContractInfoOf::::contains_key(&address) + } + + fn code_hash(&self, address: &H160) -> Option { + >::get(&address).map(|contract| contract.code_hash) + } + + fn own_code_hash(&mut self) -> &H256 { + &self.top_frame_mut().contract_info().code_hash + } + + fn caller_is_origin(&self) -> bool { + self.origin == self.caller() + } + + fn caller_is_root(&self) -> bool { + // if the caller isn't origin, then it can't be root. + self.caller_is_origin() && self.origin == Origin::Root + } + + fn balance(&self) -> 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) -> U256 { + self.timestamp.into() + } + + fn minimum_balance(&self) -> U256 { + T::Currency::minimum_balance().into() + } + + fn deposit_event(&mut self, topics: Vec, data: Vec) { + Contracts::::deposit_event(Event::ContractEmitted { + contract: T::AddressMapper::to_address(self.account_id()), + data, + topics, + }); + } + + 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) -> U256 { + T::WeightPrice::convert(weight).into() + } + + fn gas_meter(&self) -> &GasMeter { + &self.top_frame().nested_gas + } + + fn gas_meter_mut(&mut self) -> &mut GasMeter { + &mut self.top_frame_mut().nested_gas + } + + fn charge_storage(&mut self, diff: &Diff) { + self.top_frame_mut().nested_storage.charge(diff) + } + + fn debug_buffer_enabled(&self) -> bool { + self.debug_message.is_some() + } + + fn append_debug_buffer(&mut self, msg: &str) -> bool { + if let Some(buffer) = &mut self.debug_message { + buffer + .try_extend(&mut msg.bytes()) + .map_err(|_| { + log::debug!( + target: LOG_TARGET, + "Debug buffer (of {} bytes) exhausted!", + limits::DEBUG_BUFFER_BYTES, + ) + }) + .ok(); + true + } else { + false + } + } + + fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo { + let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into(); + origin.add_filter(T::CallFilter::contains); + call.dispatch(origin) + } + + fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> { + secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ()) + } + + fn sr25519_verify(&self, signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> bool { + sp_io::crypto::sr25519_verify( + &SR25519Signature::from(*signature), + message, + &SR25519Public::from(*pub_key), + ) + } + + fn ecdsa_to_eth_address(&self, pk: &[u8; 33]) -> Result<[u8; 20], ()> { + ECDSAPublic::from(*pk).to_eth_address() + } + + #[cfg(any(test, feature = "runtime-benchmarks"))] + fn contract_info(&mut self) -> &mut ContractInfo { + self.top_frame_mut().contract_info() + } + + #[cfg(feature = "runtime-benchmarks")] + fn transient_storage(&mut self) -> &mut TransientStorage { + &mut self.transient_storage + } + + fn set_code_hash(&mut self, hash: H256) -> DispatchResult { + let frame = top_frame_mut!(self); + + let info = frame.contract_info(); + + let prev_hash = info.code_hash; + info.code_hash = hash; + + let code_info = CodeInfoOf::::get(hash).ok_or(Error::::CodeNotFound)?; + + let old_base_deposit = info.storage_base_deposit(); + let new_base_deposit = info.update_base_deposit(&code_info); + let deposit = StorageDeposit::Charge(new_base_deposit) + .saturating_sub(&StorageDeposit::Charge(old_base_deposit)); + + frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit); + + Self::increment_refcount(hash)?; + Self::decrement_refcount(prev_hash); + Contracts::::deposit_event(Event::ContractCodeUpdated { + contract: T::AddressMapper::to_address(&frame.account_id), + new_code_hash: hash, + old_code_hash: prev_hash, + }); + Ok(()) + } + + 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); + Ok(()) + } else { + Err(Error::::CodeNotFound.into()) + } + }) + } + + fn decrement_refcount(code_hash: H256) { + >::mutate(code_hash, |existing| { + if let Some(info) = existing { + *info.refcount_mut() = info.refcount().saturating_sub(1); + } + }); + } + + fn lock_delegate_dependency(&mut self, code_hash: 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); + + let code_info = CodeInfoOf::::get(code_hash).ok_or(Error::::CodeNotFound)?; + let deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit()); + + info.lock_delegate_dependency(code_hash, deposit)?; + Self::increment_refcount(code_hash)?; + frame + .nested_storage + .charge_deposit(frame.account_id.clone(), StorageDeposit::Charge(deposit)); + Ok(()) + } + + fn unlock_delegate_dependency(&mut self, code_hash: &H256) -> DispatchResult { + let frame = self.top_frame_mut(); + let info = frame.contract_info.get(&frame.account_id); + + let deposit = info.unlock_delegate_dependency(code_hash)?; + Self::decrement_refcount(*code_hash); + frame + .nested_storage + .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(deposit)); + Ok(()) + } + + fn locked_delegate_dependencies_count(&mut self) -> usize { + self.top_frame_mut().contract_info().delegate_dependencies_count() + } + + fn is_read_only(&self) -> bool { + self.top_frame().read_only + } +} + +mod sealing { + use super::*; + + pub trait Sealed {} + + impl<'a, T: Config, E> Sealed for Stack<'a, T, E> {} +} + +/// These tests exercise the executive layer. +/// +/// In these tests the VM/loader are mocked. Instead of dealing with wasm bytecode they use simple +/// closures. This allows you to tackle executive logic more thoroughly without writing a +/// wasm VM code. +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + exec::ExportedFunction::*, + gas::GasMeter, + test_utils::*, + tests::{ + test_utils::{get_balance, place_contract, set_balance}, + ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, + }, + AddressMapper, Error, + }; + use assert_matches::assert_matches; + use frame_support::{assert_err, assert_ok, parameter_types}; + use frame_system::{EventRecord, Phase}; + use pallet_revive_uapi::ReturnFlags; + use pretty_assertions::assert_eq; + use sp_runtime::{traits::Hash, DispatchError}; + use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc}; + + type System = frame_system::Pallet; + + type MockStack<'a> = Stack<'a, Test, MockExecutable>; + + parameter_types! { + static Loader: MockLoader = MockLoader::default(); + } + + fn events() -> Vec> { + System::events() + .into_iter() + .filter_map(|meta| match meta.event { + MetaEvent::Contracts(contract_event) => Some(contract_event), + _ => None, + }) + .collect() + } + + struct MockCtx<'a> { + ext: &'a mut MockStack<'a>, + input_data: Vec, + } + + #[derive(Clone)] + struct MockExecutable { + func: Rc Fn(MockCtx<'a>, &Self) -> ExecResult + 'static>, + func_type: ExportedFunction, + code_hash: H256, + code_info: CodeInfo, + } + + #[derive(Default, Clone)] + pub struct MockLoader { + map: HashMap, + counter: u64, + } + + impl MockLoader { + fn code_hashes() -> Vec { + Loader::get().map.keys().copied().collect() + } + + fn insert( + func_type: ExportedFunction, + f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static, + ) -> H256 { + Loader::mutate(|loader| { + // Generate code hashes as monotonically increasing values. + let hash = ::Hash::from_low_u64_be(loader.counter); + loader.counter += 1; + loader.map.insert( + hash, + MockExecutable { + func: Rc::new(f), + func_type, + code_hash: hash, + code_info: CodeInfo::::new(ALICE), + }, + ); + hash + }) + } + } + + impl Executable for MockExecutable { + fn from_storage( + code_hash: H256, + _gas_meter: &mut GasMeter, + ) -> Result { + Loader::mutate(|loader| { + loader.map.get(&code_hash).cloned().ok_or(Error::::CodeNotFound.into()) + }) + } + + fn execute>( + self, + ext: &mut E, + function: ExportedFunction, + input_data: Vec, + ) -> ExecResult { + if let Constructor = function { + E::increment_refcount(self.code_hash).unwrap(); + } + // # Safety + // + // We know that we **always** call execute with a `MockStack` in this test. + // + // # Note + // + // The transmute is necessary because `execute` has to be generic over all + // `E: Ext`. However, `MockExecutable` can't be generic over `E` as it would + // constitute a cycle. + let ext = unsafe { mem::transmute(ext) }; + if function == self.func_type { + (self.func)(MockCtx { ext, input_data }, &self) + } else { + exec_success() + } + } + + fn code(&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 + } + + fn code_info(&self) -> &CodeInfo { + &self.code_info + } + } + + fn exec_success() -> ExecResult { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) + } + + fn exec_trapped() -> ExecResult { + Err(ExecError { error: >::ContractTrapped.into(), origin: ErrorOrigin::Callee }) + } + + #[test] + fn it_works() { + parameter_types! { + static TestData: Vec = vec![0]; + } + + let value = Default::default(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let exec_ch = MockLoader::insert(Call, |_ctx, _executable| { + TestData::mutate(|data| data.push(1)); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, exec_ch); + let mut storage_meter = + storage::meter::Meter::new(&Origin::from_account_id(ALICE), 0, value).unwrap(); + + assert_matches!( + MockStack::run_call( + Origin::from_account_id(ALICE), + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + value, + vec![], + None, + ), + Ok(_) + ); + }); + + assert_eq!(TestData::get(), vec![0, 1]); + } + + #[test] + fn transfer_works() { + // This test verifies that a contract is able to transfer + // some funds to another account. + ExtBuilder::default().build().execute_with(|| { + set_balance(&ALICE, 100); + set_balance(&BOB, 0); + + MockStack::transfer(&ALICE, &BOB, 55).unwrap(); + + assert_eq!(get_balance(&ALICE), 45); + assert_eq!(get_balance(&BOB), 55); + }); + } + + #[test] + fn correct_transfer_on_call() { + let value = 55; + + let success_ch = MockLoader::insert(Call, move |ctx, _| { + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) + }); + + ExtBuilder::default().build().execute_with(|| { + 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( + origin.clone(), + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + value, + vec![], + None, + ) + .unwrap(); + + assert_eq!(get_balance(&ALICE), 100 - value); + assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); + }); + } + + #[test] + fn correct_transfer_on_delegate_call() { + let value = 35; + + let success_ch = MockLoader::insert(Call, move |ctx, _| { + 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(), 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(&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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + value, + vec![], + None, + ) + .unwrap(); + + assert_eq!(get_balance(&ALICE), 100 - value); + assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); + }); + } + + #[test] + fn changes_are_reverted_on_failing_call() { + // This test verifies that changes are reverted on a call which fails (or equally, returns + // a non-zero status code). + + let return_ch = MockLoader::insert(Call, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() }) + }); + + ExtBuilder::default().build().execute_with(|| { + 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 55, + vec![], + None, + ) + .unwrap(); + + assert!(output.did_revert()); + assert_eq!(get_balance(&ALICE), 100); + assert_eq!(get_balance(&BOB), balance); + }); + } + + #[test] + fn balance_too_low() { + // This test verifies that a contract can't send value if it's + // balance is too low. + let origin = ALICE; + let dest = BOB; + + ExtBuilder::default().build().execute_with(|| { + set_balance(&origin, 0); + + let result = MockStack::transfer(&origin, &dest, 100); + + assert_eq!(result, Err(Error::::TransferFailed.into())); + assert_eq!(get_balance(&origin), 0); + assert_eq!(get_balance(&dest), 0); + }); + } + + #[test] + fn output_is_returned_on_success() { + // Verifies that if a contract returns data with a successful exit status, this data + // is returned from the execution context. + let return_ch = MockLoader::insert(Call, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] }) + }); + + ExtBuilder::default().build().execute_with(|| { + 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + let output = result.unwrap(); + assert!(!output.did_revert()); + assert_eq!(output.data, vec![1, 2, 3, 4]); + }); + } + + #[test] + fn output_is_returned_on_failure() { + // Verifies that if a contract returns data with a failing exit status, this data + // is returned from the execution context. + let 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 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![], + None, + ); + + let output = result.unwrap(); + assert!(output.did_revert()); + assert_eq!(output.data, vec![1, 2, 3, 4]); + }); + } + + #[test] + fn input_data_to_call() { + let input_data_ch = MockLoader::insert(Call, |ctx, _| { + assert_eq!(ctx.input_data, &[1, 2, 3, 4]); + exec_success() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, input_data_ch); + let 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![1, 2, 3, 4], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn input_data_to_instantiate() { + let input_data_ch = MockLoader::insert(Constructor, |ctx, _| { + assert_eq!(ctx.input_data, &[1, 2, 3, 4]); + exec_success() + }); + + // This one tests passing the input data into a contract via instantiate. + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = + MockExecutable::from_storage(input_data_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + let 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, + vec![1, 2, 3, 4], + Some(&[0; 32]), + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn max_depth() { + // This test verifies that when we reach the maximal depth creation of an + // yet another context fails. + parameter_types! { + static ReachedBottom: bool = false; + } + let value = Default::default(); + let recurse_ch = MockLoader::insert(Call, |ctx, _| { + // Try to call into yourself. + let r = ctx.ext.call( + Weight::zero(), + U256::zero(), + &BOB_ADDR, + U256::zero(), + vec![], + true, + false, + ); + + ReachedBottom::mutate(|reached_bottom| { + if !*reached_bottom { + // We are first time here, it means we just reached bottom. + // Verify that we've got proper error and set `reached_bottom`. + assert_eq!(r, Err(Error::::MaxCallDepthReached.into())); + *reached_bottom = true; + } else { + // We just unwinding stack here. + assert_matches!(r, Ok(_)); + } + }); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + set_balance(&BOB, 1); + place_contract(&BOB, recurse_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); + + let result = MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + value, + vec![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn caller_returns_proper_values() { + parameter_types! { + static WitnessedCallerBob: Option = None; + static WitnessedCallerCharlie: Option = None; + } + + let bob_ch = MockLoader::insert(Call, |ctx, _| { + // Record the caller for bob. + WitnessedCallerBob::mutate(|caller| { + 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(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ), + Ok(_) + ); + exec_success() + }); + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + // Record the caller for charlie. + WitnessedCallerCharlie::mutate(|caller| { + let origin = ctx.ext.caller(); + *caller = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); + }); + exec_success() + }); + + ExtBuilder::default().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, 0, 0).unwrap(); + + let result = MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + + 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_ADDR)); + // Verify that ALICE is not a contract + assert!(!ctx.ext.is_contract(&ALICE_ADDR)); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + + 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![], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn code_hash_returns_proper_values() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // ALICE is not a contract and hence they do not have a code_hash + assert!(ctx.ext.code_hash(&ALICE_ADDR).is_none()); + // BOB is a contract and hence it has a code_hash + assert!(ctx.ext.code_hash(&BOB_ADDR).is_some()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn own_code_hash_returns_proper_values() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + let code_hash = ctx.ext.code_hash(&BOB_ADDR).unwrap(); + assert_eq!(*ctx.ext.own_code_hash(), code_hash); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn caller_is_origin_returns_proper_values() { + let code_charlie = MockLoader::insert(Call, |ctx, _| { + // BOB is not the origin of the stack call + assert!(!ctx.ext.caller_is_origin()); + exec_success() + }); + + let code_bob = MockLoader::insert(Call, |ctx, _| { + // ALICE is the origin of the call stack + assert!(ctx.ext.caller_is_origin()); + // BOB calls CHARLIE + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + }); + + 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(); + // ALICE -> BOB (caller is origin) -> CHARLIE (caller is not origin) + 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 root_caller_succeeds() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + let 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn root_caller_does_not_succeed_when_value_not_zero() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + let 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 1, + vec![0], + None, + ); + assert_matches!(result, Err(_)); + }); + } + + #[test] + fn root_caller_succeeds_with_consecutive_calls() { + let code_charlie = MockLoader::insert(Call, |ctx, _| { + // BOB is not root, even though the origin is root. + assert!(!ctx.ext.caller_is_root()); + exec_success() + }); + + let code_bob = MockLoader::insert(Call, |ctx, _| { + // root is the origin of the call stack. + assert!(ctx.ext.caller_is_root()); + // BOB calls CHARLIE. + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn address_returns_proper_values() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + // Verify that address matches BOB. + assert_eq!(ctx.ext.address(), BOB_ADDR); + + // Call into charlie contract. + assert_matches!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ), + Ok(_) + ); + exec_success() + }); + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + 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 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![], + None, + ); + + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn refuse_instantiate_with_value_below_existential_deposit() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success()); + + ExtBuilder::default().existential_deposit(15).build().execute_with(|| { + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + + assert_matches!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + 0, // <- zero value + vec![], + Some(&[0; 32]), + None, + ), + Err(_) + ); + }); + } + + #[test] + fn instantiation_work_with_success_output() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![80, 65, 83, 83] }) + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 1000); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, min_balance * 100, min_balance).unwrap(); + + let instantiated_contract_address = assert_matches!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + + min_balance, + vec![], + 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_id).unwrap(), + dummy_ch + ); + assert_eq!( + &events(), + &[Event::Instantiated { + deployer: ALICE_ADDR, + contract: instantiated_contract_address + }] + ); + }); + } + + #[test] + fn instantiation_fails_with_failing_output() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70, 65, 73, 76] }) + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 1000); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, min_balance * 100, min_balance).unwrap(); + + let instantiated_contract_address = assert_matches!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + + min_balance, + vec![], + 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_id).is_none()); + assert!(events().is_empty()); + }); + } + + #[test] + fn instantiation_from_contract() { + let dummy_ch = MockLoader::insert(Call, |_, _| exec_success()); + let instantiated_contract_address = Rc::new(RefCell::new(None::)); + let instantiator_ch = MockLoader::insert(Call, { + let instantiated_contract_address = Rc::clone(&instantiated_contract_address); + move |ctx, _| { + // Instantiate a contract and save it's address in `instantiated_contract_address`. + let (address, output) = ctx + .ext + .instantiate( + Weight::zero(), + U256::zero(), + dummy_ch, + ::Currency::minimum_balance().into(), + vec![], + Some(&[48; 32]), + ) + .unwrap(); + + *instantiated_contract_address.borrow_mut() = Some(address); + Ok(output) + } + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + set_balance(&ALICE, min_balance * 100); + place_contract(&BOB, instantiator_ch); + let 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( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + min_balance * 10, + vec![], + None, + ), + Ok(_) + ); + + let instantiated_contract_address = + *instantiated_contract_address.borrow().as_ref().unwrap(); + + 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_id).unwrap(), + dummy_ch + ); + assert_eq!( + &events(), + &[ + Event::Instantiated { + deployer: BOB_ADDR, + contract: instantiated_contract_address + }, + Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB_ADDR + }, + ] + ); + }); + } + + #[test] + fn instantiation_traps() { + let dummy_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into())); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + // Instantiate a contract and save it's address in `instantiated_contract_address`. + assert_matches!( + ctx.ext.instantiate( + Weight::zero(), + U256::zero(), + dummy_ch, + ::Currency::minimum_balance().into(), + vec![], + Some(&[0; 32]), + ), + Err(ExecError { + error: DispatchError::Other("It's a trap!"), + origin: ErrorOrigin::Callee, + }) + ); + + exec_success() + } + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_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(); + + assert_matches!( + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ), + Ok(_) + ); + + // The contract wasn't instantiated so we don't expect to see an instantiation + // event here. + assert_eq!( + &events(), + &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB_ADDR },] + ); + }); + } + + #[test] + fn termination_from_instantiate_fails() { + let terminate_ch = MockLoader::insert(Constructor, |ctx, _| { + ctx.ext.terminate(&ALICE_ADDR).unwrap(); + exec_success() + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = + MockExecutable::from_storage(terminate_ch, &mut gas_meter).unwrap(); + set_balance(&ALICE, 10_000); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 100).unwrap(); + + assert_eq!( + MockStack::run_instantiate( + ALICE, + executable, + &mut gas_meter, + &mut storage_meter, + 100, + vec![], + Some(&[0; 32]), + None, + ), + Err(Error::::TerminatedInConstructor.into()) + ); + + assert_eq!(&events(), &[]); + }); + } + + #[test] + fn in_memory_changes_not_discarded() { + // Call stack: BOB -> CHARLIE (trap) -> BOB' (success) + // This tests verifies some edge case of the contract info cache: + // We change some value in our contract info before calling into a contract + // that calls into ourself. This triggers a case where BOBs contract info + // is written to storage and invalidated by the successful execution of BOB'. + // The trap of CHARLIE reverts the storage changes to BOB. When the root BOB regains + // control it reloads its contract info from storage. We check that changes that + // are made before calling into CHARLIE are not discarded. + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + let info = ctx.ext.contract_info(); + assert_eq!(info.storage_byte_deposit, 0); + info.storage_byte_deposit = 42; + assert_eq!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ), + exec_trapped() + ); + assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42); + } + exec_success() + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + assert!(ctx + .ext + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) + .is_ok()); + exec_trapped() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let 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 recursive_call_during_constructor_is_balance_transfer() { + let code = MockLoader::insert(Constructor, |ctx, _| { + 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() + }); + + // This one tests passing the input data into a contract via instantiate. + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let executable = MockExecutable::from_storage(code, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + let 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, + 10, + vec![], + Some(&[0; 32]), + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn cannot_send_more_balance_than_available_to_self() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + 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() + }); + + 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"); + } + + #[test] + fn printing_works_on_fail() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + ctx.ext.append_debug_buffer("This is a test"); + ctx.ext.append_debug_buffer("More text"); + exec_trapped() + }); + + let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let 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 gas_meter, + &mut storage_meter, + 0, + vec![], + Some(&mut debug_buffer), + ); + assert!(result.is_err()); + }); + + assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text"); + } + + #[test] + fn debug_buffer_is_limited() { + let code_hash = MockLoader::insert(Call, move |ctx, _| { + ctx.ext.append_debug_buffer("overflowing bytes"); + exec_success() + }); + + // Pre-fill the buffer almost up to its limit, leaving not enough space to the message + let debug_buf_before = DebugBuffer::try_from(vec![0u8; DebugBuffer::bound() - 5]).unwrap(); + let mut debug_buf_after = debug_buf_before.clone(); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let 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_buf_after), + ) + .unwrap(); + assert_eq!(debug_buf_before, debug_buf_after); + }); + } + + #[test] + fn call_reentry_direct_recursion() { + // call the contract passed as input with disabled reentry + let code_bob = MockLoader::insert(Call, |ctx, _| { + let dest = H160::from_slice(ctx.input_data.as_ref()); + ctx.ext + .call(Weight::zero(), U256::zero(), &dest, U256::zero(), vec![], false, false) + }); + + let code_charlie = MockLoader::insert(Call, |_, _| exec_success()); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let 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( + origin.clone(), + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + CHARLIE_ADDR.as_bytes().to_vec(), + None, + )); + + // Calling into oneself fails + assert_err!( + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + BOB_ADDR.as_bytes().to_vec(), + None, + ) + .map_err(|e| e.error), + >::ReentranceDenied, + ); + }); + } + + #[test] + fn call_deny_reentry() { + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + false, + false, + ) + } else { + exec_success() + } + }); + + // call BOB with input set to '1' + let code_charlie = MockLoader::insert(Call, |ctx, _| { + ctx.ext.call( + Weight::zero(), + U256::zero(), + &BOB_ADDR, + U256::zero(), + vec![1], + true, + false, + ) + }); + + 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(); + + // BOB -> CHARLIE -> BOB fails as BOB denies reentry. + assert_err!( + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ) + .map_err(|e| e.error), + >::ReentranceDenied, + ); + }); + } + + #[test] + fn call_runtime_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + let call = RuntimeCall::System(frame_system::Call::remark_with_event { + remark: b"Hello World".to_vec(), + }); + ctx.ext.call_runtime(call).unwrap(); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + System::reset_events(); + MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap(); + + let remark_hash = ::Hashing::hash(b"Hello World"); + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked { + sender: BOB_CONTRACT_ID, + hash: remark_hash + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB_ADDR, + }), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn call_runtime_filter() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + use frame_system::Call as SysCall; + use pallet_balances::Call as BalanceCall; + use pallet_utility::Call as UtilCall; + + // remark should still be allowed + let allowed_call = + RuntimeCall::System(SysCall::remark_with_event { remark: b"Hello".to_vec() }); + + // transfers are disallowed by the `TestFiler` (see below) + let forbidden_call = RuntimeCall::Balances(BalanceCall::transfer_allow_death { + dest: CHARLIE, + value: 22, + }); + + // simple cases: direct call + assert_err!( + ctx.ext.call_runtime(forbidden_call.clone()), + frame_system::Error::::CallFiltered + ); + + // as part of a patch: return is OK (but it interrupted the batch) + assert_ok!(ctx.ext.call_runtime(RuntimeCall::Utility(UtilCall::batch { + calls: vec![allowed_call.clone(), forbidden_call, allowed_call] + })),); + + // the transfer wasn't performed + assert_eq!(get_balance(&CHARLIE), 0); + + exec_success() + }); + + TestFilter::set_filter(|call| match call { + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) => false, + _ => true, + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + System::reset_events(); + MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap(); + + let remark_hash = ::Hashing::hash(b"Hello"); + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked { + sender: BOB_CONTRACT_ID, + hash: remark_hash + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Utility(pallet_utility::Event::ItemCompleted), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Utility(pallet_utility::Event::BatchInterrupted { + index: 1, + error: frame_system::Error::::CallFiltered.into() + },), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB_ADDR, + }), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn nonce() { + let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped()); + let success_code = MockLoader::insert(Constructor, |_, _| exec_success()); + let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| { + ctx.ext + .instantiate( + Weight::zero(), + 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.account_id()), 0); + assert_eq!(ctx.ext.caller().account_id().unwrap(), &ALICE); + let (addr, _) = ctx + .ext + .instantiate( + Weight::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.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(), U256::zero(), &addr, U256::zero(), vec![], false, false) + .unwrap(); + + assert_eq!(System::account_nonce(ALICE), alice_nonce); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); + assert_eq!(System::account_nonce(&account_id), 0); + + exec_success() + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + let fail_executable = + MockExecutable::from_storage(fail_code, &mut gas_meter).unwrap(); + let success_executable = + MockExecutable::from_storage(success_code, &mut gas_meter).unwrap(); + let succ_fail_executable = + MockExecutable::from_storage(succ_fail_code, &mut gas_meter).unwrap(); + let succ_succ_executable = + MockExecutable::from_storage(succ_succ_code, &mut gas_meter).unwrap(); + set_balance(&ALICE, min_balance * 10_000); + set_balance(&BOB, min_balance * 10_000); + let 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( + ALICE, + fail_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 100, + vec![], + Some(&[0; 32]), + None, + ) + .ok(); + assert_eq!(System::account_nonce(&ALICE), 0); + + assert_ok!(MockStack::run_instantiate( + ALICE, + success_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 100, + vec![], + Some(&[0; 32]), + None, + )); + assert_eq!(System::account_nonce(&ALICE), 1); + + assert_ok!(MockStack::run_instantiate( + ALICE, + succ_fail_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 200, + vec![], + Some(&[0; 32]), + None, + )); + assert_eq!(System::account_nonce(&ALICE), 2); + + assert_ok!(MockStack::run_instantiate( + ALICE, + succ_succ_executable, + &mut gas_meter, + &mut storage_meter, + min_balance * 200, + vec![], + Some(&[0; 32]), + None, + )); + assert_eq!(System::account_nonce(&ALICE), 3); + }); + } + + #[test] + fn set_storage_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + // Write + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New)); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::New) + ); + + // Overwrite + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![42]), false), + Ok(WriteOutcome::Overwritten(3)) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![48]), true), + Ok(WriteOutcome::Taken(vec![4, 5, 6])) + ); + assert_eq!(ctx.ext.set_storage(&Key::Fix([3; 32]), None, false), Ok(WriteOutcome::New)); + assert_eq!(ctx.ext.set_storage(&Key::Fix([4; 32]), None, true), Ok(WriteOutcome::New)); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::Overwritten(0)) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::Taken(vec![])) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn set_storage_varsized_key_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + // Write + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![1, 2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 19].to_vec()).unwrap(), + Some(vec![4, 5, 6]), + true + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([3; 19].to_vec()).unwrap(), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([4; 64].to_vec()).unwrap(), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([5; 30].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([6; 128].to_vec()).unwrap(), + Some(vec![]), + true + ), + Ok(WriteOutcome::New) + ); + + // Overwrite + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![42, 43, 44]), + false + ), + Ok(WriteOutcome::Overwritten(3)) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 19].to_vec()).unwrap(), + Some(vec![48]), + true + ), + Ok(WriteOutcome::Taken(vec![4, 5, 6])) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([3; 19].to_vec()).unwrap(), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::try_from_var([4; 64].to_vec()).unwrap(), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([5; 30].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::Overwritten(0)) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([6; 128].to_vec()).unwrap(), + Some(vec![]), + true + ), + Ok(WriteOutcome::Taken(vec![])) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_storage(&Key::Fix([1; 32])), Some(vec![1, 2, 3])); + assert_eq!(ctx.ext.get_storage(&Key::Fix([2; 32])), Some(vec![])); + assert_eq!(ctx.ext.get_storage(&Key::Fix([3; 32])), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_size_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage(&Key::Fix([2; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([1; 32])), Some(3)); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([2; 32])), Some(0)); + assert_eq!(ctx.ext.get_storage_size(&Key::Fix([3; 32])), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_varsized_key_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 19].to_vec()).unwrap(), + Some(vec![1, 2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 16].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.get_storage(&Key::try_from_var([1; 19].to_vec()).unwrap()), + Some(vec![1, 2, 3]) + ); + assert_eq!( + ctx.ext.get_storage(&Key::try_from_var([2; 16].to_vec()).unwrap()), + Some(vec![]) + ); + assert_eq!(ctx.ext.get_storage(&Key::try_from_var([3; 8].to_vec()).unwrap()), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_storage_size_varsized_key_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([1; 19].to_vec()).unwrap(), + Some(vec![1, 2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_storage( + &Key::try_from_var([2; 16].to_vec()).unwrap(), + Some(vec![]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.get_storage_size(&Key::try_from_var([1; 19].to_vec()).unwrap()), + Some(3) + ); + assert_eq!( + ctx.ext.get_storage_size(&Key::try_from_var([2; 16].to_vec()).unwrap()), + Some(0) + ); + assert_eq!( + ctx.ext.get_storage_size(&Key::try_from_var([3; 8].to_vec()).unwrap()), + None + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 1000); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn set_transient_storage_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + // Write + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![4, 5, 6]), true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::New) + ); + + // Overwrite + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([1; 32]), Some(vec![42]), false), + Ok(WriteOutcome::Overwritten(3)) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([2; 32]), Some(vec![48]), true), + Ok(WriteOutcome::Taken(vec![4, 5, 6])) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([3; 32]), None, false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([4; 32]), None, true), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([5; 32]), Some(vec![]), false), + Ok(WriteOutcome::Overwritten(0)) + ); + assert_eq!( + ctx.ext.set_transient_storage(&Key::Fix([6; 32]), Some(vec![]), true), + Ok(WriteOutcome::Taken(vec![])) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn get_transient_storage_works() { + // Call stack: BOB -> CHARLIE(success) -> BOB' (success) + let storage_key_1 = &Key::Fix([1; 32]); + let storage_key_2 = &Key::Fix([2; 32]); + let storage_key_3 = &Key::Fix([3; 32]); + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + assert_eq!( + ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ), + exec_success() + ); + assert_eq!(ctx.ext.get_transient_storage(storage_key_1), Some(vec![3])); + assert_eq!(ctx.ext.get_transient_storage(storage_key_2), Some(vec![])); + assert_eq!(ctx.ext.get_transient_storage(storage_key_3), None); + } else { + assert_eq!( + ctx.ext.set_transient_storage(storage_key_1, Some(vec![3]), true), + Ok(WriteOutcome::Taken(vec![1, 2])) + ); + assert_eq!( + ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false), + Ok(WriteOutcome::New) + ); + } + exec_success() + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + assert!(ctx + .ext + .call(Weight::zero(), 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); + exec_success() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let 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 get_transient_storage_size_works() { + let storage_key_1 = &Key::Fix([1; 32]); + let storage_key_2 = &Key::Fix([2; 32]); + let storage_key_3 = &Key::Fix([3; 32]); + let code_hash = MockLoader::insert(Call, |ctx, _| { + assert_eq!( + ctx.ext.set_transient_storage(storage_key_1, Some(vec![1, 2, 3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.set_transient_storage(storage_key_2, Some(vec![]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(ctx.ext.get_transient_storage_size(storage_key_1), Some(3)); + assert_eq!(ctx.ext.get_transient_storage_size(storage_key_2), Some(0)); + assert_eq!(ctx.ext.get_transient_storage_size(storage_key_3), None); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + assert_ok!(MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + )); + }); + } + + #[test] + fn rollback_transient_storage_works() { + // Call stack: BOB -> CHARLIE (trap) -> BOB' (success) + let storage_key = &Key::Fix([1; 32]); + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data[0] == 0 { + assert_eq!( + ctx.ext.set_transient_storage(storage_key, Some(vec![1, 2]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ), + exec_trapped() + ); + assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![1, 2])); + } else { + let overwritten_length = ctx.ext.get_transient_storage_size(storage_key).unwrap(); + assert_eq!( + ctx.ext.set_transient_storage(storage_key, Some(vec![3]), false), + Ok(WriteOutcome::Overwritten(overwritten_length)) + ); + assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![3])); + } + exec_success() + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + assert!(ctx + .ext + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) + .is_ok()); + exec_trapped() + }); + + // This one tests passing the input data into a contract via call. + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let 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 ecdsa_to_eth_address_returns_proper_value() { + let bob_ch = MockLoader::insert(Call, |ctx, _| { + let pubkey_compressed = array_bytes::hex2array_unchecked( + "028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91", + ); + assert_eq!( + ctx.ext.ecdsa_to_eth_address(&pubkey_compressed).unwrap(), + array_bytes::hex2array_unchecked::<_, 20>( + "09231da7b19A016f9e576d23B16277062F4d46A8" + ) + ); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_ch); + + let 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![], + None, + ); + assert_matches!(result, Ok(_)); + }); + } +} diff --git a/substrate/frame/revive/src/gas.rs b/substrate/frame/revive/src/gas.rs new file mode 100644 index 0000000000000000000000000000000000000000..2034f39e9bc535ae0a61b914cbfd28c4f97c84f3 --- /dev/null +++ b/substrate/frame/revive/src/gas.rs @@ -0,0 +1,416 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{exec::ExecError, weights::WeightInfo, Config, Error}; +use core::marker::PhantomData; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo}, + weights::Weight, + DefaultNoBound, +}; +use sp_runtime::{traits::Zero, DispatchError}; + +#[cfg(test)] +use std::{any::Any, fmt::Debug}; + +#[derive(Debug, PartialEq, Eq)] +pub struct ChargedAmount(Weight); + +impl ChargedAmount { + pub fn amount(&self) -> Weight { + self.0 + } +} + +/// Meter for syncing the gas between the executor and the gas meter. +#[derive(DefaultNoBound)] +struct EngineMeter { + fuel: u64, + _phantom: PhantomData, +} + +impl EngineMeter { + /// Create a meter with the given fuel limit. + fn new(limit: Weight) -> Self { + Self { + fuel: limit.ref_time().saturating_div(Self::ref_time_per_fuel()), + _phantom: PhantomData, + } + } + + /// Set the fuel left to the given value. + /// Returns the amount of Weight consumed since the last update. + fn set_fuel(&mut self, fuel: u64) -> Weight { + let consumed = self.fuel.saturating_sub(fuel).saturating_mul(Self::ref_time_per_fuel()); + self.fuel = fuel; + Weight::from_parts(consumed, 0) + } + + /// Charge the given amount of gas. + /// Returns the amount of fuel left. + fn charge_ref_time(&mut self, ref_time: u64) -> Result { + let amount = ref_time + .checked_div(Self::ref_time_per_fuel()) + .ok_or(Error::::InvalidSchedule)?; + + self.fuel.checked_sub(amount).ok_or_else(|| Error::::OutOfGas)?; + Ok(Syncable(self.fuel.try_into().map_err(|_| Error::::OutOfGas)?)) + } + + /// How much ref time does each PolkaVM gas correspond to. + fn ref_time_per_fuel() -> u64 { + // We execute 6 different instructions therefore we have to divide the actual + // computed gas costs by 6 to have a rough estimate as to how expensive each + // single executed instruction is going to be. + let instr_cost = T::WeightInfo::instr_i64_load_store(1) + .saturating_sub(T::WeightInfo::instr_i64_load_store(0)) + .ref_time(); + instr_cost / 6 + } +} + +/// Used to capture the gas left before entering a host function. +/// +/// Has to be consumed in order to sync back the gas after leaving the host function. +#[must_use] +pub struct RefTimeLeft(u64); + +/// Resource that needs to be synced to the executor. +/// +/// Wrapped to make sure that the resource will be synced back the the executor. +#[must_use] +pub struct Syncable(polkavm::Gas); + +impl From for polkavm::Gas { + fn from(from: Syncable) -> Self { + from.0 + } +} + +#[cfg(not(test))] +pub trait TestAuxiliaries {} +#[cfg(not(test))] +impl TestAuxiliaries for T {} + +#[cfg(test)] +pub trait TestAuxiliaries: Any + Debug + PartialEq + Eq {} +#[cfg(test)] +impl TestAuxiliaries for T {} + +/// This trait represents a token that can be used for charging `GasMeter`. +/// There is no other way of charging it. +/// +/// Implementing type is expected to be super lightweight hence `Copy` (`Clone` is added +/// for consistency). If inlined there should be no observable difference compared +/// to a hand-written code. +pub trait Token: Copy + Clone + TestAuxiliaries { + /// Return the amount of gas that should be taken by this token. + /// + /// This function should be really lightweight and must not fail. It is not + /// expected that implementors will query the storage or do any kinds of heavy operations. + /// + /// That said, implementors of this function still can run into overflows + /// while calculating the amount. In this case it is ok to use saturating operations + /// since on overflow they will return `max_value` which should consume all gas. + fn weight(&self) -> Weight; + + /// Returns true if this token is expected to influence the lowest gas limit. + fn influence_lowest_gas_limit(&self) -> bool { + true + } +} + +/// A wrapper around a type-erased trait object of what used to be a `Token`. +#[cfg(test)] +pub struct ErasedToken { + pub description: String, + pub token: Box, +} + +#[derive(DefaultNoBound)] +pub struct GasMeter { + gas_limit: Weight, + /// Amount of gas left from initial gas limit. Can reach zero. + gas_left: Weight, + /// Due to `adjust_gas` and `nested` the `gas_left` can temporarily dip below its final value. + gas_left_lowest: Weight, + /// The amount of resources that was consumed by the execution engine. + /// We have to track it separately in order to avoid the loss of precision that happens when + /// converting from ref_time to the execution engine unit. + engine_meter: EngineMeter, + _phantom: PhantomData, + #[cfg(test)] + tokens: Vec, +} + +impl GasMeter { + pub fn new(gas_limit: Weight) -> Self { + GasMeter { + gas_limit, + gas_left: gas_limit, + gas_left_lowest: gas_limit, + engine_meter: EngineMeter::new(gas_limit), + _phantom: PhantomData, + #[cfg(test)] + tokens: Vec::new(), + } + } + + /// Create a new gas meter by removing gas from the current meter. + /// + /// # Note + /// + /// Passing `0` as amount is interpreted as "all remaining gas". + pub fn nested(&mut self, amount: Weight) -> Self { + let amount = Weight::from_parts( + if amount.ref_time().is_zero() { + self.gas_left().ref_time() + } else { + amount.ref_time() + }, + if amount.proof_size().is_zero() { + self.gas_left().proof_size() + } else { + amount.proof_size() + }, + ) + .min(self.gas_left); + self.gas_left -= amount; + GasMeter::new(amount) + } + + /// Absorb the remaining gas of a nested meter after we are done using it. + pub fn absorb_nested(&mut self, nested: Self) { + self.gas_left_lowest = (self.gas_left + nested.gas_limit) + .saturating_sub(nested.gas_required()) + .min(self.gas_left_lowest); + self.gas_left += nested.gas_left; + } + + /// Account for used gas. + /// + /// Amount is calculated by the given `token`. + /// + /// Returns `OutOfGas` if there is not enough gas or addition of the specified + /// amount of gas has lead to overflow. + /// + /// NOTE that amount isn't consumed if there is not enough gas. This is considered + /// safe because we always charge gas before performing any resource-spending action. + #[inline] + pub fn charge>(&mut self, token: Tok) -> Result { + #[cfg(test)] + { + // Unconditionally add the token to the storage. + let erased_tok = + ErasedToken { description: format!("{:?}", token), token: Box::new(token) }; + self.tokens.push(erased_tok); + } + let amount = token.weight(); + // It is OK to not charge anything on failure because we always charge _before_ we perform + // any action + self.gas_left = self.gas_left.checked_sub(&amount).ok_or_else(|| Error::::OutOfGas)?; + Ok(ChargedAmount(amount)) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_gas>(&mut self, charged_amount: ChargedAmount, token: Tok) { + if token.influence_lowest_gas_limit() { + self.gas_left_lowest = self.gas_left_lowest(); + } + let adjustment = charged_amount.0.saturating_sub(token.weight()); + self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit); + } + + /// Hand over the gas metering responsibility from the executor to this meter. + /// + /// Needs to be called when entering a host function to update this meter with the + /// gas that was tracked by the executor. It tracks the latest seen total value + /// in order to compute the delta that needs to be charged. + pub fn sync_from_executor( + &mut self, + engine_fuel: polkavm::Gas, + ) -> Result { + let weight_consumed = self + .engine_meter + .set_fuel(engine_fuel.try_into().map_err(|_| Error::::OutOfGas)?); + self.gas_left + .checked_reduce(weight_consumed) + .ok_or_else(|| Error::::OutOfGas)?; + Ok(RefTimeLeft(self.gas_left.ref_time())) + } + + /// Hand over the gas metering responsibility from this meter to the executor. + /// + /// Needs to be called when leaving a host function in order to calculate how much + /// gas needs to be charged from the **executor**. It updates the last seen executor + /// total value so that it is correct when `sync_from_executor` is called the next time. + /// + /// It is important that this does **not** actually sync with the executor. That has + /// to be done by the caller. + pub fn sync_to_executor(&mut self, before: RefTimeLeft) -> Result { + let ref_time_consumed = before.0.saturating_sub(self.gas_left().ref_time()); + self.engine_meter.charge_ref_time(ref_time_consumed) + } + + /// Returns the amount of gas that is required to run the same call. + /// + /// This can be different from `gas_spent` because due to `adjust_gas` the amount of + /// spent gas can temporarily drop and be refunded later. + pub fn gas_required(&self) -> Weight { + self.gas_limit.saturating_sub(self.gas_left_lowest()) + } + + /// Returns how much gas was spent + pub fn gas_consumed(&self) -> Weight { + self.gas_limit.saturating_sub(self.gas_left) + } + + /// Returns how much gas left from the initial budget. + pub fn gas_left(&self) -> Weight { + self.gas_left + } + + /// The amount of gas in terms of engine gas. + pub fn engine_fuel_left(&self) -> Result { + self.engine_meter.fuel.try_into().map_err(|_| >::OutOfGas.into()) + } + + /// Turn this GasMeter into a DispatchResult that contains the actually used gas. + pub fn into_dispatch_result( + self, + result: Result, + base_weight: Weight, + ) -> DispatchResultWithPostInfo + where + E: Into, + { + let post_info = PostDispatchInfo { + actual_weight: Some(self.gas_consumed().saturating_add(base_weight)), + pays_fee: Default::default(), + }; + + result + .map(|_| post_info) + .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e.into().error }) + } + + fn gas_left_lowest(&self) -> Weight { + self.gas_left_lowest.min(self.gas_left) + } + + #[cfg(test)] + pub fn tokens(&self) -> &[ErasedToken] { + &self.tokens + } +} + +#[cfg(test)] +mod tests { + use super::{GasMeter, Token, Weight}; + use crate::tests::Test; + + /// A simple utility macro that helps to match against a + /// list of tokens. + macro_rules! match_tokens { + ($tokens_iter:ident,) => { + }; + ($tokens_iter:ident, $x:expr, $($rest:tt)*) => { + { + let next = ($tokens_iter).next().unwrap(); + let pattern = $x; + + // Note that we don't specify the type name directly in this macro, + // we only have some expression $x of some type. At the same time, we + // have an iterator of Box and to downcast we need to specify + // the type which we want downcast to. + // + // So what we do is we assign `_pattern_typed_next_ref` to a variable which has + // the required type. + // + // Then we make `_pattern_typed_next_ref = token.downcast_ref()`. This makes + // rustc infer the type `T` (in `downcast_ref`) to be the same as in $x. + + let mut _pattern_typed_next_ref = &pattern; + _pattern_typed_next_ref = match next.token.downcast_ref() { + Some(p) => { + assert_eq!(p, &pattern); + p + } + None => { + panic!("expected type {} got {}", stringify!($x), next.description); + } + }; + } + + match_tokens!($tokens_iter, $($rest)*); + }; + } + + /// A trivial token that charges the specified number of gas units. + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + struct SimpleToken(u64); + impl Token for SimpleToken { + fn weight(&self) -> Weight { + Weight::from_parts(self.0, 0) + } + } + + #[test] + fn it_works() { + let gas_meter = GasMeter::::new(Weight::from_parts(50000, 0)); + assert_eq!(gas_meter.gas_left(), Weight::from_parts(50000, 0)); + } + + #[test] + fn tracing() { + let mut gas_meter = GasMeter::::new(Weight::from_parts(50000, 0)); + assert!(!gas_meter.charge(SimpleToken(1)).is_err()); + + let mut tokens = gas_meter.tokens().iter(); + match_tokens!(tokens, SimpleToken(1),); + } + + // This test makes sure that nothing can be executed if there is no gas. + #[test] + fn refuse_to_execute_anything_if_zero() { + let mut gas_meter = GasMeter::::new(Weight::zero()); + assert!(gas_meter.charge(SimpleToken(1)).is_err()); + } + + // Make sure that the gas meter does not charge in case of overcharge + #[test] + fn overcharge_does_not_charge() { + let mut gas_meter = GasMeter::::new(Weight::from_parts(200, 0)); + + // The first charge is should lead to OOG. + assert!(gas_meter.charge(SimpleToken(300)).is_err()); + + // The gas meter should still contain the full 200. + assert!(gas_meter.charge(SimpleToken(200)).is_ok()); + } + + // Charging the exact amount that the user paid for should be + // possible. + #[test] + fn charge_exact_amount() { + let mut gas_meter = GasMeter::::new(Weight::from_parts(25, 0)); + assert!(!gas_meter.charge(SimpleToken(25)).is_err()); + } +} diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..1cc77a673b1712b6b4a0485b1825310421b44b36 --- /dev/null +++ b/substrate/frame/revive/src/lib.rs @@ -0,0 +1,1239 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![doc = include_str!("../README.md")] +#![allow(rustdoc::private_intra_doc_links)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")] + +extern crate alloc; +mod address; +mod benchmarking; +mod benchmarking_dummy; +mod exec; +mod gas; +mod primitives; +use crate::exec::MomentOf; +use frame_support::traits::IsType; +pub use primitives::*; +use sp_core::U256; + +mod limits; +mod storage; +mod transient_storage; +mod wasm; + +pub mod chain_extension; +pub mod debug; +pub mod test_utils; +pub mod weights; + +#[cfg(test)] +mod tests; +use crate::{ + exec::{AccountIdOf, ExecError, Executable, Ext, Key, Origin, Stack as ExecStack}, + gas::GasMeter, + storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, + wasm::{CodeInfo, RuntimeCosts, WasmBlob}, +}; +use codec::{Codec, Decode, Encode}; +use environmental::*; +use frame_support::{ + dispatch::{ + DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays, + PostDispatchInfo, RawOrigin, + }, + ensure, + traits::{ + fungible::{Inspect, Mutate, MutateHold}, + ConstU32, Contains, EnsureOrigin, Get, Time, + }, + weights::{Weight, WeightMeter}, + BoundedVec, RuntimeDebugNoBound, +}; +use frame_system::{ + ensure_signed, + pallet_prelude::{BlockNumberFor, OriginFor}, + EventRecord, Pallet as System, +}; +use scale_info::TypeInfo; +use sp_core::{H160, H256}; +use sp_runtime::{ + traits::{BadOrigin, Convert, Dispatchable, Saturating}, + DispatchError, +}; + +pub use crate::{ + address::{AddressMapper, DefaultAddressMapper}, + debug::Tracing, + pallet::*, +}; +pub use weights::WeightInfo; + +#[cfg(doc)] +pub use crate::wasm::SyscallDoc; + +type TrieId = BoundedVec>; +type BalanceOf = + <::Currency as Inspect<::AccountId>>::Balance; +type CodeVec = BoundedVec::MaxCodeLen>; +type EventRecordOf = + EventRecord<::RuntimeEvent, ::Hash>; +type DebugBuffer = BoundedVec>; + +/// Used as a sentinel value when reading and writing contract memory. +/// +/// It is usually used to signal `None` to a contract when only a primitive is allowed +/// and we don't want to go through encoding a full Rust type. Using `u32::Max` is a safe +/// sentinel because contracts are never allowed to use such a large amount of resources +/// that this value makes sense for a memory location or length. +const SENTINEL: u32 = u32::MAX; + +/// The target that is used for the log output emitted by this crate. +/// +/// Hence you can use this target to selectively increase the log level for this crate. +/// +/// Example: `RUST_LOG=runtime::revive=debug my_code --dev` +const LOG_TARGET: &str = "runtime::revive"; + +/// This version determines which syscalls are available to contracts. +/// +/// Needs to be bumped every time a versioned syscall is added. +const API_VERSION: u16 = 0; + +#[test] +fn api_version_up_to_date() { + assert!( + API_VERSION == crate::wasm::HIGHEST_API_VERSION, + "A new versioned API has been added. The `API_VERSION` needs to be bumped." + ); +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use crate::debug::Debugger; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_core::U256; + use sp_runtime::Perbill; + + /// The in-code storage version. + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::config(with_default)] + pub trait Config: frame_system::Config { + /// The time implementation used to supply timestamps to contracts through `seal_now`. + type Time: Time; + + /// The fungible in which fees are paid and contract balances are held. + #[pallet::no_default] + type Currency: Inspect + + Mutate + + MutateHold; + + /// The overarching event type. + #[pallet::no_default_bounds] + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The overarching call type. + #[pallet::no_default_bounds] + type RuntimeCall: Dispatchable + + GetDispatchInfo + + codec::Decode + + IsType<::RuntimeCall>; + + /// Overarching hold reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: From; + + /// Filter that is applied to calls dispatched by contracts. + /// + /// Use this filter to control which dispatchables are callable by contracts. + /// This is applied in **addition** to [`frame_system::Config::BaseCallFilter`]. + /// It is recommended to treat this as a whitelist. + /// + /// # Stability + /// + /// The runtime **must** make sure that all dispatchables that are callable by + /// contracts remain stable. In addition [`Self::RuntimeCall`] itself must remain stable. + /// This means that no existing variants are allowed to switch their positions. + /// + /// # Note + /// + /// Note that dispatchables that are called via contracts do not spawn their + /// own wasm instance for each call (as opposed to when called via a transaction). + /// Therefore please make sure to be restrictive about which dispatchables are allowed + /// in order to not introduce a new DoS vector like memory allocation patterns that can + /// be exploited to drive the runtime into a panic. + /// + /// This filter does not apply to XCM transact calls. To impose restrictions on XCM transact + /// calls, you must configure them separately within the XCM pallet itself. + #[pallet::no_default_bounds] + type CallFilter: Contains<::RuntimeCall>; + + /// Used to answer contracts' queries regarding the current weight price. This is **not** + /// used to calculate the actual fee and is only for informational purposes. + #[pallet::no_default_bounds] + type WeightPrice: Convert>; + + /// Describes the weights of the dispatchables of this module and is also used to + /// construct a default cost schedule. + type WeightInfo: WeightInfo; + + /// Type that allows the runtime authors to add new host functions for a contract to call. + #[pallet::no_default_bounds] + type ChainExtension: chain_extension::ChainExtension + Default; + + /// The amount of balance a caller has to pay for each byte of storage. + /// + /// # Note + /// + /// It is safe to change this value on a live chain as all refunds are pro rata. + #[pallet::constant] + #[pallet::no_default_bounds] + type DepositPerByte: Get>; + + /// The amount of balance a caller has to pay for each storage item. + /// + /// # Note + /// + /// It is safe to change this value on a live chain as all refunds are pro rata. + #[pallet::constant] + #[pallet::no_default_bounds] + type DepositPerItem: Get>; + + /// The percentage of the storage deposit that should be held for using a code hash. + /// Instantiating a contract, or calling [`chain_extension::Ext::lock_delegate_dependency`] + /// protects the code from being removed. In order to prevent abuse these actions are + /// protected with a percentage of the code deposit. + #[pallet::constant] + type CodeHashLockupDepositPercent: Get; + + /// Only valid type is [`DefaultAddressMapper`]. + #[pallet::no_default_bounds] + type AddressMapper: AddressMapper>; + + /// The maximum length of a contract code in bytes. + /// + /// This value hugely affects the memory requirements of this pallet since all the code of + /// all contracts on the call stack will need to be held in memory. Setting of a correct + /// value will be enforced in [`Pallet::integrity_test`]. + #[pallet::constant] + type MaxCodeLen: Get; + + /// Make contract callable functions marked as `#[unstable]` available. + /// + /// Contracts that use `#[unstable]` functions won't be able to be uploaded unless + /// this is set to `true`. This is only meant for testnets and dev nodes in order to + /// experiment with new features. + /// + /// # Warning + /// + /// Do **not** set to `true` on productions chains. + #[pallet::constant] + type UnsafeUnstableInterface: Get; + + /// Origin allowed to upload code. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to upload contract + /// code. + #[pallet::no_default_bounds] + type UploadOrigin: EnsureOrigin; + + /// Origin allowed to instantiate code. + /// + /// # Note + /// + /// This is not enforced when a contract instantiates another contract. The + /// [`Self::UploadOrigin`] should make sure that no code is deployed that does unwanted + /// instantiations. + /// + /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to instantiate + /// contract code. + #[pallet::no_default_bounds] + type InstantiateOrigin: EnsureOrigin; + + /// For most production chains, it's recommended to use the `()` implementation of this + /// trait. This implementation offers additional logging when the log target + /// "runtime::revive" is set to trace. + #[pallet::no_default_bounds] + type Debug: Debugger; + + /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and + /// execute XCM programs. + #[pallet::no_default_bounds] + type Xcm: xcm_builder::Controller< + OriginFor, + ::RuntimeCall, + BlockNumberFor, + >; + + /// The amount of memory in bytes that parachain nodes 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 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. + /// + /// This value is usually higher than [`Self::RuntimeMemory`] to account for the fact + /// that validators have to hold all storage items in PvF memory. + type PVFMemory: Get; + } + + /// Container for different types that implement [`DefaultConfig`]` of this pallet. + pub mod config_preludes { + use super::*; + use frame_support::{ + derive_impl, + traits::{ConstBool, ConstU32}, + }; + use frame_system::EnsureSigned; + use sp_core::parameter_types; + + type AccountId = sp_runtime::AccountId32; + type Balance = u64; + const UNITS: Balance = 10_000_000_000; + const CENTS: Balance = UNITS / 100; + + pub const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS + } + + parameter_types! { + pub const DepositPerItem: Balance = deposit(1, 0); + pub const DepositPerByte: Balance = deposit(0, 1); + pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); + } + + /// A type providing default configurations for this pallet in testing environment. + pub struct TestDefaultConfig; + + impl Time for TestDefaultConfig { + type Moment = u64; + fn now() -> Self::Moment { + unimplemented!("No default `now` implementation in `TestDefaultConfig` provide a custom `T::Time` type.") + } + } + + impl> Convert for TestDefaultConfig { + fn convert(w: Weight) -> T { + w.ref_time().into() + } + } + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)] + impl frame_system::DefaultConfig for TestDefaultConfig {} + + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + #[inject_runtime_type] + type RuntimeEvent = (); + + #[inject_runtime_type] + type RuntimeHoldReason = (); + + #[inject_runtime_type] + type RuntimeCall = (); + type AddressMapper = DefaultAddressMapper; + type CallFilter = (); + type ChainExtension = (); + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type Time = Self; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type WeightInfo = (); + type WeightPrice = Self; + type Debug = (); + type Xcm = (); + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + } + } + + #[pallet::event] + pub enum Event { + /// Contract deployed by address at the specified address. + Instantiated { deployer: H160, contract: H160 }, + + /// Contract has been removed. + /// + /// # Note + /// + /// The only way for a contract to be removed and emitting this event is by calling + /// `seal_terminate`. + Terminated { + /// The contract that was terminated. + contract: H160, + /// The account that received the contracts remaining balance + beneficiary: H160, + }, + + /// Code with the specified hash has been stored. + CodeStored { code_hash: H256, deposit_held: BalanceOf, uploader: H160 }, + + /// A custom event emitted by the contract. + ContractEmitted { + /// The contract that emitted the event. + 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: H256, deposit_released: BalanceOf, remover: H160 }, + + /// A contract's code was updated. + ContractCodeUpdated { + /// The contract that has been updated. + contract: H160, + /// New code hash that was set for the contract. + new_code_hash: H256, + /// Previous code hash of the contract. + old_code_hash: H256, + }, + + /// A contract was called either by a plain account or another contract. + /// + /// # Note + /// + /// Please keep in mind that like all events this is only emitted for successful + /// calls. This is because on failure all storage changes including events are + /// rolled back. + Called { + /// The caller of the `contract`. + caller: Origin, + /// The contract that was called. + contract: H160, + }, + + /// A contract delegate called a code hash. + /// + /// # Note + /// + /// Please keep in mind that like all events this is only emitted for successful + /// calls. This is because on failure all storage changes including events are + /// rolled back. + DelegateCalled { + /// The contract that performed the delegate call and hence in whose context + /// the `code_hash` is executed. + contract: H160, + /// The code hash that was delegate called. + code_hash: H256, + }, + + /// Some funds have been transferred and held as storage deposit. + StorageDepositTransferredAndHeld { from: H160, to: H160, amount: BalanceOf }, + + /// Some storage deposit funds have been transferred and released. + StorageDepositTransferredAndReleased { from: H160, to: H160, amount: BalanceOf }, + } + + #[pallet::error] + pub enum Error { + /// Invalid schedule supplied, e.g. with zero weight of a basic operation. + InvalidSchedule, + /// Invalid combination of flags supplied to `seal_call` or `seal_delegate_call`. + InvalidCallFlags, + /// The executed contract exhausted its gas limit. + OutOfGas, + /// The output buffer supplied to a contract API call was too small. + OutputBufferTooSmall, + /// Performing the requested transfer failed. Probably because there isn't enough + /// free balance in the sender's account. + TransferFailed, + /// Performing a call was denied because the calling depth reached the limit + /// of what is specified in the schedule. + MaxCallDepthReached, + /// No contract was found at the specified address. + ContractNotFound, + /// The code supplied to `instantiate_with_code` exceeds the limit specified in the + /// current schedule. + CodeTooLarge, + /// No code could be found at the supplied code hash. + CodeNotFound, + /// No code info could be found at the supplied code hash. + CodeInfoNotFound, + /// A buffer outside of sandbox memory was passed to a contract API function. + OutOfBounds, + /// Input passed to a contract API function failed to decode as expected type. + DecodingFailed, + /// Contract trapped during execution. + ContractTrapped, + /// The size defined in `T::MaxValueSize` was exceeded. + ValueTooLarge, + /// Termination of a contract is not allowed while the contract is already + /// on the call stack. Can be triggered by `seal_terminate`. + TerminatedWhileReentrant, + /// `seal_call` forwarded this contracts input. It therefore is no longer available. + InputForwarded, + /// The amount of topics passed to `seal_deposit_events` exceeds the limit. + TooManyTopics, + /// The chain does not provide a chain extension. Calling the chain extension results + /// in this error. Note that this usually shouldn't happen as deploying such contracts + /// is rejected. + NoChainExtension, + /// Failed to decode the XCM program. + XCMDecodeFailed, + /// A contract with the same AccountId already exists. + DuplicateContract, + /// A contract self destructed in its constructor. + /// + /// This can be triggered by a call to `seal_terminate`. + TerminatedInConstructor, + /// A call tried to invoke a contract that is flagged as non-reentrant. + ReentranceDenied, + /// A contract called into the runtime which then called back into this pallet. + ReenteredPallet, + /// A contract attempted to invoke a state modifying API while being in read-only mode. + StateChangeDenied, + /// Origin doesn't have enough balance to pay the required storage deposits. + StorageDepositNotEnoughFunds, + /// More storage was created than allowed by the storage deposit limit. + StorageDepositLimitExhausted, + /// Code removal was denied because the code is still in use by at least one contract. + CodeInUse, + /// The contract ran to completion but decided to revert its storage changes. + /// Please note that this error is only returned from extrinsics. When called directly + /// or via RPC an `Ok` will be returned. In this case the caller needs to inspect the flags + /// to determine whether a reversion has taken place. + ContractReverted, + /// The contract failed to compile or is missing the correct entry points. + /// + /// A more detailed error can be found on the node console if debug messages are enabled + /// by supplying `-lruntime::revive=debug`. + CodeRejected, + /// The contract has reached its maximum number of delegate dependencies. + MaxDelegateDependenciesReached, + /// The dependency was not found in the contract's delegate dependencies. + DelegateDependencyNotFound, + /// The contract already depends on the given delegate dependency. + DelegateDependencyAlreadyExists, + /// Can not add a delegate dependency to the code hash of the contract itself. + CannotAddSelfAsDelegateDependency, + /// Can not add more data to transient storage. + OutOfTransientStorage, + /// The contract tried to call a syscall which does not exist (at its current api level). + InvalidSyscall, + /// Invalid storage flags were passed to one of the storage syscalls. + InvalidStorageFlags, + /// PolkaVM failed during code execution. Probably due to a malformed program. + ExecutionFailed, + /// Failed to convert a U256 to a Balance. + BalanceConversionFailed, + } + + /// A reason for the pallet contracts placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The Pallet has reserved it for storing code on-chain. + CodeUploadDepositReserve, + /// The Pallet has reserved it for storage deposit. + StorageDepositReserve, + } + + /// A mapping from a contract's code hash to its code. + #[pallet::storage] + pub(crate) type PristineCode = StorageMap<_, Identity, H256, CodeVec>; + + /// A mapping from a contract's code hash to its code info. + #[pallet::storage] + pub(crate) type CodeInfoOf = StorageMap<_, Identity, H256, CodeInfo>; + + /// The code associated with a given account. + #[pallet::storage] + pub(crate) type ContractInfoOf = StorageMap<_, Identity, H160, ContractInfo>; + + /// Evicted contracts that await child trie deletion. + /// + /// Child trie deletion is a heavy operation depending on the amount of storage items + /// stored in said trie. Therefore this operation is performed lazily in `on_idle`. + #[pallet::storage] + pub(crate) type DeletionQueue = StorageMap<_, Twox64Concat, u32, TrieId>; + + /// A pair of monotonic counters used to track the latest contract marked for deletion + /// and the latest deleted contract in queue. + #[pallet::storage] + pub(crate) type DeletionQueueCounter = + StorageValue<_, DeletionQueueManager, ValueQuery>; + + #[pallet::extra_constants] + impl Pallet { + #[pallet::constant_name(ApiVersion)] + fn api_version() -> u16 { + API_VERSION + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_idle(_block: BlockNumberFor, limit: Weight) -> Weight { + let mut meter = WeightMeter::with_limit(limit); + ContractInfo::::process_deletion_queue_batch(&mut meter); + meter.consumed() + } + + fn integrity_test() { + // Total runtime memory limit + let max_runtime_mem: u32 = T::RuntimeMemory::get(); + // Memory limits for a single contract: + // Value stack size: 1Mb per contract, default defined in wasmi + const MAX_STACK_SIZE: u32 = 1024 * 1024; + // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract + let max_heap_size = limits::MEMORY_BYTES; + // The root frame is not accounted in CALL_STACK_DEPTH + let max_call_depth = + limits::CALL_STACK_DEPTH.checked_add(1).expect("CallStack size is too big"); + // Transient storage uses a BTreeMap, which has overhead compared to the raw size of + // key-value data. To ensure safety, a margin of 2x the raw key-value size is used. + let max_transient_storage_size = limits::TRANSIENT_STORAGE_BYTES + .checked_mul(2) + .expect("MaxTransientStorageSize is too large"); + + // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. + // + // In worst case, the decoded Wasm contract code would be `x16` times larger than the + // encoded one. This is because even a single-byte wasm instruction has 16-byte size in + // wasmi. This gives us `MaxCodeLen*16` safety margin. + // + // Next, the pallet keeps the Wasm blob for each + // contract, hence we add up `MaxCodeLen` to the safety margin. + // + // The inefficiencies of the freeing-bump allocator + // being used in the client for the runtime memory allocations, could lead to possible + // memory allocations for contract code grow up to `x4` times in some extreme cases, + // which gives us total multiplier of `17*4` for `MaxCodeLen`. + // + // That being said, for every contract executed in runtime, at least `MaxCodeLen*17*4` + // memory should be available. Note that maximum allowed heap memory and stack size per + // each contract (stack frame) should also be counted. + // + // The pallet holds transient storage with a size up to `max_transient_storage_size`. + // + // Finally, we allow 50% of the runtime memory to be utilized by the contracts call + // stack, keeping the rest for other facilities, such as PoV, etc. + // + // This gives us the following formula: + // + // `(MaxCodeLen * 17 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth + + // max_transient_storage_size < max_runtime_mem/2` + // + // Hence the upper limit for the `MaxCodeLen` can be defined as follows: + let code_len_limit = max_runtime_mem + .saturating_div(2) + .saturating_sub(max_transient_storage_size) + .saturating_div(max_call_depth) + .saturating_sub(max_heap_size) + .saturating_sub(MAX_STACK_SIZE) + .saturating_div(17 * 4); + + assert!( + T::MaxCodeLen::get() < code_len_limit, + "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ + (current value is {:?}), to avoid possible runtime oom issues.", + max_call_depth, + code_len_limit, + T::MaxCodeLen::get(), + ); + + // Validators are configured to be able to use more memory than block builders. This is + // because in addition to `max_runtime_mem` they need to hold additional data in + // memory: PoV in multiple copies (1x encoded + 2x decoded) and all storage which + // includes emitted events. The assumption is that storage/events size + // can be a maximum of half of the validator runtime memory - max_runtime_mem. + let max_block_ref_time = T::BlockWeights::get() + .get(DispatchClass::Normal) + .max_total + .unwrap_or_else(|| T::BlockWeights::get().max_block) + .ref_time(); + let max_payload_size = limits::PAYLOAD_BYTES; + let max_key_size = + Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize]) + .expect("Key of maximal size shall be created") + .hash() + .len() as u32; + + // We can use storage to store items using the available block ref_time with the + // `set_storage` host function. + let max_storage_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::SetStorage { + new_bytes: max_payload_size, + old_bytes: 0, + }) + .ref_time())) + .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64)) + .try_into() + .expect("Storage size too big"); + + let max_pvf_mem: u32 = T::PVFMemory::get(); + let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2; + + assert!( + max_storage_size < storage_size_limit, + "Maximal storage size {} exceeds the storage limit {}", + max_storage_size, + storage_size_limit + ); + + // We can use storage to store events using the available block ref_time with the + // `deposit_event` host function. The overhead of stored events, which is around 100B, + // is not taken into account to simplify calculations, as it does not change much. + let max_events_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::DepositEvent { + num_topic: 0, + len: max_payload_size, + }) + .ref_time())) + .saturating_mul(max_payload_size as u64)) + .try_into() + .expect("Events size too big"); + + assert!( + max_events_size < storage_size_limit, + "Maximal events size {} exceeds the events limit {}", + max_events_size, + storage_size_limit + ); + } + } + + #[pallet::call] + impl Pallet + where + BalanceOf: Into + TryFrom, + MomentOf: Into, + { + /// Makes a call to an account, optionally transferring some balance. + /// + /// # Parameters + /// + /// * `dest`: Address of the contract to call. + /// * `value`: The balance to transfer from the `origin` to `dest`. + /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `storage_deposit_limit`: The maximum amount of balance that can be charged from the + /// caller to pay for the storage consumed. + /// * `data`: The input data to pass to the contract. + /// + /// * If the account is a smart-contract account, the associated code will be + /// executed and any value will be transferred. + /// * If the account is a regular account, any value will be transferred. + /// * If no account exists and the call value is not less than `existential_deposit`, + /// a regular account will be created and any value will be transferred. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))] + pub fn call( + origin: OriginFor, + dest: H160, + #[pallet::compact] value: BalanceOf, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + data: Vec, + ) -> DispatchResultWithPostInfo { + let mut output = Self::bare_call( + origin, + dest, + value, + gas_limit, + storage_deposit_limit, + data, + DebugInfo::Skip, + CollectEvents::Skip, + ); + if let Ok(return_value) = &output.result { + if return_value.did_revert() { + output.result = Err(>::ContractReverted.into()); + } + } + dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call()) + } + + /// Instantiates a contract from a previously deployed wasm binary. + /// + /// This function is identical to [`Self::instantiate_with_code`] but without the + /// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary + /// must be supplied. + #[pallet::call_index(1)] + #[pallet::weight( + T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit) + )] + pub fn instantiate( + origin: OriginFor, + #[pallet::compact] value: BalanceOf, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + code_hash: sp_core::H256, + data: Vec, + salt: Option<[u8; 32]>, + ) -> DispatchResultWithPostInfo { + let data_len = data.len() as u32; + let mut output = Self::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Existing(code_hash), + data, + salt, + DebugInfo::Skip, + CollectEvents::Skip, + ); + if let Ok(retval) = &output.result { + if retval.result.did_revert() { + output.result = Err(>::ContractReverted.into()); + } + } + dispatch_result( + output.result.map(|result| result.result), + output.gas_consumed, + T::WeightInfo::instantiate(data_len), + ) + } + + /// Instantiates a new contract from the supplied `code` optionally transferring + /// some balance. + /// + /// This dispatchable has the same effect as calling [`Self::upload_code`] + + /// [`Self::instantiate`]. Bundling them together provides efficiency gains. Please + /// also check the documentation of [`Self::upload_code`]. + /// + /// # Parameters + /// + /// * `value`: The balance to transfer from the `origin` to the newly created contract. + /// * `gas_limit`: The gas limit enforced when executing the constructor. + /// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved + /// from the caller to pay for the storage consumed. + /// * `code`: The contract code to deploy in raw bytes. + /// * `data`: The input data to pass to the contract constructor. + /// * `salt`: Used for the address derivation. If `Some` is supplied then `CREATE2` + /// semantics are used. If `None` then `CRATE1` is used. + /// + /// + /// Instantiation is executed as follows: + /// + /// - The supplied `code` is deployed, and a `code_hash` is created for that code. + /// - If the `code_hash` already exists on the chain the underlying `code` will be shared. + /// - The destination address is computed based on the sender, code_hash and the salt. + /// - The smart-contract account is created at the computed address. + /// - The `value` is transferred to the new account. + /// - The `deploy` function is executed in the context of the newly-created account. + #[pallet::call_index(2)] + #[pallet::weight( + T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32) + .saturating_add(*gas_limit) + )] + pub fn instantiate_with_code( + origin: OriginFor, + #[pallet::compact] value: BalanceOf, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + code: Vec, + data: Vec, + salt: Option<[u8; 32]>, + ) -> DispatchResultWithPostInfo { + let code_len = code.len() as u32; + let data_len = data.len() as u32; + let mut output = Self::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Upload(code), + data, + salt, + DebugInfo::Skip, + CollectEvents::Skip, + ); + if let Ok(retval) = &output.result { + if retval.result.did_revert() { + output.result = Err(>::ContractReverted.into()); + } + } + dispatch_result( + output.result.map(|result| result.result), + output.gas_consumed, + T::WeightInfo::instantiate_with_code(code_len, data_len), + ) + } + + /// Upload new `code` without instantiating a contract from it. + /// + /// If the code does not already exist a deposit is reserved from the caller + /// and unreserved only when [`Self::remove_code`] is called. The size of the reserve + /// depends on the size of the supplied `code`. + /// + /// # Note + /// + /// Anyone can instantiate a contract from any uploaded code and thus prevent its removal. + /// To avoid this situation a constructor could employ access control so that it can + /// only be instantiated by permissioned entities. The same is true when uploading + /// through [`Self::instantiate_with_code`]. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::upload_code_determinism_enforced(code.len() as u32))] + pub fn upload_code( + origin: OriginFor, + code: Vec, + #[pallet::compact] storage_deposit_limit: BalanceOf, + ) -> DispatchResult { + Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ()) + } + + /// Remove the code stored under `code_hash` and refund the deposit to its owner. + /// + /// A code can only be removed by its original uploader (its owner) and only if it is + /// not used by any contract. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::remove_code())] + pub fn remove_code( + origin: OriginFor, + code_hash: sp_core::H256, + ) -> DispatchResultWithPostInfo { + let origin = ensure_signed(origin)?; + >::remove(&origin, code_hash)?; + // we waive the fee because removing unused code is beneficial + Ok(Pays::No.into()) + } + + /// Privileged function that changes the code of an existing contract. + /// + /// This takes care of updating refcounts and all other necessary operations. Returns + /// an error if either the `code_hash` or `dest` do not exist. + /// + /// # Note + /// + /// This does **not** change the address of the contract in question. This means + /// that the contract address is no longer derived from its code hash after calling + /// this dispatchable. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::set_code())] + pub fn set_code( + origin: OriginFor, + dest: H160, + code_hash: sp_core::H256, + ) -> DispatchResult { + ensure_root(origin)?; + >::try_mutate(&dest, |contract| { + let contract = if let Some(contract) = contract { + contract + } else { + return Err(>::ContractNotFound.into()) + }; + >>::increment_refcount(code_hash)?; + >>::decrement_refcount(contract.code_hash); + Self::deposit_event(Event::ContractCodeUpdated { + contract: dest, + new_code_hash: code_hash, + old_code_hash: contract.code_hash, + }); + contract.code_hash = code_hash; + Ok(()) + }) + } + } +} + +/// Create a dispatch result reflecting the amount of consumed gas. +fn dispatch_result( + result: Result, + gas_consumed: Weight, + base_weight: Weight, +) -> DispatchResultWithPostInfo { + let post_info = PostDispatchInfo { + actual_weight: Some(gas_consumed.saturating_add(base_weight)), + pays_fee: Default::default(), + }; + + result + .map(|_| post_info) + .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e }) +} + +impl Pallet +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 + /// runtime as opposed to from an extrinsic. It returns more information and allows the + /// enablement of features that are not suitable for an extrinsic (debugging, event + /// collection). + pub fn bare_call( + origin: OriginFor, + dest: H160, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + data: Vec, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractExecResult, EventRecordOf> { + let mut gas_meter = GasMeter::new(gas_limit); + let mut storage_deposit = Default::default(); + let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) { + Some(DebugBuffer::default()) + } else { + None + }; + let try_call = || { + let origin = Origin::from_runtime_origin(origin)?; + let mut storage_meter = StorageMeter::new(&origin, storage_deposit_limit, value)?; + let result = ExecStack::>::run_call( + origin.clone(), + dest, + &mut gas_meter, + &mut storage_meter, + value, + data, + debug_message.as_mut(), + )?; + storage_deposit = storage_meter.try_into_deposit(&origin)?; + Ok(result) + }; + let result = Self::run_guarded(try_call); + let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { + Some(System::::read_events_no_consensus().map(|e| *e).collect()) + } else { + None + }; + ContractExecResult { + result: result.map_err(|r| r.error), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), + storage_deposit, + debug_message: debug_message.unwrap_or_default().to_vec(), + events, + } + } + + /// A generalized version of [`Self::instantiate`] or [`Self::instantiate_with_code`]. + /// + /// Identical to [`Self::instantiate`] or [`Self::instantiate_with_code`] but tailored towards + /// being called by other code within the runtime as opposed to from an extrinsic. It returns + /// more information and allows the enablement of features that are not suitable for an + /// extrinsic (debugging, event collection). + pub fn bare_instantiate( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + mut storage_deposit_limit: BalanceOf, + code: Code, + data: Vec, + salt: Option<[u8; 32]>, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractInstantiateResult, EventRecordOf> { + let mut gas_meter = GasMeter::new(gas_limit); + let mut storage_deposit = Default::default(); + let mut debug_message = + if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None }; + let try_instantiate = || { + let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?; + let (executable, upload_deposit) = match code { + Code::Upload(code) => { + let upload_account = T::UploadOrigin::ensure_origin(origin)?; + let (executable, upload_deposit) = Self::try_upload_code( + upload_account, + code, + storage_deposit_limit, + debug_message.as_mut(), + )?; + storage_deposit_limit.saturating_reduce(upload_deposit); + (executable, upload_deposit) + }, + Code::Existing(code_hash) => + (WasmBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()), + }; + let instantiate_origin = Origin::from_account_id(instantiate_account.clone()); + let mut storage_meter = + StorageMeter::new(&instantiate_origin, storage_deposit_limit, value)?; + let result = ExecStack::>::run_instantiate( + instantiate_account, + executable, + &mut gas_meter, + &mut storage_meter, + value, + data, + salt.as_ref(), + debug_message.as_mut(), + ); + storage_deposit = storage_meter + .try_into_deposit(&instantiate_origin)? + .saturating_add(&StorageDeposit::Charge(upload_deposit)); + result + }; + let output = Self::run_guarded(try_instantiate); + let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { + Some(System::::read_events_no_consensus().map(|e| *e).collect()) + } else { + None + }; + ContractInstantiateResult { + result: output + .map(|(addr, result)| InstantiateReturnValue { result, addr }) + .map_err(|e| e.error), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), + storage_deposit, + debug_message: debug_message.unwrap_or_default().to_vec(), + events, + } + } + + /// A generalized version of [`Self::upload_code`]. + /// + /// It is identical to [`Self::upload_code`] and only differs in the information it returns. + pub fn bare_upload_code( + origin: OriginFor, + code: Vec, + storage_deposit_limit: BalanceOf, + ) -> CodeUploadResult> { + let origin = T::UploadOrigin::ensure_origin(origin)?; + let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, None)?; + Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }) + } + + /// Query storage of a specified contract under a specified key. + pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult { + let contract_info = + ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; + + let maybe_value = contract_info.read(&Key::from_fixed(key)); + Ok(maybe_value) + } + + /// Uploads new code and returns the Wasm blob and deposit amount collected. + fn try_upload_code( + origin: T::AccountId, + code: Vec, + storage_deposit_limit: BalanceOf, + mut debug_message: Option<&mut DebugBuffer>, + ) -> Result<(WasmBlob, BalanceOf), DispatchError> { + let mut module = WasmBlob::from_code(code, origin).map_err(|(err, msg)| { + debug_message.as_mut().map(|d| d.try_extend(msg.bytes())); + err + })?; + let deposit = module.store_code()?; + ensure!(storage_deposit_limit >= deposit, >::StorageDepositLimitExhausted); + Ok((module, deposit)) + } + + /// Run the supplied function `f` if no other instance of this pallet is on the stack. + fn run_guarded Result>(f: F) -> Result { + executing_contract::using_once(&mut false, || { + executing_contract::with(|f| { + // Fail if already entered contract execution + if *f { + return Err(()) + } + // We are entering contract execution + *f = true; + Ok(()) + }) + .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed") + .map_err(|_| >::ReenteredPallet.into()) + .map(|_| f()) + .and_then(|r| r) + }) + } +} + +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 + AccountId: Codec, + Balance: Codec, + BlockNumber: Codec, + EventRecord: Codec, + { + /// Perform a call from a specified account to a given contract. + /// + /// See [`crate::Pallet::bare_call`]. + fn call( + origin: AccountId, + dest: H160, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> ContractExecResult; + + /// Instantiate a new contract. + /// + /// See `[crate::Pallet::bare_instantiate]`. + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: Code, + data: Vec, + salt: Option<[u8; 32]>, + ) -> ContractInstantiateResult; + + /// Upload new code without instantiating a contract from it. + /// + /// See [`crate::Pallet::bare_upload_code`]. + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + ) -> CodeUploadResult; + + /// Query a given storage key in a given contract. + /// + /// Returns `Ok(Some(Vec))` if the storage value exists under the given key in the + /// specified account and `Ok(None)` if it doesn't. If the account specified by the address + /// doesn't exist, or doesn't have a contract then `Err` is returned. + fn get_storage( + address: H160, + key: [u8; 32], + ) -> GetStorageResult; + } +} diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a714a89d4869b0712e21f97a5e2cd2502684bb3 --- /dev/null +++ b/substrate/frame/revive/src/limits.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Limits that are observeable by contract code. +//! +//! It is important to never change this limits without supporting the old limits +//! for already deployed contracts. This is what the [`crate::Contract::behaviour_version`] +//! is meant for. This is true for either increasing or decreasing the limit. +//! +//! Limits in this file are different from the limits configured on the [`Config`] trait which are +//! generally only affect actions that cannot be performed by a contract: For example, uploading new +//! code only be done via a transaction but not by a contract. Hence the maximum contract size can +//! be raised (but not lowered) by the runtime configuration. + +/// The maximum depth of the call stack. +/// +/// A 0 means that no callings of other contracts are possible. In other words only the origin +/// called "root contract" is allowed to execute then. +pub const CALL_STACK_DEPTH: u32 = 5; + +/// The maximum number of topics a call to [`crate::SyscallDoc::deposit_event`] can emit. +/// +/// We set it to the same limit that ethereum has. It is unlikely to change. +pub const NUM_EVENT_TOPICS: u32 = 4; + +/// The maximum number of code hashes a contract can lock. +pub const DELEGATE_DEPENDENCIES: u32 = 32; + +/// How much memory do we allow the contract to allocate. +pub const MEMORY_BYTES: u32 = 16 * 64 * 1024; + +/// Maximum size of events (excluding topics) and storage values. +pub const PAYLOAD_BYTES: u32 = 512; + +/// The maximum size of the transient storage in bytes. +/// +/// This includes keys, values, and previous entries used for storage rollback. +pub const TRANSIENT_STORAGE_BYTES: u32 = 4 * 1024; + +/// The maximum allowable length in bytes for (transient) storage keys. +pub const STORAGE_KEY_BYTES: u32 = 128; + +/// The maximum size of the debug buffer contracts can write messages to. +/// +/// The buffer will always be disabled for on-chain execution. +pub const DEBUG_BUFFER_BYTES: u32 = 2 * 1024 * 1024; diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs new file mode 100644 index 0000000000000000000000000000000000000000..67bc144c3dd233924928695afdbe5a5057ce46a9 --- /dev/null +++ b/substrate/frame/revive/src/primitives.rs @@ -0,0 +1,283 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A crate that hosts a common definitions that are relevant for the pallet-revive. + +use crate::H160; +use alloc::vec::Vec; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::weights::Weight; +use pallet_revive_uapi::ReturnFlags; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchError, RuntimeDebug, +}; + +/// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and +/// `ContractsApi::instantiate`. +/// +/// It contains the execution result together with some auxiliary information. +/// +/// #Note +/// +/// It has been extended to include `events` at the end of the struct while not bumping the +/// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data +/// should be ignored to avoid any potential compatibility issues. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ContractResult { + /// How much weight was consumed during execution. + pub gas_consumed: Weight, + /// How much weight is required as gas limit in order to execute this call. + /// + /// This value should be used to determine the weight limit for on-chain execution. + /// + /// # Note + /// + /// This can only different from [`Self::gas_consumed`] when weight pre charging + /// is used. Currently, only `seal_call_runtime` makes use of pre charging. + /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging + /// when a non-zero `gas_limit` argument is supplied. + pub gas_required: Weight, + /// How much balance was paid by the origin into the contract's deposit account in order to + /// pay for storage. + /// + /// The storage deposit is never actually charged from the origin in case of [`Self::result`] + /// is `Err`. This is because on error all storage changes are rolled back including the + /// payment of the deposit. + pub storage_deposit: StorageDeposit, + /// An optional debug message. This message is only filled when explicitly requested + /// by the code that calls into the contract. Otherwise it is empty. + /// + /// The contained bytes are valid UTF-8. This is not declared as `String` because + /// this type is not allowed within the runtime. + /// + /// Clients should not make any assumptions about the format of the buffer. + /// They should just display it as-is. It is **not** only a collection of log lines + /// provided by a contract but a formatted buffer with different sections. + /// + /// # Note + /// + /// The debug message is never generated during on-chain execution. It is reserved for + /// RPC calls. + pub debug_message: Vec, + /// The execution result of the wasm code. + pub result: R, + /// The events that were emitted during execution. It is an option as event collection is + /// optional. + pub events: Option>, +} + +/// Result type of a `bare_call` call as well as `ContractsApi::call`. +pub type ContractExecResult = + ContractResult, Balance, EventRecord>; + +/// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`. +pub type ContractInstantiateResult = + ContractResult, Balance, EventRecord>; + +/// Result type of a `bare_code_upload` call. +pub type CodeUploadResult = Result, DispatchError>; + +/// Result type of a `get_storage` call. +pub type GetStorageResult = Result>, ContractAccessError>; + +/// The possible errors that can happen querying the storage of a contract. +#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] +pub enum ContractAccessError { + /// The given address doesn't point to a contract. + DoesntExist, + /// Storage key cannot be decoded from the provided input data. + KeyDecodingFailed, +} + +/// Output of a contract call or instantiation which ran to completion. +#[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, + /// Buffer passed along by `seal_return`. Empty when `seal_return` was never called. + pub data: Vec, +} + +impl ExecReturnValue { + /// The contract did revert all storage changes. + pub fn did_revert(&self) -> bool { + self.flags.contains(ReturnFlags::REVERT) + } +} + +/// The result of a successful contract instantiation. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct InstantiateReturnValue { + /// The output of the called constructor. + pub result: ExecReturnValue, + /// The 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 { + /// The key under which the new code is stored. + 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 { + /// A wasm module as raw bytes. + Upload(Vec), + /// The code hash of an on-chain wasm blob. + Existing(sp_core::H256), +} + +/// The amount of balance that was either charged or refunded in order to pay for storage. +#[derive( + Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo, +)] +pub enum StorageDeposit { + /// The transaction reduced storage consumption. + /// + /// This means that the specified amount of balance was transferred from the involved + /// deposit accounts to the origin. + Refund(Balance), + /// The transaction increased storage consumption. + /// + /// This means that the specified amount of balance was transferred from the origin + /// to the involved deposit accounts. + Charge(Balance), +} + +impl Default for StorageDeposit { + fn default() -> Self { + Self::Charge(Zero::zero()) + } +} + +impl StorageDeposit { + /// Returns how much balance is charged or `0` in case of a refund. + pub fn charge_or_zero(&self) -> Balance { + match self { + Self::Charge(amount) => *amount, + Self::Refund(_) => Zero::zero(), + } + } + + pub fn is_zero(&self) -> bool { + match self { + Self::Charge(amount) => amount.is_zero(), + Self::Refund(amount) => amount.is_zero(), + } + } +} + +impl StorageDeposit +where + Balance: Saturating + Ord + Copy, +{ + /// This is essentially a saturating signed add. + pub fn saturating_add(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Refund(rhs)) => + if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + }, + (Refund(lhs), Charge(rhs)) => + if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + }, + } + } + + /// This is essentially a saturating signed sub. + pub fn saturating_sub(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Charge(rhs)) => + if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + }, + (Refund(lhs), Refund(rhs)) => + if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + }, + } + } + + /// If the amount of deposit (this type) is constrained by a `limit` this calculates how + /// much balance (if any) is still available from this limit. + /// + /// # Note + /// + /// In case of a refund the return value can be larger than `limit`. + pub fn available(&self, limit: &Balance) -> Balance { + use StorageDeposit::*; + match self { + Charge(amount) => limit.saturating_sub(*amount), + Refund(amount) => limit.saturating_add(*amount), + } + } +} + +/// Determines whether events should be collected during execution. +#[derive( + Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo, +)] +pub enum CollectEvents { + /// Collect events. + /// + /// # Note + /// + /// Events should only be collected when called off-chain, as this would otherwise + /// collect all the Events emitted in the block so far and put them into the PoV. + /// + /// **Never** use this mode for on-chain execution. + UnsafeCollect, + /// Skip event collection. + Skip, +} + +/// Determines whether debug messages will be collected. +#[derive( + Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo, +)] +pub enum DebugInfo { + /// Collect debug messages. + /// # Note + /// + /// This should only ever be set to `UnsafeDebug` when executing as an RPC because + /// it adds allocations and could be abused to drive the runtime into an OOM panic. + UnsafeDebug, + /// Skip collection of debug messages. + Skip, +} diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef7ce2db32cff0715bc6c6254b89c8b70168cd3a --- /dev/null +++ b/substrate/frame/revive/src/storage.rs @@ -0,0 +1,483 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains routines for accessing and altering a contract related state. + +pub mod meter; + +use crate::{ + address::AddressMapper, + exec::{AccountIdOf, Key}, + limits, + storage::meter::Diff, + weights::WeightInfo, + BalanceOf, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, Error, + TrieId, SENTINEL, +}; +use alloc::vec::Vec; +use codec::{Decode, Encode, MaxEncodedLen}; +use core::marker::PhantomData; +use frame_support::{ + storage::child::{self, ChildInfo}, + weights::{Weight, WeightMeter}, + CloneNoBound, DefaultNoBound, +}; +use scale_info::TypeInfo; +use sp_core::{ConstU32, Get, H160}; +use sp_io::KillStorageResult; +use sp_runtime::{ + traits::{Hash, Saturating, Zero}, + BoundedBTreeMap, DispatchError, DispatchResult, RuntimeDebug, +}; + +type DelegateDependencyMap = + 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. +#[derive(Encode, Decode, CloneNoBound, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub struct ContractInfo { + /// Unique ID for the subtree encoded as a bytes vector. + pub trie_id: TrieId, + /// The code associated with a given account. + pub code_hash: 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. + storage_items: u32, + /// This records to how much deposit the accumulated `storage_bytes` amount to. + pub storage_byte_deposit: BalanceOf, + /// This records to how much deposit the accumulated `storage_items` amount to. + storage_item_deposit: BalanceOf, + /// This records how much deposit is put down in order to pay for the contract itself. + /// + /// We need to store this information separately so it is not used when calculating any refunds + /// since the base deposit can only ever be refunded on contract termination. + storage_base_deposit: BalanceOf, + /// Map of code hashes and deposit balances. + /// + /// Tracks the code hash and deposit held for locking delegate dependencies. Dependencies added + /// to the map can not be removed from the chain state and can be safely used for delegate + /// calls. + delegate_dependencies: DelegateDependencyMap, +} + +impl ContractInfo { + /// Constructs a new contract info **without** writing it to storage. + /// + /// This returns an `Err` if an contract with the supplied `account` already exists + /// in storage. + pub fn new( + address: &H160, + nonce: T::Nonce, + code_hash: sp_core::H256, + ) -> Result { + if >::contains_key(address) { + return Err(Error::::DuplicateContract.into()) + } + + let trie_id = { + let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash); + buf.as_ref() + .to_vec() + .try_into() + .expect("Runtime uses a reasonable hash size. Hence sizeof(T::Hash) <= 128; qed") + }; + + let contract = Self { + trie_id, + code_hash, + storage_bytes: 0, + storage_items: 0, + storage_byte_deposit: Zero::zero(), + storage_item_deposit: Zero::zero(), + storage_base_deposit: Zero::zero(), + delegate_dependencies: Default::default(), + }; + + Ok(contract) + } + + /// Returns the number of locked delegate dependencies. + pub fn delegate_dependencies_count(&self) -> usize { + self.delegate_dependencies.len() + } + + /// Associated child trie unique id is built from the hash part of the trie id. + pub fn child_trie_info(&self) -> ChildInfo { + ChildInfo::new_default(self.trie_id.as_ref()) + } + + /// The deposit paying for the accumulated storage generated within the contract's child trie. + pub fn extra_deposit(&self) -> BalanceOf { + self.storage_byte_deposit.saturating_add(self.storage_item_deposit) + } + + /// Same as [`Self::extra_deposit`] but including the base deposit. + pub fn total_deposit(&self) -> BalanceOf { + self.extra_deposit().saturating_add(self.storage_base_deposit) + } + + /// Returns the storage base deposit of the contract. + pub fn storage_base_deposit(&self) -> BalanceOf { + self.storage_base_deposit + } + + /// Reads a storage kv pair of a contract. + /// + /// The read is performed from the `trie_id` only. The `address` is not necessary. If the + /// contract doesn't store under the given `key` `None` is returned. + pub fn read(&self, key: &Key) -> Option> { + child::get_raw(&self.child_trie_info(), key.hash().as_slice()) + } + + /// Returns `Some(len)` (in bytes) if a storage item exists at `key`. + /// + /// Returns `None` if the `key` wasn't previously set by `set_storage` or + /// was deleted. + pub fn size(&self, key: &Key) -> Option { + child::len(&self.child_trie_info(), key.hash().as_slice()) + } + + /// Update a storage entry into a contract's kv storage. + /// + /// If the `new_value` is `None` then the kv pair is removed. If `take` is true + /// a [`WriteOutcome::Taken`] is returned instead of a [`WriteOutcome::Overwritten`]. + /// + /// This function also records how much storage was created or removed if a `storage_meter` + /// is supplied. It should only be absent for testing or benchmarking code. + pub fn write( + &self, + key: &Key, + new_value: Option>, + storage_meter: Option<&mut meter::NestedMeter>, + take: bool, + ) -> Result { + let hashed_key = key.hash(); + self.write_raw(&hashed_key, new_value, storage_meter, take) + } + + /// Update a storage entry into a contract's kv storage. + /// Function used in benchmarks, which can simulate prefix collision in keys. + #[cfg(feature = "runtime-benchmarks")] + pub fn bench_write_raw( + &self, + key: &[u8], + new_value: Option>, + take: bool, + ) -> Result { + self.write_raw(key, new_value, None, take) + } + + fn write_raw( + &self, + key: &[u8], + new_value: Option>, + storage_meter: Option<&mut meter::NestedMeter>, + take: bool, + ) -> Result { + let child_trie_info = &self.child_trie_info(); + let (old_len, old_value) = if take { + let val = child::get_raw(child_trie_info, key); + (val.as_ref().map(|v| v.len() as u32), val) + } else { + (child::len(child_trie_info, key), None) + }; + + if let Some(storage_meter) = storage_meter { + let mut diff = meter::Diff::default(); + match (old_len, new_value.as_ref().map(|v| v.len() as u32)) { + (Some(old_len), Some(new_len)) => + if new_len > old_len { + diff.bytes_added = new_len - old_len; + } else { + diff.bytes_removed = old_len - new_len; + }, + (None, Some(new_len)) => { + diff.bytes_added = new_len; + diff.items_added = 1; + }, + (Some(old_len), None) => { + diff.bytes_removed = old_len; + diff.items_removed = 1; + }, + (None, None) => (), + } + storage_meter.charge(&diff); + } + + match &new_value { + Some(new_value) => child::put_raw(child_trie_info, key, new_value), + None => child::kill(child_trie_info, key), + } + + Ok(match (old_len, old_value) { + (None, _) => WriteOutcome::New, + (Some(old_len), None) => WriteOutcome::Overwritten(old_len), + (Some(_), Some(old_value)) => WriteOutcome::Taken(old_value), + }) + } + + /// Sets and returns the contract base deposit. + /// + /// The base deposit is updated when the `code_hash` of the contract changes, as it depends on + /// the deposit paid to upload the contract's code. + pub fn update_base_deposit(&mut self, code_info: &CodeInfo) -> BalanceOf { + let info_deposit = + Diff { bytes_added: self.encoded_size() as u32, items_added: 1, ..Default::default() } + .update_contract::(None) + .charge_or_zero(); + + // Instantiating the contract prevents its code to be deleted, therefore the base deposit + // includes a fraction (`T::CodeHashLockupDepositPercent`) of the original storage deposit + // to prevent abuse. + let upload_deposit = T::CodeHashLockupDepositPercent::get().mul_ceil(code_info.deposit()); + + let deposit = info_deposit.saturating_add(upload_deposit); + self.storage_base_deposit = deposit; + deposit + } + + /// Adds a new delegate dependency to the contract. + /// The `amount` is the amount of funds that will be reserved for the dependency. + /// + /// Returns an error if the maximum number of delegate_dependencies is reached or if + /// the delegate dependency already exists. + pub fn lock_delegate_dependency( + &mut self, + code_hash: sp_core::H256, + amount: BalanceOf, + ) -> DispatchResult { + self.delegate_dependencies + .try_insert(code_hash, amount) + .map_err(|_| Error::::MaxDelegateDependenciesReached)? + .map_or(Ok(()), |_| Err(Error::::DelegateDependencyAlreadyExists)) + .map_err(Into::into) + } + + /// Removes the delegate dependency from the contract and returns the deposit held for this + /// dependency. + /// + /// Returns an error if the entry doesn't exist. + pub fn unlock_delegate_dependency( + &mut self, + code_hash: &sp_core::H256, + ) -> Result, DispatchError> { + self.delegate_dependencies + .remove(code_hash) + .ok_or(Error::::DelegateDependencyNotFound.into()) + } + + /// Returns the delegate_dependencies of the contract. + pub fn delegate_dependencies(&self) -> &DelegateDependencyMap { + &self.delegate_dependencies + } + + /// Push a contract's trie to the deletion queue for lazy removal. + /// + /// You must make sure that the contract is also removed when queuing the trie for deletion. + pub fn queue_trie_for_deletion(&self) { + DeletionQueueManager::::load().insert(self.trie_id.clone()); + } + + /// Calculates the weight that is necessary to remove one key from the trie and how many + /// of those keys can be deleted from the deletion queue given the supplied weight limit. + pub fn deletion_budget(meter: &WeightMeter) -> (Weight, u32) { + let base_weight = T::WeightInfo::on_process_deletion_queue_batch(); + let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) - + T::WeightInfo::on_initialize_per_trie_key(0); + + // `weight_per_key` being zero makes no sense and would constitute a failure to + // benchmark properly. We opt for not removing any keys at all in this case. + let key_budget = meter + .limit() + .saturating_sub(base_weight) + .checked_div_per_component(&weight_per_key) + .unwrap_or(0) as u32; + + (weight_per_key, key_budget) + } + + /// Delete as many items from the deletion queue possible within the supplied weight limit. + pub fn process_deletion_queue_batch(meter: &mut WeightMeter) { + if meter.try_consume(T::WeightInfo::on_process_deletion_queue_batch()).is_err() { + return + }; + + let mut queue = >::load(); + if queue.is_empty() { + return; + } + + let (weight_per_key, budget) = Self::deletion_budget(&meter); + let mut remaining_key_budget = budget; + while remaining_key_budget > 0 { + let Some(entry) = queue.next() else { break }; + + #[allow(deprecated)] + let outcome = child::kill_storage( + &ChildInfo::new_default(&entry.trie_id), + Some(remaining_key_budget), + ); + + match outcome { + // This happens when our budget wasn't large enough to remove all keys. + KillStorageResult::SomeRemaining(keys_removed) => { + remaining_key_budget.saturating_reduce(keys_removed); + break + }, + KillStorageResult::AllRemoved(keys_removed) => { + entry.remove(); + // charge at least one key even if none were removed. + remaining_key_budget = remaining_key_budget.saturating_sub(keys_removed.max(1)); + }, + }; + } + + meter.consume(weight_per_key.saturating_mul(u64::from(budget - remaining_key_budget))) + } + + /// Returns the code hash of the contract specified by `account` ID. + pub fn load_code_hash(account: &AccountIdOf) -> Option { + >::get(&T::AddressMapper::to_address(account)).map(|i| i.code_hash) + } +} + +/// Information about what happened to the pre-existing value when calling [`ContractInfo::write`]. +#[cfg_attr(any(test, feature = "runtime-benchmarks"), derive(Debug, PartialEq))] +pub enum WriteOutcome { + /// No value existed at the specified key. + New, + /// A value of the returned length was overwritten. + Overwritten(u32), + /// The returned value was taken out of storage before being overwritten. + /// + /// This is only returned when specifically requested because it causes additional work + /// depending on the size of the pre-existing value. When not requested [`Self::Overwritten`] + /// is returned instead. + Taken(Vec), +} + +impl WriteOutcome { + /// Extracts the size of the overwritten value or `0` if there + /// was no value in storage. + pub fn old_len(&self) -> u32 { + match self { + Self::New => 0, + Self::Overwritten(len) => *len, + Self::Taken(value) => value.len() as u32, + } + } + + /// Extracts the size of the overwritten value or `SENTINEL` if there + /// was no value in storage. + /// + /// # Note + /// + /// We cannot use `0` as sentinel value because there could be a zero sized + /// storage entry which is different from a non existing one. + pub fn old_len_with_sentinel(&self) -> u32 { + match self { + Self::New => SENTINEL, + Self::Overwritten(len) => *len, + Self::Taken(value) => value.len() as u32, + } + } +} + +/// Manage the removal of contracts storage that are marked for deletion. +/// +/// When a contract is deleted by calling `seal_terminate` it becomes inaccessible +/// immediately, but the deletion of the storage items it has accumulated is performed +/// later by pulling the contract from the queue in the `on_idle` hook. +#[derive(Encode, Decode, TypeInfo, MaxEncodedLen, DefaultNoBound, Clone)] +#[scale_info(skip_type_params(T))] +pub struct DeletionQueueManager { + /// Counter used as a key for inserting a new deleted contract in the queue. + /// The counter is incremented after each insertion. + insert_counter: u32, + /// The index used to read the next element to be deleted in the queue. + /// The counter is incremented after each deletion. + delete_counter: u32, + + _phantom: PhantomData, +} + +/// View on a contract that is marked for deletion. +struct DeletionQueueEntry<'a, T: Config> { + /// the trie id of the contract to delete. + trie_id: TrieId, + + /// A mutable reference on the queue so that the contract can be removed, and none can be added + /// or read in the meantime. + queue: &'a mut DeletionQueueManager, +} + +impl<'a, T: Config> DeletionQueueEntry<'a, T> { + /// Remove the contract from the deletion queue. + fn remove(self) { + >::remove(self.queue.delete_counter); + self.queue.delete_counter = self.queue.delete_counter.wrapping_add(1); + >::set(self.queue.clone()); + } +} + +impl DeletionQueueManager { + /// Load the `DeletionQueueCounter`, so we can perform read or write operations on the + /// DeletionQueue storage. + fn load() -> Self { + >::get() + } + + /// Returns `true` if the queue contains no elements. + fn is_empty(&self) -> bool { + self.insert_counter.wrapping_sub(self.delete_counter) == 0 + } + + /// Insert a contract in the deletion queue. + fn insert(&mut self, trie_id: TrieId) { + >::insert(self.insert_counter, trie_id); + self.insert_counter = self.insert_counter.wrapping_add(1); + >::set(self.clone()); + } + + /// Fetch the next contract to be deleted. + /// + /// Note: + /// we use the delete counter to get the next value to read from the queue and thus don't pay + /// the cost of an extra call to `sp_io::storage::next_key` to lookup the next entry in the map + fn next(&mut self) -> Option> { + if self.is_empty() { + return None + } + + let entry = >::get(self.delete_counter); + entry.map(|trie_id| DeletionQueueEntry { trie_id, queue: self }) + } +} + +#[cfg(test)] +#[cfg(feature = "riscv")] +impl DeletionQueueManager { + pub fn from_test_values(insert_counter: u32, delete_counter: u32) -> Self { + Self { insert_counter, delete_counter, _phantom: Default::default() } + } + pub fn as_test_tuple(&self) -> (u32, u32) { + (self.insert_counter, self.delete_counter) + } +} diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs new file mode 100644 index 0000000000000000000000000000000000000000..a2ece03f9aaf95d2ca771475bfd9e820a45b40ea --- /dev/null +++ b/substrate/frame/revive/src/storage/meter.rs @@ -0,0 +1,883 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains functions to meter the storage deposit. + +use crate::{ + address::AddressMapper, storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, + Event, HoldReason, Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, +}; +use alloc::vec::Vec; +use core::{fmt::Debug, marker::PhantomData}; +use frame_support::{ + traits::{ + fungible::{Mutate, MutateHold}, + tokens::{Fortitude, Fortitude::Polite, Precision, Preservation, Restriction}, + Get, + }, + DefaultNoBound, RuntimeDebugNoBound, +}; +use sp_runtime::{ + traits::{Saturating, Zero}, + DispatchError, FixedPointNumber, FixedU128, +}; + +/// Deposit that uses the native fungible's balance type. +pub type DepositOf = Deposit>; + +/// A production root storage meter that actually charges from its origin. +pub type Meter = RawMeter; + +/// A production nested storage meter that actually charges from its origin. +pub type NestedMeter = RawMeter; + +/// A production storage meter that actually charges from its origin. +/// +/// This can be used where we want to be generic over the state (Root vs. Nested). +pub type GenericMeter = RawMeter; + +/// A trait that allows to decouple the metering from the charging of balance. +/// +/// This mostly exists for testing so that the charging can be mocked. +pub trait Ext { + /// This checks whether `origin` is able to afford the storage deposit limit. + /// + /// It is necessary to do this check beforehand so that the charge won't fail later on. + /// + /// `origin`: The origin of the call stack from which is responsible for putting down a deposit. + /// `limit`: The limit with which the meter was constructed. + /// `min_leftover`: How much `free_balance` in addition to the existential deposit (ed) should + /// be left inside the `origin` account. + /// + /// Returns the limit that should be used by the meter. If origin can't afford the `limit` + /// it returns `Err`. + fn check_limit( + origin: &T::AccountId, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result, DispatchError>; + /// This is called to inform the implementer that some balance should be charged due to + /// some interaction of the `origin` with a `contract`. + /// + /// The balance transfer can either flow from `origin` to `contract` or the other way + /// around depending on whether `amount` constitutes a `Charge` or a `Refund`. + /// It should be used in combination with `check_limit` to check that no more balance than this + /// limit is ever charged. + fn charge( + origin: &T::AccountId, + contract: &T::AccountId, + amount: &DepositOf, + state: &ContractState, + ) -> Result<(), DispatchError>; +} + +/// This [`Ext`] is used for actual on-chain execution when balance needs to be charged. +/// +/// It uses [`frame_support::traits::fungible::Mutate`] in order to do accomplish the reserves. +pub enum ReservingExt {} + +/// Used to implement a type state pattern for the meter. +/// +/// It is sealed and cannot be implemented outside of this module. +pub trait State: private::Sealed {} + +/// State parameter that constitutes a meter that is in its root state. +#[derive(Default, Debug)] +pub struct Root; + +/// State parameter that constitutes a meter that is in its nested state. +/// Its value indicates whether the nested meter has its own limit. +#[derive(DefaultNoBound, RuntimeDebugNoBound)] +pub enum Nested { + #[default] + DerivedLimit, + OwnLimit, +} + +impl State for Root {} +impl State for Nested {} + +/// A type that allows the metering of consumed or freed storage of a single contract call stack. +#[derive(DefaultNoBound, RuntimeDebugNoBound)] +pub struct RawMeter { + /// The limit of how much balance this meter is allowed to consume. + limit: BalanceOf, + /// The amount of balance that was used in this meter and all of its already absorbed children. + total_deposit: DepositOf, + /// The amount of storage changes that were recorded in this meter alone. + own_contribution: Contribution, + /// List of charges that should be applied at the end of a contract stack execution. + /// + /// We only have one charge per contract hence the size of this vector is + /// limited by the maximum call depth. + charges: Vec>, + /// We store the nested state to determine if it has a special limit for sub-call. + nested: S, + /// Type parameter only used in impls. + _phantom: PhantomData, +} + +/// This type is used to describe a storage change when charging from the meter. +#[derive(Default, RuntimeDebugNoBound)] +pub struct Diff { + /// How many bytes were added to storage. + pub bytes_added: u32, + /// How many bytes were removed from storage. + pub bytes_removed: u32, + /// How many storage items were added to storage. + pub items_added: u32, + /// How many storage items were removed from storage. + pub items_removed: u32, +} + +impl Diff { + /// Calculate how much of a charge or refund results from applying the diff and store it + /// in the passed `info` if any. + /// + /// # Note + /// + /// In case `None` is passed for `info` only charges are calculated. This is because refunds + /// are calculated pro rata of the existing storage within a contract and hence need extract + /// this information from the passed `info`. + pub fn update_contract(&self, info: Option<&mut ContractInfo>) -> DepositOf { + let per_byte = T::DepositPerByte::get(); + let per_item = T::DepositPerItem::get(); + let bytes_added = self.bytes_added.saturating_sub(self.bytes_removed); + let items_added = self.items_added.saturating_sub(self.items_removed); + let mut bytes_deposit = Deposit::Charge(per_byte.saturating_mul((bytes_added).into())); + let mut items_deposit = Deposit::Charge(per_item.saturating_mul((items_added).into())); + + // Without any contract info we can only calculate diffs which add storage + let info = if let Some(info) = info { + info + } else { + debug_assert_eq!(self.bytes_removed, 0); + debug_assert_eq!(self.items_removed, 0); + return bytes_deposit.saturating_add(&items_deposit) + }; + + // Refunds are calculated pro rata based on the accumulated storage within the contract + let bytes_removed = self.bytes_removed.saturating_sub(self.bytes_added); + let items_removed = self.items_removed.saturating_sub(self.items_added); + let ratio = FixedU128::checked_from_rational(bytes_removed, info.storage_bytes) + .unwrap_or_default() + .min(FixedU128::from_u32(1)); + bytes_deposit = bytes_deposit + .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_byte_deposit))); + let ratio = FixedU128::checked_from_rational(items_removed, info.storage_items) + .unwrap_or_default() + .min(FixedU128::from_u32(1)); + items_deposit = items_deposit + .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_item_deposit))); + + // We need to update the contract info structure with the new deposits + info.storage_bytes = + info.storage_bytes.saturating_add(bytes_added).saturating_sub(bytes_removed); + info.storage_items = + info.storage_items.saturating_add(items_added).saturating_sub(items_removed); + match &bytes_deposit { + Deposit::Charge(amount) => + info.storage_byte_deposit = info.storage_byte_deposit.saturating_add(*amount), + Deposit::Refund(amount) => + info.storage_byte_deposit = info.storage_byte_deposit.saturating_sub(*amount), + } + match &items_deposit { + Deposit::Charge(amount) => + info.storage_item_deposit = info.storage_item_deposit.saturating_add(*amount), + Deposit::Refund(amount) => + info.storage_item_deposit = info.storage_item_deposit.saturating_sub(*amount), + } + + bytes_deposit.saturating_add(&items_deposit) + } +} + +impl Diff { + fn saturating_add(&self, rhs: &Self) -> Self { + Self { + bytes_added: self.bytes_added.saturating_add(rhs.bytes_added), + bytes_removed: self.bytes_removed.saturating_add(rhs.bytes_removed), + items_added: self.items_added.saturating_add(rhs.items_added), + items_removed: self.items_removed.saturating_add(rhs.items_removed), + } + } +} + +/// The state of a contract. +/// +/// In case of termination the beneficiary is indicated. +#[derive(RuntimeDebugNoBound, Clone, PartialEq, Eq)] +pub enum ContractState { + Alive, + Terminated { beneficiary: AccountIdOf }, +} + +/// Records information to charge or refund a plain account. +/// +/// All the charges are deferred to the end of a whole call stack. Reason is that by doing +/// this we can do all the refunds before doing any charge. This way a plain account can use +/// more deposit than it has balance as along as it is covered by a refund. This +/// essentially makes the order of storage changes irrelevant with regard to the deposit system. +/// The only exception is when a special (tougher) deposit limit is specified for a cross-contract +/// call. In that case the limit is enforced once the call is returned, rolling it back if +/// exhausted. +#[derive(RuntimeDebugNoBound, Clone)] +struct Charge { + contract: T::AccountId, + amount: DepositOf, + state: ContractState, +} + +/// Records the storage changes of a storage meter. +#[derive(RuntimeDebugNoBound)] +enum Contribution { + /// The contract the meter belongs to is alive and accumulates changes using a [`Diff`]. + Alive(Diff), + /// The meter was checked against its limit using [`RawMeter::enforce_limit`] at the end of + /// its execution. In this process the [`Diff`] was converted into a [`Deposit`]. + Checked(DepositOf), + /// The contract was terminated. In this process the [`Diff`] was converted into a [`Deposit`] + /// in order to calculate the refund. Upon termination the `reducible_balance` in the + /// contract's account is transferred to the [`beneficiary`]. + Terminated { deposit: DepositOf, beneficiary: AccountIdOf }, +} + +impl Contribution { + /// See [`Diff::update_contract`]. + fn update_contract(&self, info: Option<&mut ContractInfo>) -> DepositOf { + match self { + Self::Alive(diff) => diff.update_contract::(info), + Self::Terminated { deposit, beneficiary: _ } | Self::Checked(deposit) => + deposit.clone(), + } + } +} + +impl Default for Contribution { + fn default() -> Self { + Self::Alive(Default::default()) + } +} + +/// Functions that apply to all states. +impl RawMeter +where + T: Config, + E: Ext, + S: State + Default + Debug, +{ + /// Create a new child that has its `limit`. + /// Passing `0` as the limit is interpreted as to take whatever is remaining from its parent. + /// + /// This is called whenever a new subcall is initiated in order to track the storage + /// usage for this sub call separately. This is necessary because we want to exchange balance + /// with the current contract we are interacting with. + pub fn nested(&self, limit: BalanceOf) -> RawMeter { + debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + // If a special limit is specified higher than it is available, + // we want to enforce the lesser limit to the nested meter, to fail in the sub-call. + let limit = self.available().min(limit); + if limit.is_zero() { + RawMeter { limit: self.available(), ..Default::default() } + } else { + RawMeter { limit, nested: Nested::OwnLimit, ..Default::default() } + } + } + + /// Absorb a child that was spawned to handle a sub call. + /// + /// This should be called whenever a sub call comes to its end and it is **not** reverted. + /// This does the actual balance transfer from/to `origin` and `contract` based on the + /// overall storage consumption of the call. It also updates the supplied contract info. + /// + /// In case a contract reverted the child meter should just be dropped in order to revert + /// any changes it recorded. + /// + /// # Parameters + /// + /// - `absorbed`: The child storage meter that should be absorbed. + /// - `origin`: The origin that spawned the original root meter. + /// - `contract`: The contract's account that this sub call belongs to. + /// - `info`: The info of the contract in question. `None` if the contract was terminated. + pub fn absorb( + &mut self, + absorbed: RawMeter, + contract: &T::AccountId, + info: Option<&mut ContractInfo>, + ) { + let own_deposit = absorbed.own_contribution.update_contract(info); + self.total_deposit = self + .total_deposit + .saturating_add(&absorbed.total_deposit) + .saturating_add(&own_deposit); + self.charges.extend_from_slice(&absorbed.charges); + if !own_deposit.is_zero() { + self.charges.push(Charge { + contract: contract.clone(), + amount: own_deposit, + state: absorbed.contract_state(), + }); + } + } + + /// The amount of balance that is still available from the original `limit`. + fn available(&self) -> BalanceOf { + self.total_deposit.available(&self.limit) + } + + /// Returns the state of the currently executed contract. + fn contract_state(&self) -> ContractState { + match &self.own_contribution { + Contribution::Terminated { deposit: _, beneficiary } => + ContractState::Terminated { beneficiary: beneficiary.clone() }, + _ => ContractState::Alive, + } + } +} + +/// Functions that only apply to the root state. +impl RawMeter +where + T: Config, + E: Ext, +{ + /// Create new storage meter for the specified `origin` and `limit`. + /// + /// This tries to [`Ext::check_limit`] on `origin` and fails if this is not possible. + pub fn new( + origin: &Origin, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result { + // Check the limit only if the origin is not root. + return match origin { + Origin::Root => Ok(Self { limit, ..Default::default() }), + Origin::Signed(o) => { + let limit = E::check_limit(o, limit, min_leftover)?; + Ok(Self { limit, ..Default::default() }) + }, + } + } + + /// The total amount of deposit that should change hands as result of the execution + /// that this meter was passed into. This will also perform all the charges accumulated + /// in the whole contract stack. + /// + /// This drops the root meter in order to make sure it is only called when the whole + /// execution did finish. + pub fn try_into_deposit(self, origin: &Origin) -> Result, DispatchError> { + // Only refund or charge deposit if the origin is not root. + let origin = match origin { + Origin::Root => return Ok(Deposit::Charge(Zero::zero())), + Origin::Signed(o) => o, + }; + for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) { + E::charge(origin, &charge.contract, &charge.amount, &charge.state)?; + } + for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Charge(_))) { + E::charge(origin, &charge.contract, &charge.amount, &charge.state)?; + } + Ok(self.total_deposit) + } +} + +/// Functions that only apply to the nested state. +impl> RawMeter { + /// Charges `diff` from the meter. + pub fn charge(&mut self, diff: &Diff) { + match &mut self.own_contribution { + Contribution::Alive(own) => *own = own.saturating_add(diff), + _ => panic!("Charge is never called after termination; qed"), + }; + } + + /// Adds a deposit charge. + /// + /// Use this method instead of [`Self::charge`] when the charge is not the result of a storage + /// change. This is the case when a `delegate_dependency` is added or removed, or when the + /// `code_hash` is updated. [`Self::charge`] cannot be used here because we keep track of the + /// deposit charge separately from the storage charge. + pub fn charge_deposit(&mut self, contract: T::AccountId, amount: DepositOf) { + self.total_deposit = self.total_deposit.saturating_add(&amount); + self.charges.push(Charge { contract, amount, state: ContractState::Alive }); + } + + /// Charges from `origin` a storage deposit for contract instantiation. + /// + /// This immediately transfers the balance in order to create the account. + pub fn charge_instantiate( + &mut self, + origin: &T::AccountId, + contract: &T::AccountId, + contract_info: &mut ContractInfo, + code_info: &CodeInfo, + ) -> Result<(), DispatchError> { + debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + + // We need to make sure that the contract's account exists. + let ed = Pallet::::min_balance(); + self.total_deposit = Deposit::Charge(ed); + T::Currency::transfer(origin, contract, ed, Preservation::Preserve)?; + + // A consumer is added at account creation and removed it on termination, otherwise the + // runtime could remove the account. As long as a contract exists its account must exist. + // With the consumer, a correct runtime cannot remove the account. + System::::inc_consumers(contract)?; + + let deposit = contract_info.update_base_deposit(&code_info); + let deposit = Deposit::Charge(deposit); + + self.charge_deposit(contract.clone(), deposit); + Ok(()) + } + + /// Call to tell the meter that the currently executing contract was terminated. + /// + /// This will manipulate the meter so that all storage deposit accumulated in + /// `contract_info` will be refunded to the `origin` of the meter. And the free + /// (`reducible_balance`) will be sent to the `beneficiary`. + pub fn terminate(&mut self, info: &ContractInfo, beneficiary: T::AccountId) { + debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + self.own_contribution = Contribution::Terminated { + deposit: Deposit::Refund(info.total_deposit()), + beneficiary, + }; + } + + /// [`Self::charge`] does not enforce the storage limit since we want to do this check as late + /// as possible to allow later refunds to offset earlier charges. + /// + /// # Note + /// + /// We normally need to call this **once** for every call stack and not for every cross contract + /// call. However, if a dedicated limit is specified for a sub-call, this needs to be called + /// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is + /// used. + pub fn enforce_limit( + &mut self, + info: Option<&mut ContractInfo>, + ) -> Result<(), DispatchError> { + let deposit = self.own_contribution.update_contract(info); + let total_deposit = self.total_deposit.saturating_add(&deposit); + // We don't want to override a `Terminated` with a `Checked`. + if matches!(self.contract_state(), ContractState::Alive) { + self.own_contribution = Contribution::Checked(deposit); + } + if let Deposit::Charge(amount) = total_deposit { + if amount > self.limit { + return Err(>::StorageDepositLimitExhausted.into()) + } + } + Ok(()) + } + + /// This is a wrapper around [`Self::enforce_limit`] to use on the exit from a sub-call to + /// enforce its special limit if needed. + pub fn enforce_subcall_limit( + &mut self, + info: Option<&mut ContractInfo>, + ) -> Result<(), DispatchError> { + match self.nested { + Nested::OwnLimit => self.enforce_limit(info), + Nested::DerivedLimit => Ok(()), + } + } +} + +impl Ext for ReservingExt { + fn check_limit( + origin: &T::AccountId, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result, DispatchError> { + let limit = T::Currency::reducible_balance(origin, Preservation::Preserve, Polite) + .saturating_sub(min_leftover) + .min(limit); + Ok(limit) + } + + fn charge( + origin: &T::AccountId, + contract: &T::AccountId, + amount: &DepositOf, + state: &ContractState, + ) -> Result<(), DispatchError> { + match amount { + Deposit::Charge(amount) | Deposit::Refund(amount) if amount.is_zero() => return Ok(()), + Deposit::Charge(amount) => { + // This could fail if the `origin` does not have enough liquidity. Ideally, though, + // this should have been checked before with `check_limit`. + T::Currency::transfer_and_hold( + &HoldReason::StorageDepositReserve.into(), + origin, + contract, + *amount, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite, + )?; + + Pallet::::deposit_event(Event::StorageDepositTransferredAndHeld { + from: T::AddressMapper::to_address(origin), + to: T::AddressMapper::to_address(contract), + amount: *amount, + }); + }, + Deposit::Refund(amount) => { + let transferred = T::Currency::transfer_on_hold( + &HoldReason::StorageDepositReserve.into(), + contract, + origin, + *amount, + Precision::BestEffort, + Restriction::Free, + Fortitude::Polite, + )?; + + Pallet::::deposit_event(Event::StorageDepositTransferredAndReleased { + from: T::AddressMapper::to_address(contract), + to: T::AddressMapper::to_address(origin), + amount: transferred, + }); + + if transferred < *amount { + // This should never happen, if it does it means that there is a bug in the + // runtime logic. In the rare case this happens we try to refund as much as we + // can, thus the `Precision::BestEffort`. + log::error!( + target: LOG_TARGET, + "Failed to repatriate full storage deposit {:?} from contract {:?} to origin {:?}. Transferred {:?}.", + amount, contract, origin, transferred, + ); + } + }, + } + if let ContractState::::Terminated { beneficiary } = state { + System::::dec_consumers(&contract); + // Whatever is left in the contract is sent to the termination beneficiary. + T::Currency::transfer( + &contract, + &beneficiary, + T::Currency::reducible_balance(&contract, Preservation::Expendable, Polite), + Preservation::Expendable, + )?; + } + Ok(()) + } +} + +mod private { + pub trait Sealed {} + impl Sealed for super::Root {} + impl Sealed for super::Nested {} +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{exec::AccountIdOf, test_utils::*, tests::Test}; + use frame_support::parameter_types; + use pretty_assertions::assert_eq; + + type TestMeter = RawMeter; + + parameter_types! { + static TestExtTestValue: TestExt = Default::default(); + } + + #[derive(Debug, PartialEq, Eq, Clone)] + struct LimitCheck { + origin: AccountIdOf, + limit: BalanceOf, + min_leftover: BalanceOf, + } + + #[derive(Debug, PartialEq, Eq, Clone)] + struct Charge { + origin: AccountIdOf, + contract: AccountIdOf, + amount: DepositOf, + state: ContractState, + } + + #[derive(Default, Debug, PartialEq, Eq, Clone)] + pub struct TestExt { + limit_checks: Vec, + charges: Vec, + } + + impl TestExt { + fn clear(&mut self) { + self.limit_checks.clear(); + self.charges.clear(); + } + } + + impl Ext for TestExt { + fn check_limit( + origin: &AccountIdOf, + limit: BalanceOf, + min_leftover: BalanceOf, + ) -> Result, DispatchError> { + TestExtTestValue::mutate(|ext| { + ext.limit_checks + .push(LimitCheck { origin: origin.clone(), limit, min_leftover }) + }); + Ok(limit) + } + + fn charge( + origin: &AccountIdOf, + contract: &AccountIdOf, + amount: &DepositOf, + state: &ContractState, + ) -> Result<(), DispatchError> { + TestExtTestValue::mutate(|ext| { + ext.charges.push(Charge { + origin: origin.clone(), + contract: contract.clone(), + amount: amount.clone(), + state: state.clone(), + }) + }); + Ok(()) + } + } + + fn clear_ext() { + TestExtTestValue::mutate(|ext| ext.clear()) + } + + struct ChargingTestCase { + origin: Origin, + deposit: DepositOf, + expected: TestExt, + } + + #[derive(Default)] + struct StorageInfo { + bytes: u32, + items: u32, + bytes_deposit: BalanceOf, + items_deposit: BalanceOf, + } + + fn new_info(info: StorageInfo) -> ContractInfo { + ContractInfo:: { + trie_id: Default::default(), + code_hash: Default::default(), + storage_bytes: info.bytes, + storage_items: info.items, + storage_byte_deposit: info.bytes_deposit, + storage_item_deposit: info.items_deposit, + storage_base_deposit: Default::default(), + delegate_dependencies: Default::default(), + } + } + + #[test] + fn new_reserves_balance_works() { + clear_ext(); + + TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + + assert_eq!( + TestExtTestValue::get(), + TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + ..Default::default() + } + ) + } + + #[test] + fn empty_charge_works() { + clear_ext(); + + let mut meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + + // an empty charge does not create a `Charge` entry + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Default::default()); + meter.absorb(nested0, &BOB, None); + + assert_eq!( + TestExtTestValue::get(), + TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + ..Default::default() + } + ) + } + + #[test] + fn charging_works() { + let test_cases = vec![ + ChargingTestCase { + origin: Origin::::from_account_id(ALICE), + deposit: Deposit::Refund(28), + expected: TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }], + charges: vec![ + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(10), + state: ContractState::Alive, + }, + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(20), + state: ContractState::Alive, + }, + Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(2), + state: ContractState::Alive, + }, + ], + }, + }, + ChargingTestCase { + origin: Origin::::Root, + deposit: Deposit::Charge(0), + expected: TestExt { limit_checks: vec![], charges: vec![] }, + }, + ]; + + for test_case in test_cases { + clear_ext(); + + let mut meter = TestMeter::new(&test_case.origin, 100, 0).unwrap(); + assert_eq!(meter.available(), 100); + + let mut nested0_info = new_info(StorageInfo { + bytes: 100, + items: 5, + bytes_deposit: 100, + items_deposit: 10, + }); + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Diff { + bytes_added: 108, + bytes_removed: 5, + items_added: 1, + items_removed: 2, + }); + nested0.charge(&Diff { bytes_removed: 99, ..Default::default() }); + + let mut nested1_info = new_info(StorageInfo { + bytes: 100, + items: 10, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested1 = nested0.nested(BalanceOf::::zero()); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }); + nested0.absorb(nested1, &CHARLIE, Some(&mut nested1_info)); + + let mut nested2_info = new_info(StorageInfo { + bytes: 100, + items: 7, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested2 = nested0.nested(BalanceOf::::zero()); + nested2.charge(&Diff { items_removed: 7, ..Default::default() }); + nested0.absorb(nested2, &CHARLIE, Some(&mut nested2_info)); + + nested0.enforce_limit(Some(&mut nested0_info)).unwrap(); + meter.absorb(nested0, &BOB, Some(&mut nested0_info)); + + assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + + assert_eq!(nested0_info.extra_deposit(), 112); + assert_eq!(nested1_info.extra_deposit(), 110); + assert_eq!(nested2_info.extra_deposit(), 100); + + assert_eq!(TestExtTestValue::get(), test_case.expected) + } + } + + #[test] + fn termination_works() { + let test_cases = vec![ + ChargingTestCase { + origin: Origin::::from_account_id(ALICE), + deposit: Deposit::Refund(108), + expected: TestExt { + limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }], + charges: vec![ + Charge { + origin: ALICE, + contract: CHARLIE, + amount: Deposit::Refund(120), + state: ContractState::Terminated { beneficiary: CHARLIE }, + }, + Charge { + origin: ALICE, + contract: BOB, + amount: Deposit::Charge(12), + state: ContractState::Alive, + }, + ], + }, + }, + ChargingTestCase { + origin: Origin::::Root, + deposit: Deposit::Charge(0), + expected: TestExt { limit_checks: vec![], charges: vec![] }, + }, + ]; + + for test_case in test_cases { + clear_ext(); + + let mut meter = TestMeter::new(&test_case.origin, 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + + let mut nested0 = meter.nested(BalanceOf::::zero()); + nested0.charge(&Diff { + bytes_added: 5, + bytes_removed: 1, + items_added: 3, + items_removed: 1, + }); + nested0.charge(&Diff { items_added: 2, ..Default::default() }); + + let mut nested1_info = new_info(StorageInfo { + bytes: 100, + items: 10, + bytes_deposit: 100, + items_deposit: 20, + }); + let mut nested1 = nested0.nested(BalanceOf::::zero()); + nested1.charge(&Diff { items_removed: 5, ..Default::default() }); + nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); + nested1.terminate(&nested1_info, CHARLIE); + nested0.enforce_limit(Some(&mut nested1_info)).unwrap(); + nested0.absorb(nested1, &CHARLIE, None); + + meter.absorb(nested0, &BOB, None); + assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit); + assert_eq!(TestExtTestValue::get(), test_case.expected) + } + } +} diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..671efebdf4bd502de91fb6efe1765096576433d7 --- /dev/null +++ b/substrate/frame/revive/src/test_utils.rs @@ -0,0 +1,66 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Shared utilities for testing contracts. +//! This is not part of the tests module because it is made public for other crates to use. + +#![cfg(feature = "std")] + +pub mod builder; + +use crate::{BalanceOf, Config}; +use frame_support::weights::Weight; +use sp_core::H160; +pub use sp_runtime::AccountId32; + +const fn ee_suffix(addr: H160) -> AccountId32 { + let mut id = [0u8; 32]; + let mut i = 0; + while i < 20 { + id[i] = addr.0[i]; + i += 1; + } + + let mut j = 20; + while j < 32 { + id[j] = 0xee; + j += 1; + } + + AccountId32::new(id) +} + +pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const ALICE_ADDR: H160 = H160([1u8; 20]); +pub const ETH_ALICE: AccountId32 = ee_suffix(ALICE_ADDR); + +pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const BOB_ADDR: H160 = H160([2u8; 20]); +pub const BOB_CONTRACT_ID: AccountId32 = ee_suffix(BOB_ADDR); + +pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +pub const CHARLIE_ADDR: H160 = H160([3u8; 20]); + +pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); +pub const DJANGO_ADDR: H160 = H160([4u8; 20]); +pub const ETH_DJANGO: AccountId32 = ee_suffix(DJANGO_ADDR); + +pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); + +pub fn deposit_limit() -> BalanceOf { + 10_000_000u32.into() +} diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs new file mode 100644 index 0000000000000000000000000000000000000000..d361590df95ae8d1b3aedaec391c4b5f772e266e --- /dev/null +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -0,0 +1,226 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{deposit_limit, GAS_LIMIT}; +use crate::{ + address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, + ContractExecResult, ContractInstantiateResult, DebugInfo, EventRecordOf, ExecReturnValue, + InstantiateReturnValue, OriginFor, Pallet, Weight, +}; +use frame_support::pallet_prelude::DispatchResultWithPostInfo; +use paste::paste; +use sp_core::H160; + +/// Helper macro to generate a builder for contract API calls. +macro_rules! builder { + // Entry point to generate a builder for the given method. + ( + $method:ident($($field:ident: $type:ty,)*) -> $result:ty; + $($extra:item)* + ) => { + paste!{ + builder!([< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*); + } + }; + // Generate the builder struct and its methods. + ( + $name:ident, + $method:ident($($field:ident: $type:ty,)*) -> $result:ty; + $($extra:item)* + ) => { + #[doc = concat!("A builder to construct a ", stringify!($method), " call")] + pub struct $name { + $($field: $type,)* + } + + #[allow(dead_code)] + impl $name + where + BalanceOf: Into + TryFrom, + crate::MomentOf: Into, + T::Hash: frame_support::traits::IsType, + { + $( + #[doc = concat!("Set the ", stringify!($field))] + pub fn $field(mut self, value: $type) -> Self { + self.$field = value; + self + } + )* + + #[doc = concat!("Build the ", stringify!($method), " call")] + pub fn build(self) -> $result { + Pallet::::$method( + $(self.$field,)* + ) + } + + $($extra)* + } + } +} + +pub struct Contract { + pub account_id: AccountIdOf, + pub addr: H160, +} + +builder!( + instantiate_with_code( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + code: Vec, + data: Vec, + salt: Option<[u8; 32]>, + ) -> DispatchResultWithPostInfo; + + /// Create an [`InstantiateWithCodeBuilder`] with default values. + pub fn instantiate_with_code(origin: OriginFor, code: Vec) -> Self { + Self { + origin, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + code, + data: vec![], + salt: Some([0; 32]), + } + } +); + +builder!( + instantiate( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + code_hash: sp_core::H256, + data: Vec, + salt: Option<[u8; 32]>, + ) -> DispatchResultWithPostInfo; + + /// Create an [`InstantiateBuilder`] with default values. + pub fn instantiate(origin: OriginFor, code_hash: sp_core::H256) -> Self { + Self { + origin, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + code_hash, + data: vec![], + salt: Some([0; 32]), + } + } +); + +builder!( + bare_instantiate( + origin: OriginFor, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + code: Code, + data: Vec, + salt: Option<[u8; 32]>, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractInstantiateResult, EventRecordOf>; + + /// Build the instantiate call and unwrap the result. + pub fn build_and_unwrap_result(self) -> InstantiateReturnValue { + self.build().result.unwrap() + } + + /// Build the instantiate call and unwrap the account id. + pub fn build_and_unwrap_contract(self) -> Contract { + let addr = self.build().result.unwrap().addr; + let account_id = T::AddressMapper::to_account_id(&addr); + Contract{ account_id, addr } + } + + /// Create a [`BareInstantiateBuilder`] with default values. + pub fn bare_instantiate(origin: OriginFor, code: Code) -> Self { + Self { + origin, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + code, + data: vec![], + salt: Some([0; 32]), + debug: DebugInfo::UnsafeDebug, + collect_events: CollectEvents::Skip, + } + } +); + +builder!( + call( + origin: OriginFor, + dest: H160, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + data: Vec, + ) -> DispatchResultWithPostInfo; + + /// Create a [`CallBuilder`] with default values. + pub fn call(origin: OriginFor, dest: H160) -> Self { + CallBuilder { + origin, + dest, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + data: vec![], + } + } +); + +builder!( + bare_call( + origin: OriginFor, + dest: H160, + value: BalanceOf, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + data: Vec, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> ContractExecResult, EventRecordOf>; + + /// Build the call and unwrap the result. + pub fn build_and_unwrap_result(self) -> ExecReturnValue { + self.build().result.unwrap() + } + + /// Create a [`BareCallBuilder`] with default values. + pub fn bare_call(origin: OriginFor, dest: H160) -> Self { + Self { + origin, + dest, + value: 0u32.into(), + gas_limit: GAS_LIMIT, + storage_deposit_limit: deposit_limit::(), + data: vec![], + debug: DebugInfo::UnsafeDebug, + collect_events: CollectEvents::Skip, + } + } +); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..c7185caf0efb9aa8f33cbea27d290e637a90f859 --- /dev/null +++ b/substrate/frame/revive/src/tests.rs @@ -0,0 +1,4257 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "riscv"), allow(dead_code, unused_imports, unused_macros))] + +mod pallet_dummy; +mod test_debug; + +use self::{ + test_debug::TestDebug, + test_utils::{ensure_stored, expected_deposit}, +}; +use crate::{ + self as pallet_revive, + address::{create1, create2, AddressMapper}, + chain_extension::{ + ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, + RetVal, ReturnFlags, + }, + exec::Key, + limits, + primitives::CodeUploadReturnValue, + storage::DeletionQueueManager, + test_utils::*, + tests::test_utils::{get_contract, get_contract_checked}, + wasm::Memory, + weights::WeightInfo, + 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, + pallet_prelude::EnsureOrigin, + parameter_types, + storage::child, + traits::{ + fungible::{BalancedHold, Inspect, Mutate, MutateHold}, + tokens::Preservation, + ConstU32, ConstU64, Contains, OnIdle, OnInitialize, StorageVersion, + }, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightMeter}, +}; +use frame_system::{EventRecord, Phase}; +use pallet_revive_fixtures::{bench::dummy_unique, compile_module}; +use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode; +use sp_io::hashing::blake2_256; +use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; +use sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, Convert, IdentityLookup}, + AccountId32, BuildStorage, DispatchError, Perbill, TokenError, +}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + Utility: pallet_utility, + Contracts: pallet_revive, + Proxy: pallet_proxy, + Dummy: pallet_dummy + } +); + +macro_rules! assert_return_code { + ( $x:expr , $y:expr $(,)? ) => {{ + assert_eq!(u32::from_le_bytes($x.data[..].try_into().unwrap()), $y as u32); + }}; +} + +macro_rules! assert_refcount { + ( $code_hash:expr , $should:expr $(,)? ) => {{ + let is = crate::CodeInfoOf::::get($code_hash).map(|m| m.refcount()).unwrap(); + assert_eq!(is, $should); + }}; +} + +pub mod test_utils { + use super::{Contracts, DepositPerByte, DepositPerItem, Test}; + use crate::{ + 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: 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); + } + pub fn set_balance(who: &AccountIdOf, amount: u64) { + let _ = ::Currency::set_balance(who, amount); + } + pub fn get_balance(who: &AccountIdOf) -> u64 { + ::Currency::free_balance(who) + } + pub fn get_balance_on_hold( + reason: &::RuntimeHoldReason, + who: &AccountIdOf, + ) -> u64 { + ::Currency::balance_on_hold(reason.into(), who) + } + pub fn get_contract(addr: &H160) -> ContractInfo { + get_contract_checked(addr).unwrap() + } + pub fn get_contract_checked(addr: &H160) -> Option> { + ContractInfoOf::::get(addr) + } + 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: &H160) -> BalanceOf { + let contract_info = self::get_contract(&addr); + let info_size = contract_info.encoded_size() as u64; + DepositPerByte::get() + .saturating_mul(info_size) + .saturating_add(DepositPerItem::get()) + } + pub fn expected_deposit(code_len: usize) -> u64 { + // For code_info, the deposit for max_encoded_len is taken. + let code_info_len = CodeInfo::::max_encoded_len() as u64; + // Calculate deposit to be reserved. + // We add 2 storage items: one for code, other for code_info + DepositPerByte::get().saturating_mul(code_len as u64 + code_info_len) + + DepositPerItem::get().saturating_mul(2) + } + pub fn ensure_stored(code_hash: 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::*, ALICE}, + tests::RuntimeOrigin, + Code, + }; + use sp_core::{H160, H256}; + + pub fn bare_instantiate(code: Code) -> BareInstantiateBuilder { + BareInstantiateBuilder::::bare_instantiate(RuntimeOrigin::signed(ALICE), code) + } + + pub fn bare_call(dest: H160) -> BareCallBuilder { + BareCallBuilder::::bare_call(RuntimeOrigin::signed(ALICE), dest) + } + + pub fn instantiate_with_code(code: Vec) -> InstantiateWithCodeBuilder { + InstantiateWithCodeBuilder::::instantiate_with_code( + RuntimeOrigin::signed(ALICE), + code, + ) + } + + pub fn instantiate(code_hash: H256) -> InstantiateBuilder { + InstantiateBuilder::::instantiate(RuntimeOrigin::signed(ALICE), code_hash) + } + + pub fn call(dest: H160) -> CallBuilder { + CallBuilder::::call(RuntimeOrigin::signed(ALICE), dest) + } +} + +impl Test { + pub fn set_unstable_interface(unstable_interface: bool) { + UNSTABLE_INTERFACE.with(|v| *v.borrow_mut() = unstable_interface); + } +} + +parameter_types! { + static TestExtensionTestValue: TestExtension = Default::default(); +} + +#[derive(Clone)] +pub struct TestExtension { + enabled: bool, + last_seen_buffer: Vec, + last_seen_input_len: u32, +} + +#[derive(Default)] +pub struct RevertingExtension; + +#[derive(Default)] +pub struct DisabledExtension; + +#[derive(Default)] +pub struct TempStorageExtension { + storage: u32, +} + +impl TestExtension { + fn disable() { + TestExtensionTestValue::mutate(|e| e.enabled = false) + } + + fn last_seen_buffer() -> Vec { + TestExtensionTestValue::get().last_seen_buffer.clone() + } + + fn last_seen_input_len() -> u32 { + TestExtensionTestValue::get().last_seen_input_len + } +} + +impl Default for TestExtension { + fn default() -> Self { + Self { enabled: true, last_seen_buffer: vec![], last_seen_input_len: 0 } + } +} + +impl ChainExtension for TestExtension { + fn call(&mut self, mut env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + let func_id = env.func_id(); + let id = env.ext_id() as u32 | func_id as u32; + match func_id { + 0 => { + let input = env.read(8)?; + env.write(&input, false, None)?; + TestExtensionTestValue::mutate(|e| e.last_seen_buffer = input); + Ok(RetVal::Converging(id)) + }, + 1 => { + TestExtensionTestValue::mutate(|e| e.last_seen_input_len = env.in_len()); + Ok(RetVal::Converging(id)) + }, + 2 => { + let mut enc = &env.read(9)?[4..8]; + let weight = Weight::from_parts( + u32::decode(&mut enc).map_err(|_| Error::::ContractTrapped)?.into(), + 0, + ); + env.charge_weight(weight)?; + Ok(RetVal::Converging(id)) + }, + 3 => Ok(RetVal::Diverging { flags: ReturnFlags::REVERT, data: vec![42, 99] }), + _ => { + panic!("Passed unknown id to test chain extension: {}", func_id); + }, + } + } + + fn enabled() -> bool { + TestExtensionTestValue::get().enabled + } +} + +impl RegisteredChainExtension for TestExtension { + const ID: u16 = 0; +} + +impl ChainExtension for RevertingExtension { + fn call(&mut self, _env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + Ok(RetVal::Diverging { flags: ReturnFlags::REVERT, data: vec![0x4B, 0x1D] }) + } + + fn enabled() -> bool { + TestExtensionTestValue::get().enabled + } +} + +impl RegisteredChainExtension for RevertingExtension { + const ID: u16 = 1; +} + +impl ChainExtension for DisabledExtension { + fn call(&mut self, _env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + panic!("Disabled chain extensions are never called") + } + + fn enabled() -> bool { + false + } +} + +impl RegisteredChainExtension for DisabledExtension { + const ID: u16 = 2; +} + +impl ChainExtension for TempStorageExtension { + fn call(&mut self, env: Environment) -> ExtensionResult + where + E: Ext, + M: ?Sized + Memory, + { + let func_id = env.func_id(); + match func_id { + 0 => self.storage = 42, + 1 => assert_eq!(self.storage, 42, "Storage is preserved inside the same call."), + 2 => { + assert_eq!(self.storage, 0, "Storage is different for different calls."); + self.storage = 99; + }, + 3 => assert_eq!(self.storage, 99, "Storage is preserved inside the same call."), + _ => { + panic!("Passed unknown id to test chain extension: {}", func_id); + }, + } + Ok(RetVal::Converging(0)) + } + + fn enabled() -> bool { + TestExtensionTestValue::get().enabled + } +} + +impl RegisteredChainExtension for TempStorageExtension { + const ID: u16 = 3; +} + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max( + Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + ); + pub static ExistentialDeposit: u64 = 1; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type AccountId = AccountId32; + type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type ExistentialDeposit = ExistentialDeposit; + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + +#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] +impl pallet_timestamp::Config for Test {} + +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +impl pallet_proxy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = (); + type ProxyDepositBase = ConstU64<1>; + type ProxyDepositFactor = ConstU64<1>; + type MaxProxies = ConstU32<32>; + type WeightInfo = (); + type MaxPending = ConstU32<32>; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = ConstU64<1>; + type AnnouncementDepositFactor = ConstU64<1>; +} + +impl pallet_dummy::Config for Test {} + +parameter_types! { + pub static DepositPerByte: BalanceOf = 1; + pub const DepositPerItem: BalanceOf = 2; + pub static CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); +} + +impl Convert> for Test { + fn convert(w: Weight) -> BalanceOf { + w.ref_time() + } +} + +/// A filter whose filter function can be swapped at runtime. +pub struct TestFilter; + +#[derive(Clone)] +pub struct Filters { + filter: fn(&RuntimeCall) -> bool, +} + +impl Default for Filters { + fn default() -> Self { + Filters { filter: (|_| true) } + } +} + +parameter_types! { + static CallFilter: Filters = Default::default(); +} + +impl TestFilter { + pub fn set_filter(filter: fn(&RuntimeCall) -> bool) { + CallFilter::mutate(|fltr| fltr.filter = filter); + } +} + +impl Contains for TestFilter { + fn contains(call: &RuntimeCall) -> bool { + (CallFilter::get().filter)(call) + } +} + +parameter_types! { + pub static UploadAccount: Option<::AccountId> = None; + pub static InstantiateAccount: Option<::AccountId> = None; +} + +pub struct EnsureAccount(core::marker::PhantomData<(T, A)>); +impl>>> + EnsureOrigin<::RuntimeOrigin> for EnsureAccount +where + ::AccountId: From, +{ + type Success = T::AccountId; + + fn try_origin(o: T::RuntimeOrigin) -> Result { + let who = as EnsureOrigin<_>>::try_origin(o.clone())?; + if matches!(A::get(), Some(a) if who != a) { + return Err(o); + } + + Ok(who) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Err(()) + } +} +parameter_types! { + pub static UnstableInterface: bool = true; +} + +#[derive_impl(crate::config_preludes::TestDefaultConfig)] +impl Config for Test { + type Time = Timestamp; + type Currency = Balances; + type CallFilter = TestFilter; + type ChainExtension = + (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type AddressMapper = DefaultAddressMapper; + type UnsafeUnstableInterface = UnstableInterface; + type UploadOrigin = EnsureAccount; + type InstantiateOrigin = EnsureAccount; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type Debug = TestDebug; +} + +pub struct ExtBuilder { + existential_deposit: u64, + storage_version: Option, + code_hashes: Vec, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: ExistentialDeposit::get(), + storage_version: None, + code_hashes: vec![], + } + } +} + +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn with_code_hashes(mut self, code_hashes: Vec) -> Self { + self.code_hashes = code_hashes; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn build(self) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.register_extension(KeystoreExt::new(MemoryKeystore::new())); + ext.execute_with(|| { + use frame_support::traits::OnGenesis; + + Pallet::::on_genesis(); + if let Some(storage_version) = self.storage_version { + storage_version.put::>(); + } + System::set_block_number(1) + }); + ext.execute_with(|| { + for code_hash in self.code_hashes { + CodeInfoOf::::insert(code_hash, crate::CodeInfo::new(ALICE)); + } + }); + ext + } +} + +fn initialize_block(number: u64) { + System::reset_events(); + System::initialize(&number, &[0u8; 32].into(), &Default::default()); +} + +struct ExtensionInput<'a> { + extension_id: u16, + func_id: u16, + extra: &'a [u8], +} + +impl<'a> ExtensionInput<'a> { + fn to_vec(&self) -> Vec { + ((self.extension_id as u32) << 16 | (self.func_id as u32)) + .to_le_bytes() + .iter() + .chain(self.extra) + .cloned() + .collect() + } +} + +impl<'a> From> for Vec { + fn from(input: ExtensionInput) -> Vec { + input.to_vec() + } +} + +impl Default for Origin { + fn default() -> Self { + Self::Signed(ALICE) + } +} + +/// We can only run the tests if we have a riscv toolchain installed +#[cfg(feature = "riscv")] +mod run_tests { + use super::*; + use pretty_assertions::{assert_eq, assert_ne}; + use sp_core::U256; + + #[test] + fn calling_plain_account_is_balance_transfer() { + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 100_000_000); + 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(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let value = 100; + + // We determine the storage deposit limit after uploading because it depends on ALICEs + // free balance which is changed by uploading a module. + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm, + deposit_limit::(), + )); + + // Drop previous events + initialize_block(2); + + // Check at the end to get hash on error easily + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Existing(code_hash)) + .value(value) + .build_and_unwrap_contract(); + assert!(ContractInfoOf::::contains_key(&addr)); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: account_id.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: account_id.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id.clone(), + amount: value, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::ContractEmitted { + 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_ADDR, + contract: addr + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE_ADDR, + to: addr, + amount: test_utils::contract_info_storage_deposit(&addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[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(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + // Create + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_contract(); + + // Call contract with allowed storage value. + 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()); + + // Call contract with too large a storage value. + assert_err_ignore_postinfo!( + builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(), + Error::::ValueTooLarge, + ); + }); + } + + // Fail out of fuel (ref_time weight) in the engine. + #[test] + fn run_out_of_fuel_engine() { + let (wasm, _code_hash) = compile_module("run_out_of_gas").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(100 * min_balance) + .build_and_unwrap_contract(); + + // Call the contract with a fixed gas limit. It must run out of gas because it just + // loops forever. + assert_err_ignore_postinfo!( + builder::call(addr) + .gas_limit(Weight::from_parts(10_000_000_000, u64::MAX)) + .build(), + Error::::OutOfGas, + ); + }); + } + + // Fail out of fuel (ref_time weight) in the host. + #[test] + fn run_out_of_fuel_host() { + let (code, _hash) = compile_module("chain_extension").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .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) + .gas_limit(gas_limit) + .data( + ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() } + .into(), + ) + .build() + .result; + assert_err!(result, >::OutOfGas); + }); + } + + #[test] + fn gas_syncs_work() { + let (code, _code_hash) = compile_module("caller_is_origin_n").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let contract = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + 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(contract.addr).data(1u32.encode()).build(); + assert_ok!(result.result); + let gas_consumed_once = result.gas_consumed.ref_time(); + let host_consumed_once = + ::WeightInfo::seal_caller_is_origin().ref_time(); + let engine_consumed_once = + gas_consumed_once - host_consumed_once - engine_consumed_noop; + + let result = builder::bare_call(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; + let engine_consumed_twice = + gas_consumed_twice - host_consumed_twice - engine_consumed_noop; + + // Second contract just repeats first contract's instructions twice. + // If runtime syncs gas with the engine properly, this should pass. + assert_eq!(engine_consumed_twice, engine_consumed_once * 2); + }); + } + + /// Check that contracts with the same account id have different trie ids. + /// Check the `Nonce` storage item for more information. + #[test] + fn instantiate_unique_trie_id() { + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); + + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_limit::()) + .unwrap(); + + // Instantiate the contract and store its trie id for later comparison. + let 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. + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).build(), + >::DuplicateContract, + ); + + // Terminate the contract. + assert_ok!(builder::call(addr).build()); + + // Re-Instantiate after termination. + assert_ok!(builder::instantiate(code_hash).build()); + + // Trie ids shouldn't match or we might have a collision + assert_ne!(trie_id, get_contract(&addr).trie_id); + }); + } + + #[test] + fn storage_work() { + let (code, _code_hash) = compile_module("storage").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + builder::bare_call(addr).build_and_unwrap_result(); + }); + } + + #[test] + fn storage_max_value_limit() { + let (wasm, _code_hash) = compile_module("storage_size").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + // Create + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_contract(); + get_contract(&addr); + + // Call contract with allowed storage value. + 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()); + + // Call contract with too large a storage value. + assert_err_ignore_postinfo!( + builder::call(addr).data((limits::PAYLOAD_BYTES + 1).encode()).build(), + Error::::ValueTooLarge, + ); + }); + } + + #[test] + fn transient_storage_work() { + let (code, _code_hash) = compile_module("transient_storage").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + builder::bare_call(addr).build_and_unwrap_result(); + }); + } + + #[test] + fn transient_storage_limit_in_call() { + let (wasm_caller, _code_hash_caller) = + compile_module("create_transient_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("set_transient_storage").unwrap(); + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let 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) + .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) + .data((4u32 * 1024u32, 200u32, &addr_callee).encode()) + .build(), + >::OutOfTransientStorage, + ); + + // Call a contract with a storage value that is too large. + // Limit exceeded in the callee contract. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .data((50u32, 4 * 1024u32, &addr_callee).encode()) + .build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn deploy_and_call_other_contract() { + let (caller_wasm, _caller_code_hash) = compile_module("caller_contract").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("return_with_data").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + + // Create + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let 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, + deposit_limit::(), + ) + .unwrap(); + + // 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).data(callee_code_hash.as_ref().to_vec()).build()); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: callee_account.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: callee_account.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: callee_account.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: caller_account.clone(), + to: callee_account.clone(), + amount: 32768 // hardcoded in wasm + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: caller_addr, + contract: callee_addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: caller_account.clone(), + to: callee_account.clone(), + amount: 32768, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(caller_account.clone()), + contract: callee_addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: caller_addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE_ADDR, + to: callee_addr, + amount: test_utils::contract_info_storage_deposit(&callee_addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn delegate_call() { + let (caller_wasm, _caller_code_hash) = compile_module("delegate_call").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("delegate_call_lib").unwrap(); + + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the 'caller' + let 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) + .value(1337) + .data(callee_code_hash.as_ref().to_vec()) + .build()); + }); + } + + #[test] + fn transfer_expendable_cannot_kill_account() { + let (wasm, _code_hash) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the BOB contract. + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(1_000) + .build_and_unwrap_contract(); + + // Check that the BOB contract has been instantiated. + get_contract(&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(), + &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( + &account, + &ALICE, + total_balance, + Preservation::Expendable, + ), + TokenError::FundsUnavailable, + ); + + assert_eq!(::Currency::total_balance(&account), total_balance); + }); + } + + #[test] + fn cannot_self_destruct_through_draining() { + let (wasm, _code_hash) = compile_module("drain").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let value = 1_000; + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(value) + .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).build()); + + // Make sure the account wasn't remove by sending all free balance away. + assert_eq!( + ::Currency::total_balance(&account), + value + test_utils::contract_info_storage_deposit(&addr) + min_balance, + ); + }); + } + + #[test] + fn cannot_self_destruct_through_storage_refund_after_price_change() { + let (wasm, _code_hash) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let 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(&contract.addr).total_deposit(), info_deposit); + assert_eq!(get_contract(&contract.addr).extra_deposit(), 0); + assert_eq!( + ::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(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(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(&contract.account_id), + get_contract(&contract.addr).total_deposit() + min_balance, + ); + 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(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the BOB contract. + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_contract(); + + // Check that the BOB contract has been instantiated. + get_contract(&addr); + + // Call BOB with input data, forcing it make a recursive call to itself to + // self-destruct, resulting in a trap. + assert_err_ignore_postinfo!( + builder::call(addr).data(vec![0]).build(), + Error::::ContractTrapped, + ); + + // Check that BOB is still there. + get_contract(&addr); + }); + } + + #[test] + fn self_destruct_works() { + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(Ð_DJANGO, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let contract = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_contract(); + + // Check that the BOB contract has been instantiated. + let _ = get_contract(&contract.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(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(&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(ETH_DJANGO), + 1_000_000 + 100_000 + min_balance + ); + + // Check that the Alice is missing Django's benefit. Within ALICE's total balance + // there's also the code upload deposit held. + assert_eq!( + ::Currency::total_balance(&ALICE), + 1_000_000 - (100_000 + min_balance) + ); + + pretty_assertions::assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Terminated { + contract: contract.addr, + beneficiary: DJANGO_ADDR, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: contract.addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndReleased { + from: contract.addr, + to: ALICE_ADDR, + amount: info_deposit, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::KilledAccount { + account: contract.account_id.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: contract.account_id.clone(), + to: ETH_DJANGO, + amount: 100_000 + min_balance, + }), + topics: vec![], + }, + ], + ); + }); + } + + // This tests that one contract cannot prevent another from self-destructing by sending it + // additional funds after it has been drained. + #[test] + fn destroy_contract_and_transfer_funds() { + let (callee_wasm, callee_code_hash) = compile_module("self_destruct").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("destroy_and_transfer").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + // Create code hash for bob to instantiate + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + callee_wasm.clone(), + deposit_limit::(), + ) + .unwrap(); + + // This deploys the BOB contract, which in turn deploys the CHARLIE contract during + // construction. + 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 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. + assert_ok!(builder::call(addr_bob).data(addr_charlie.encode()).build()); + + // Check that CHARLIE has moved on to the great beyond (ie. died). + assert!(get_contract_checked(&addr_charlie).is_none()); + }); + } + + #[test] + fn cannot_self_destruct_in_constructor() { + let (wasm, _) = compile_module("self_destructing_constructor").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Fail to instantiate the BOB because the constructor calls seal_terminate. + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).value(100_000).build(), + Error::::TerminatedInConstructor, + ); + }); + } + + #[test] + fn crypto_hashes() { + let (wasm, _code_hash) = compile_module("crypto_hashes").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the CRYPTO_HASHES contract. + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_contract(); + // Perform the call. + let input = b"_DEAD_BEEF"; + use sp_io::hashing::*; + // Wraps a hash function into a more dynamic form usable for testing. + macro_rules! dyn_hash_fn { + ($name:ident) => { + Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice()) + }; + } + // All hash functions and their associated output byte lengths. + let test_cases: &[(Box Box<[u8]>>, usize)] = &[ + (dyn_hash_fn!(sha2_256), 32), + (dyn_hash_fn!(keccak_256), 32), + (dyn_hash_fn!(blake2_256), 32), + (dyn_hash_fn!(blake2_128), 16), + ]; + // Test the given hash functions for the input: "_DEAD_BEEF" + for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() { + // We offset data in the contract tables by 1. + let mut params = vec![(n + 1) as u8]; + params.extend_from_slice(input); + let result = builder::bare_call(addr).data(params).build_and_unwrap_result(); + assert!(!result.did_revert()); + let expected = hash_fn(input.as_ref()); + assert_eq!(&result.data[..*expected_size], &*expected); + } + }) + } + + #[test] + fn transfer_return_code() { + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let contract = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + // Contract has only the minimal balance so any transfer will fail. + ::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() { + 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 bob = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + // Contract calls into Django which is no valid contract + // 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::TransferFailed); + + // 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) + .build_and_unwrap_contract(); + + // Sending more than the contract has 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 * 300)) + .chain(&0u32.to_le_bytes()) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + + // Contract has enough balance but callee reverts because "1" is passed. + ::Currency::set_balance(&bob.account_id, min_balance + 1000); + let result = builder::bare_call(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&django.addr) + .iter() + .chain(&u256_bytes(5)) + .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(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&django.addr) + .iter() + .chain(&u256_bytes(5)) + .chain(&2u32.to_le_bytes()) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CalleeTrapped); + }); + } + + #[test] + fn instantiate_return_code() { + let (caller_code, _caller_hash) = compile_module("instantiate_return_code").unwrap(); + let (callee_code, callee_hash) = compile_module("ok_trap_revert").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + let callee_hash = callee_hash.as_ref().to_vec(); + + assert_ok!(builder::instantiate_with_code(callee_code) + .value(min_balance * 100) + .build()); + + let contract = builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + // Contract has only the minimal balance so any transfer will fail. + ::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(&contract.account_id, min_balance + 10_000); + let 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(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(contract.addr) + .data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect()) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::CalleeTrapped); + }); + } + + #[test] + fn disabled_chain_extension_errors_on_call() { + let (code, _hash) = compile_module("chain_extension").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let contract = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + TestExtension::disable(); + assert_err_ignore_postinfo!( + builder::call(contract.addr).data(vec![7u8; 8]).build(), + Error::::NoChainExtension, + ); + }); + } + + #[test] + fn chain_extension_works() { + let (code, _hash) = compile_module("chain_extension").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let contract = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .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(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(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(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(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(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(contract.addr) + .data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into()) + .build_and_unwrap_result(); + assert_eq!(result.flags, ReturnFlags::REVERT); + assert_eq!(result.data, vec![42, 99]); + + // diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer + // We set the MSB part to 1 (instead of 0) which routes the request into the second + // extension + let result = builder::bare_call(contract.addr) + .data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into()) + .build_and_unwrap_result(); + assert_eq!(result.flags, ReturnFlags::REVERT); + assert_eq!(result.data, vec![0x4B, 0x1D]); + + // Diverging to third chain extension that is disabled + // We set the MSB part to 2 (instead of 0) which routes the request into the third + // extension + assert_err_ignore_postinfo!( + builder::call(contract.addr) + .data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into()) + .build(), + Error::::NoChainExtension, + ); + }); + } + + #[test] + fn chain_extension_temp_storage_works() { + let (code, _hash) = compile_module("chain_extension_temp_storage").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let contract = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + // Call func 0 and func 1 back to back. + let stop_recursion = 0u8; + let mut input: Vec = + ExtensionInput { extension_id: 3, func_id: 0, extra: &[] }.into(); + input.extend_from_slice( + ExtensionInput { extension_id: 3, func_id: 1, extra: &[stop_recursion] } + .to_vec() + .as_ref(), + ); + + assert_ok!(builder::bare_call(contract.addr).data(input.clone()).build().result); + }) + } + + #[test] + fn lazy_removal_works() { + let (code, _hash) = compile_module("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let contract = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + 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(contract.addr).build()); + + // Contract info should be gone + 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)); + + // Run the lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + + // Value should be gone now + assert_matches!(child::get::(trie, &[99]), None); + }); + } + + #[test] + fn lazy_batch_removal_works() { + let (code, _hash) = compile_module("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let mut tries: Vec = vec![]; + + for i in 0..3u8 { + let contract = builder::bare_instantiate(Code::Upload(code.clone())) + .value(min_balance * 100) + .salt(Some([i; 32])) + .build_and_unwrap_contract(); + + 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. Contract info should be gone, but value should be still + // there as the lazy removal did not run, yet. + assert_ok!(builder::call(contract.addr).build()); + + assert!(!>::contains_key(&contract.addr)); + assert_matches!(child::get(trie, &[99]), Some(42)); + + tries.push(trie.clone()) + } + + // Run single lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + + // The single lazy removal should have removed all queued tries + for trie in tries.iter() { + assert_matches!(child::get::(trie, &[99]), None); + } + }); + } + + #[test] + fn lazy_removal_partial_remove_works() { + let (code, _hash) = compile_module("self_destruct").unwrap(); + + // We create a contract with some extra keys above the weight limit + let extra_keys = 7u32; + let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024)); + let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(&meter); + let vals: Vec<_> = (0..max_keys + extra_keys) + .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) + .collect(); + + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + let trie = ext.execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + let info = get_contract(&addr); + + // Put value into the contracts child trie + for val in &vals { + info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap(); + } + >::insert(&addr, info.clone()); + + // Terminate the contract + assert_ok!(builder::call(addr).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + let trie = info.child_trie_info(); + + // But value should be still there as the lazy removal did not run, yet. + for val in &vals { + assert_eq!(child::get::(&trie, &blake2_256(&val.0)), Some(val.1)); + } + + trie.clone() + }); + + // The lazy removal limit only applies to the backend but not to the overlay. + // This commits all keys from the overlay to the backend. + ext.commit_all().unwrap(); + + ext.execute_with(|| { + // Run the lazy removal + ContractInfo::::process_deletion_queue_batch(&mut meter); + + // Weight should be exhausted because we could not even delete all keys + assert!(!meter.can_consume(weight_per_key)); + + let mut num_deleted = 0u32; + let mut num_remaining = 0u32; + + for val in &vals { + match child::get::(&trie, &blake2_256(&val.0)) { + None => num_deleted += 1, + Some(x) if x == val.1 => num_remaining += 1, + Some(_) => panic!("Unexpected value in contract storage"), + } + } + + // All but one key is removed + assert_eq!(num_deleted + num_remaining, vals.len() as u32); + assert_eq!(num_deleted, max_keys); + assert_eq!(num_remaining, extra_keys); + }); + } + + #[test] + fn lazy_removal_does_no_run_on_low_remaining_weight() { + let (code, _hash) = compile_module("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + let info = get_contract(&addr); + let trie = &info.child_trie_info(); + + // Put value into the contracts child trie + child::put(trie, &[99], &42); + + // Terminate the contract + assert_ok!(builder::call(addr).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + // But value should be still there as the lazy removal did not run, yet. + assert_matches!(child::get(trie, &[99]), Some(42)); + + // Assign a remaining weight which is too low for a successful deletion of the contract + let low_remaining_weight = + <::WeightInfo as WeightInfo>::on_process_deletion_queue_batch(); + + // Run the lazy removal + Contracts::on_idle(System::block_number(), low_remaining_weight); + + // Value should still be there, since remaining weight was too low for removal + assert_matches!(child::get::(trie, &[99]), Some(42)); + + // Run the lazy removal while deletion_queue is not full + Contracts::on_initialize(System::block_number()); + + // Value should still be there, since deletion_queue was not full + assert_matches!(child::get::(trie, &[99]), Some(42)); + + // Run on_idle with max remaining weight, this should remove the value + Contracts::on_idle(System::block_number(), Weight::MAX); + + // Value should be gone + assert_matches!(child::get::(trie, &[99]), None); + }); + } + + #[test] + fn lazy_removal_does_not_use_all_weight() { + let (code, _hash) = compile_module("self_destruct").unwrap(); + + let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024)); + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + let (trie, vals, weight_per_key) = ext.execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + let info = get_contract(&addr); + let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(&meter); + assert!(max_keys > 0); + + // We create a contract with one less storage item than we can remove within the limit + let vals: Vec<_> = (0..max_keys - 1) + .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode())) + .collect(); + + // Put value into the contracts child trie + for val in &vals { + info.write(&Key::Fix(val.0), Some(val.2.clone()), None, false).unwrap(); + } + >::insert(&addr, info.clone()); + + // Terminate the contract + assert_ok!(builder::call(addr).build()); + + // Contract info should be gone + assert!(!>::contains_key(&addr)); + + let trie = info.child_trie_info(); + + // But value should be still there as the lazy removal did not run, yet. + for val in &vals { + assert_eq!(child::get::(&trie, &blake2_256(&val.0)), Some(val.1)); + } + + (trie, vals, weight_per_key) + }); + + // The lazy removal limit only applies to the backend but not to the overlay. + // This commits all keys from the overlay to the backend. + ext.commit_all().unwrap(); + + ext.execute_with(|| { + // Run the lazy removal + ContractInfo::::process_deletion_queue_batch(&mut meter); + let base_weight = + <::WeightInfo as WeightInfo>::on_process_deletion_queue_batch(); + assert_eq!(meter.consumed(), weight_per_key.mul(vals.len() as _) + base_weight); + + // All the keys are removed + for val in vals { + assert_eq!(child::get::(&trie, &blake2_256(&val.0)), None); + } + }); + } + + #[test] + fn deletion_queue_ring_buffer_overflow() { + let (code, _hash) = compile_module("self_destruct").unwrap(); + let mut ext = ExtBuilder::default().existential_deposit(50).build(); + + // setup the deletion queue with custom counters + ext.execute_with(|| { + let queue = DeletionQueueManager::from_test_values(u32::MAX - 1, u32::MAX - 1); + >::set(queue); + }); + + // commit the changes to the storage + ext.commit_all().unwrap(); + + ext.execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let mut tries: Vec = vec![]; + + // add 3 contracts to the deletion queue + for i in 0..3u8 { + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone())) + .value(min_balance * 100) + .salt(Some([i; 32])) + .build_and_unwrap_contract(); + + let info = get_contract(&addr); + let trie = &info.child_trie_info(); + + // Put value into the contracts child trie + child::put(trie, &[99], &42); + + // Terminate the contract. Contract info should be gone, but value should be still + // there as the lazy removal did not run, yet. + assert_ok!(builder::call(addr).build()); + + assert!(!>::contains_key(&addr)); + assert_matches!(child::get(trie, &[99]), Some(42)); + + tries.push(trie.clone()) + } + + // Run single lazy removal + Contracts::on_idle(System::block_number(), Weight::MAX); + + // The single lazy removal should have removed all queued tries + for trie in tries.iter() { + assert_matches!(child::get::(trie, &[99]), None); + } + + // insert and delete counter values should go from u32::MAX - 1 to 1 + assert_eq!(>::get().as_test_tuple(), (1, 1)); + }) + } + #[test] + fn refcounter() { + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Create two contracts with the same code and check that they do in fact share it. + let 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 Contract { addr: addr2, .. } = builder::bare_instantiate(Code::Existing(code_hash)) + .value(min_balance * 100) + .salt(Some([2; 32])) + .build_and_unwrap_contract(); + assert_refcount!(code_hash, 3); + + // Terminating one contract should decrement the refcount + assert_ok!(builder::call(addr0).build()); + assert_refcount!(code_hash, 2); + + // remove another one + assert_ok!(builder::call(addr1).build()); + assert_refcount!(code_hash, 1); + + // Pristine code should still be there + PristineCode::::get(code_hash).unwrap(); + + // remove the last contract + assert_ok!(builder::call(addr2).build()); + assert_refcount!(code_hash, 0); + + // refcount is `0` but code should still exists because it needs to be removed manually + assert!(crate::PristineCode::::contains_key(&code_hash)); + }); + } + + #[test] + fn debug_message_works() { + let (wasm, _code_hash) = compile_module("debug_message_works").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_contract(); + let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); + + assert_matches!(result.result, Ok(_)); + assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!"); + }); + } + + #[test] + fn debug_message_logging_disabled() { + let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_contract(); + // the dispatchables always run without debugging + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr, + 0, + GAS_LIMIT, + deposit_limit::(), + vec![] + )); + }); + } + + #[test] + fn debug_message_invalid_utf8() { + let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(30_000) + .build_and_unwrap_contract(); + let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); + assert_ok!(result.result); + assert!(result.debug_message.is_empty()); + }); + } + + #[test] + fn gas_estimation_for_subcalls() { + let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap(); + let (call_runtime_code, _caller_hash) = compile_module("call_runtime").unwrap(); + let (dummy_code, _callee_hash) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 2_000 * min_balance); + + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + let Contract { addr: addr_dummy, .. } = + builder::bare_instantiate(Code::Upload(dummy_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + 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 = [ + Weight::zero(), + GAS_LIMIT, + GAS_LIMIT * 2, + GAS_LIMIT / 5, + Weight::from_parts(0, GAS_LIMIT.proof_size()), + Weight::from_parts(GAS_LIMIT.ref_time(), 0), + ]; + + // This call is passed to the sub call in order to create a large `required_weight` + let runtime_call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge { + pre_charge: Weight::from_parts(10_000_000_000, 512 * 1024), + actual_weight: Weight::from_parts(1, 1), + }) + .encode(); + + // Encodes which contract should be sub called with which input + let sub_calls: [(&[u8], Vec<_>, bool); 2] = [ + (addr_dummy.as_ref(), vec![], false), + (addr_call_runtime.as_ref(), runtime_call, true), + ]; + + for weight in weights { + for (sub_addr, sub_input, out_of_gas_in_subcall) in &sub_calls { + let input: Vec = sub_addr + .iter() + .cloned() + .chain(weight.ref_time().to_le_bytes()) + .chain(weight.proof_size().to_le_bytes()) + .chain(sub_input.clone()) + .collect(); + + // Call in order to determine the gas that is required for this call + let result_orig = builder::bare_call(addr_caller).data(input.clone()).build(); + assert_ok!(&result_orig.result); + + // If the out of gas happens in the subcall the caller contract + // will just trap. Otherwise we would need to forward an error + // code to signal that the sub contract ran out of gas. + let error: DispatchError = if *out_of_gas_in_subcall { + assert!(result_orig.gas_required.all_gt(result_orig.gas_consumed)); + >::ContractTrapped.into() + } else { + assert_eq!(result_orig.gas_required, result_orig.gas_consumed); + >::OutOfGas.into() + }; + + // Make the same call using the estimated gas. Should succeed. + let result = builder::bare_call(addr_caller) + .gas_limit(result_orig.gas_required) + .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) + .data(input.clone()) + .build(); + assert_ok!(&result.result); + + // Check that it fails with too little ref_time + let result = builder::bare_call(addr_caller) + .gas_limit(result_orig.gas_required.sub_ref_time(1)) + .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) + .data(input.clone()) + .build(); + assert_err!(result.result, error); + + // Check that it fails with too little proof_size + let result = builder::bare_call(addr_caller) + .gas_limit(result_orig.gas_required.sub_proof_size(1)) + .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) + .data(input.clone()) + .build(); + assert_err!(result.result, error); + } + } + }); + } + + #[test] + fn gas_estimation_call_runtime() { + let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + + let 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. + let call = RuntimeCall::Dummy(pallet_dummy::Call::overestimate_pre_charge { + pre_charge: Weight::from_parts(10_000_000, 1_000), + actual_weight: Weight::from_parts(100, 100), + }); + let result = builder::bare_call(addr_caller).data(call.encode()).build(); + // contract encodes the result of the dispatch runtime + let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap(); + assert_eq!(outcome, 0); + assert!(result.gas_required.all_gt(result.gas_consumed)); + + // Make the same call using the required gas. Should succeed. + assert_ok!( + builder::bare_call(addr_caller) + .gas_limit(result.gas_required) + .data(call.encode()) + .build() + .result + ); + }); + } + + #[test] + fn call_runtime_reentrancy_guarded() { + let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap(); + let (callee_code, _callee_hash) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); + + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt(Some([0; 32])) + .build_and_unwrap_contract(); + + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(callee_code)) + .value(min_balance * 100) + .salt(Some([1; 32])) + .build_and_unwrap_contract(); + + // Call pallet_revive call() dispatchable + let call = RuntimeCall::Contracts(crate::Call::call { + dest: addr_callee, + value: 0, + gas_limit: GAS_LIMIT / 3, + storage_deposit_limit: deposit_limit::(), + data: vec![], + }); + + // Call runtime to re-enter back to contracts engine by + // calling dummy contract + let result = + builder::bare_call(addr_caller).data(call.encode()).build_and_unwrap_result(); + // Call to runtime should fail because of the re-entrancy guard + assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed); + }); + } + + #[test] + fn ecdsa_recover() { + let (wasm, _code_hash) = compile_module("ecdsa_recover").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the ecdsa_recover contract. + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_contract(); + + #[rustfmt::skip] + let signature: [u8; 65] = [ + 161, 234, 203, 74, 147, 96, 51, 212, 5, 174, 231, 9, 142, 48, 137, 201, + 162, 118, 192, 67, 239, 16, 71, 216, 125, 86, 167, 139, 70, 7, 86, 241, + 33, 87, 154, 251, 81, 29, 160, 4, 176, 239, 88, 211, 244, 232, 232, 52, + 211, 234, 100, 115, 230, 47, 80, 44, 152, 166, 62, 50, 8, 13, 86, 175, + 28, + ]; + #[rustfmt::skip] + let message_hash: [u8; 32] = [ + 162, 28, 244, 179, 96, 76, 244, 178, 188, 83, 230, 248, 143, 106, 77, 117, + 239, 95, 244, 171, 65, 95, 62, 153, 174, 166, 182, 28, 130, 73, 196, 208 + ]; + #[rustfmt::skip] + const EXPECTED_COMPRESSED_PUBLIC_KEY: [u8; 33] = [ + 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, + 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, + 152, + ]; + let mut params = vec![]; + params.extend_from_slice(&signature); + params.extend_from_slice(&message_hash); + assert!(params.len() == 65 + 32); + let result = builder::bare_call(addr).data(params).build_and_unwrap_result(); + assert!(!result.did_revert()); + assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY); + }) + } + + #[test] + fn bare_instantiate_returns_events() { + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let result = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .collect_events(CollectEvents::UnsafeCollect) + .build(); + + let events = result.events.unwrap(); + assert!(!events.is_empty()); + assert_eq!(events, System::events()); + }); + } + + #[test] + fn bare_instantiate_does_not_return_events() { + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let result = + builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build(); + + let events = result.events; + assert!(!System::events().is_empty()); + assert!(events.is_none()); + }); + } + + #[test] + fn bare_call_returns_events() { + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + let result = + builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build(); + + let events = result.events.unwrap(); + assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); + assert!(!events.is_empty()); + assert_eq!(events, System::events()); + }); + } + + #[test] + fn bare_call_does_not_return_events() { + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let min_balance = Contracts::min_balance(); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(min_balance * 100) + .build_and_unwrap_contract(); + + let result = builder::bare_call(addr).build(); + + let events = result.events; + assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); + assert!(!System::events().is_empty()); + assert!(events.is_none()); + }); + } + + #[test] + fn sr25519_verify() { + let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap(); + + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the sr25519_verify contract. + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) + .value(100_000) + .build_and_unwrap_contract(); + + let call_with = |message: &[u8; 11]| { + // Alice's signature for "hello world" + #[rustfmt::skip] + let signature: [u8; 64] = [ + 184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247, + 99, 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83, + 85, 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255, + 228, 54, 115, 63, 30, 207, 205, 131, + ]; + + // Alice's public key + #[rustfmt::skip] + let public_key: [u8; 32] = [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, + 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, + ]; + + let mut params = vec![]; + params.extend_from_slice(&signature); + params.extend_from_slice(&public_key); + params.extend_from_slice(message); + + builder::bare_call(addr).data(params).build_and_unwrap_result() + }; + + // verification should succeed for "hello world" + assert_return_code!(call_with(&b"hello world"), RuntimeReturnCode::Success); + + // verification should fail for other messages + assert_return_code!(call_with(&b"hello worlD"), RuntimeReturnCode::Sr25519VerifyFailed); + }); + } + + #[test] + fn failed_deposit_charge_should_roll_back_call() { + let (wasm_caller, _) = compile_module("call_runtime_and_call").unwrap(); + let (wasm_callee, _) = compile_module("store_call").unwrap(); + const ED: u64 = 200; + + let execute = || { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate both contracts. + let 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), + caller.account_id.clone(), + (), + 0 + )); + + // Create a Proxy call that will attempt to transfer away Alice's balance. + let transfer_call = + Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { + dest: CHARLIE, + value: pallet_balances::Pallet::::free_balance(&ALICE) - 2 * ED, + })); + + // Wrap the transfer call in a proxy call. + let transfer_proxy_call = RuntimeCall::Proxy(pallet_proxy::Call::proxy { + real: ALICE, + force_proxy_type: Some(()), + call: transfer_call, + }); + + let data = ( + (ED - DepositPerItem::get()) as u32, // storage length + addr_callee, + transfer_proxy_call, + ); + + builder::call(caller.addr).data(data.encode()).build() + }) + }; + + // With a low enough deposit per byte, the call should succeed. + let result = execute().unwrap(); + + // Bump the deposit per byte to a high value to trigger a FundsUnavailable error. + DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 20); + assert_err_with_weight!(execute(), TokenError::FundsUnavailable, result.actual_weight); + } + + #[test] + fn upload_code_works() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert!(!PristineCode::::contains_key(&code_hash)); + + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE_ADDR + }), + topics: vec![], + },] + ); + }); + } + + #[test] + fn upload_code_limit_too_low() { + let (wasm, _code_hash) = compile_module("dummy").unwrap(); + let deposit_expected = expected_deposit(wasm.len()); + let deposit_insufficient = deposit_expected.saturating_sub(1); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, deposit_insufficient,), + >::StorageDepositLimitExhausted, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn upload_code_not_enough_balance() { + let (wasm, _code_hash) = compile_module("dummy").unwrap(); + let deposit_expected = expected_deposit(wasm.len()); + let deposit_insufficient = deposit_expected.saturating_sub(1); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, deposit_insufficient); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,), + >::StorageDepositNotEnoughFunds, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn remove_code_works() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash)); + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE_ADDR + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { + code_hash, + deposit_released: deposit_expected, + remover: ALICE_ADDR + }), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn remove_code_wrong_origin() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); + // Ensure the contract was stored and get expected deposit amount to be reserved. + let deposit_expected = expected_deposit(ensure_stored(code_hash)); + + assert_noop!( + Contracts::remove_code(RuntimeOrigin::signed(BOB), code_hash), + sp_runtime::traits::BadOrigin, + ); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE_ADDR + }), + topics: vec![], + },] + ); + }); + } + + #[test] + fn remove_code_in_use() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + assert_ok!(builder::instantiate_with_code(wasm).build()); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), + >::CodeInUse, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn remove_code_not_found() { + let (_wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Drop previous events + initialize_block(2); + + assert_noop!( + Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), + >::CodeNotFound, + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn instantiate_with_zero_balance_works() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Drop previous events + initialize_block(2); + + // Instantiate the BOB contract. + let 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(&account_id), min_balance); + assert_eq!( + ::Currency::total_balance(&account_id), + min_balance + test_utils::contract_info_storage_deposit(&addr) + ); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE_ADDR + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: account_id.clone(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: account_id.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id, + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: ALICE_ADDR, + contract: addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE_ADDR, + to: addr, + amount: test_utils::contract_info_storage_deposit(&addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn instantiate_with_below_existential_deposit_works() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + let value = 50; + + // Drop previous events + initialize_block(2); + + // Instantiate the BOB contract. + let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm)) + .value(value) + .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(&account_id), min_balance + value); + assert_eq!( + ::Currency::total_balance(&account_id), + min_balance + value + test_utils::contract_info_storage_deposit(&addr) + ); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE_ADDR + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::System(frame_system::Event::NewAccount { + account: account_id.clone() + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: account_id.clone(), + free_balance: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id.clone(), + amount: min_balance, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id.clone(), + amount: 50, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Instantiated { + deployer: ALICE_ADDR, + contract: addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE_ADDR, + to: addr, + amount: test_utils::contract_info_storage_deposit(&addr), + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn storage_deposit_works() { + let (wasm, _code_hash) = compile_module("multi_store").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); + + let mut deposit = test_utils::contract_info_storage_deposit(&addr); + + // Drop previous events + initialize_block(2); + + // Create storage + 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).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).data((10u32, 20u32).encode()).build()); + // -1 for numeric instability + let refunded0 = 90 - 10 - 1; + deposit -= refunded0; + assert_eq!(get_contract(&addr).total_deposit(), deposit); + + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id.clone(), + amount: 42, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE_ADDR, + to: addr, + amount: charged0, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndHeld { + from: ALICE_ADDR, + to: addr, + amount: charged1, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts( + pallet_revive::Event::StorageDepositTransferredAndReleased { + from: addr, + to: ALICE_ADDR, + amount: refunded0, + } + ), + topics: vec![], + }, + ] + ); + }); + } + + #[test] + fn storage_deposit_callee_works() { + let (wasm_caller, _code_hash_caller) = compile_module("call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Create both contracts: Constructors do nothing. + let 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(&account_id), min_balance); + assert_eq!( + callee.total_deposit(), + deposit + test_utils::contract_info_storage_deposit(&addr_callee) + ); + }); + } + + #[test] + fn set_code_extrinsic() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + let (new_wasm, new_code_hash) = compile_module("crypto_hashes").unwrap(); + + assert_ne!(code_hash, new_code_hash); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); + + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + new_wasm, + deposit_limit::(), + )); + + // Drop previous events + initialize_block(2); + + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + + // only root can execute this extrinsic + assert_noop!( + Contracts::set_code(RuntimeOrigin::signed(ALICE), addr, new_code_hash), + sp_runtime::traits::BadOrigin, + ); + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + assert_eq!(System::events(), vec![]); + + // contract must exist + assert_noop!( + Contracts::set_code(RuntimeOrigin::root(), BOB_ADDR, new_code_hash), + >::ContractNotFound, + ); + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + assert_eq!(System::events(), vec![]); + + // new code hash must exist + assert_noop!( + Contracts::set_code(RuntimeOrigin::root(), addr, Default::default()), + >::CodeNotFound, + ); + assert_eq!(get_contract(&addr).code_hash, code_hash); + assert_refcount!(&code_hash, 1); + assert_refcount!(&new_code_hash, 0); + assert_eq!(System::events(), vec![]); + + // successful call + assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr, new_code_hash)); + assert_eq!(get_contract(&addr).code_hash, new_code_hash); + assert_refcount!(&code_hash, 0); + assert_refcount!(&new_code_hash, 1); + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated { + contract: addr, + new_code_hash, + old_code_hash: code_hash, + }), + topics: vec![], + },] + ); + }); + } + + #[test] + fn slash_cannot_kill_account() { + let (wasm, _code_hash) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let value = 700; + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm)) + .value(value) + .build_and_unwrap_contract(); + + // Drop previous events + initialize_block(2); + + let info_deposit = test_utils::contract_info_storage_deposit(&addr); + + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &account_id + ), + info_deposit + ); + + assert_eq!( + ::Currency::total_balance(&account_id), + info_deposit + value + min_balance + ); + + // Try to destroy the account of the contract by slashing the total balance. + // The account does not get destroyed because slashing only affects the balance held + // under certain `reason`. Slashing can for example happen if the contract takes part + // in staking. + let _ = ::Currency::slash( + &HoldReason::StorageDepositReserve.into(), + &account_id, + ::Currency::total_balance(&account_id), + ); + + // Slashing only removed the balance held. + assert_eq!(::Currency::total_balance(&account_id), value + min_balance); + }); + } + + #[test] + fn contract_reverted() { + let (wasm, code_hash) = compile_module("return_with_data").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let flags = ReturnFlags::REVERT; + let buffer = [4u8, 8, 15, 16, 23, 42]; + let input = (flags.bits(), buffer).encode(); + + // We just upload the code for later use + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + )); + + // Calling extrinsic: revert leads to an error + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).data(input.clone()).build(), + >::ContractReverted, + ); + + // Calling extrinsic: revert leads to an error + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).data(input.clone()).build(), + >::ContractReverted, + ); + + // Calling directly: revert leads to success but the flags indicate the error + // This is just a different way of transporting the error that allows the read out + // the `data` which is only there on success. Obviously, the contract isn't + // instantiated. + let result = builder::bare_instantiate(Code::Existing(code_hash)) + .data(input.clone()) + .build_and_unwrap_result(); + assert_eq!(result.result.flags, flags); + assert_eq!(result.result.data, buffer); + assert!(!>::contains_key(result.addr)); + + // Pass empty flags and therefore successfully instantiate the contract for later use. + let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash)) + .data(ReturnFlags::empty().bits().encode()) + .build_and_unwrap_contract(); + + // Calling extrinsic: revert leads to an error + assert_err_ignore_postinfo!( + 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).data(input).build_and_unwrap_result(); + assert_eq!(result.flags, flags); + assert_eq!(result.data, buffer); + }); + } + + #[test] + fn set_code_hash() { + let (wasm, code_hash) = compile_module("set_code_hash").unwrap(); + let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the 'caller' + let Contract { addr: 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), + new_wasm.clone(), + deposit_limit::(), + )); + + System::reset_events(); + + // First call sets new code_hash and returns 1 + 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) + .debug(DebugInfo::UnsafeDebug) + .build_and_unwrap_result(); + assert_return_code!(result, 2); + + // Checking for the last event only + assert_eq!( + &System::events(), + &[ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated { + contract: contract_addr, + new_code_hash, + old_code_hash: code_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: contract_addr, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::Called { + caller: Origin::from_account_id(ALICE), + contract: contract_addr, + }), + topics: vec![], + }, + ], + ); + }); + } + + #[test] + fn storage_deposit_limit_is_enforced() { + let (wasm, _code_hash) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let min_balance = Contracts::min_balance(); + + // Setting insufficient storage_deposit should fail. + assert_err!( + builder::bare_instantiate(Code::Upload(wasm.clone())) + // expected deposit is 2 * ed + 3 for the call + .storage_deposit_limit((2 * min_balance + 3 - 1).into()) + .build() + .result, + >::StorageDepositLimitExhausted, + ); + + // Instantiate the BOB contract. + let 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(&account_id), + info_deposit + min_balance + ); + + // Create 1 byte of storage with a price of per byte, + // setting insufficient deposit limit, as it requires 3 Balance: + // 2 for the item added + 1 for the new storage item. + assert_err_ignore_postinfo!( + builder::call(addr) + .storage_deposit_limit(2) + .data(1u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Create 1 byte of storage, should cost 3 Balance: + // 2 for the item added + 1 for the new storage item. + // Should pass as it fallbacks to DefaultDepositLimit. + assert_ok!(builder::call(addr) + .storage_deposit_limit(3) + .data(1u32.to_le_bytes().to_vec()) + .build()); + + // Use 4 more bytes of the storage for the same item, which requires 4 Balance. + // Should fail as DefaultDepositLimit is 3 and hence isn't enough. + assert_err_ignore_postinfo!( + builder::call(addr) + .storage_deposit_limit(3) + .data(5u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + }); + } + + #[test] + fn deposit_limit_in_nested_calls() { + let (wasm_caller, _code_hash_caller) = compile_module("create_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let 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) + .storage_deposit_limit(102) + .data(100u32.to_le_bytes().to_vec()) + .build()); + + // We do not remove any storage but add a storage item of 12 bytes in the caller + // contract. This would cost 12 + 2 = 14 Balance. + // The nested call doesn't get a special limit, which is set by passing 0 to it. + // This should fail as the specified parent's limit is less than the cost: 13 < + // 14. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .storage_deposit_limit(13) + .data((100u32, &addr_callee, U256::from(0u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Now we specify the parent's limit high enough to cover the caller's storage + // additions. However, we use a single byte more in the callee, hence the storage + // deposit should be 15 Balance. + // The nested call doesn't get a special limit, which is set by passing 0 to it. + // This should fail as the specified parent's limit is less than the cost: 14 + // < 15. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .storage_deposit_limit(14) + .data((101u32, &addr_callee, U256::from(0u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Now we specify the parent's limit high enough to cover both the caller's and callee's + // storage additions. However, we set a special deposit limit of 1 Balance for the + // nested call. This should fail as callee adds up 2 bytes to the storage, meaning + // that the nested call should have a deposit limit of at least 2 Balance. The + // sub-call should be rolled back, which is covered by the next test case. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .storage_deposit_limit(16) + .data((102u32, &addr_callee, U256::from(1u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Refund in the callee contract but not enough to cover the 14 Balance required by the + // caller. Note that if previous sub-call wouldn't roll back, this call would pass + // making the test case fail. We don't set a special limit for the nested call here. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .storage_deposit_limit(0) + .data((87u32, &addr_callee, U256::from(0u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + let _ = ::Currency::set_balance(&ALICE, 511); + + // Require more than the sender's balance. + // We don't set a special limit for the nested call. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .data((512u32, &addr_callee, U256::from(1u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + + // Same as above but allow for the additional deposit of 1 Balance in parent. + // We set the special deposit limit of 1 Balance for the nested call, which isn't + // enforced as callee frees up storage. This should pass. + assert_ok!(builder::call(addr_caller) + .storage_deposit_limit(1) + .data((87u32, &addr_callee, U256::from(1u64)).encode()) + .build()); + }); + } + + #[test] + fn deposit_limit_in_nested_instantiate() { + let (wasm_caller, _code_hash_caller) = + compile_module("create_storage_and_instantiate").unwrap(); + let (wasm_callee, code_hash_callee) = compile_module("store_deploy").unwrap(); + const ED: u64 = 5; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000_000); + // Create caller contract + let 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 Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm_callee)) + .data(vec![0, 0, 0, 0]) + .build_and_unwrap_contract(); + + let callee_info_len = ContractInfoOf::::get(&addr).unwrap().encoded_size() as u64; + + // We don't set a special deposit limit for the nested instantiation. + // + // The deposit limit set for the parent is insufficient for the instantiation, which + // requires: + // - callee_info_len + 2 for storing the new contract info, + // - ED for deployed contract account, + // - 2 for the storage item of 0 bytes being created in the callee constructor + // or (callee_info_len + 2 + ED + 2) Balance in total. + // + // Provided the limit is set to be 1 Balance less, + // this call should fail on the return from the caller contract. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 1) + .data((0u32, &code_hash_callee, U256::from(0u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Now we give enough limit for the instantiation itself, but require for 1 more storage + // byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on + // the return from constructor. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 2) + .data((1u32, &code_hash_callee, U256::from(0u64)).encode()) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Now we set enough limit in parent call, but an insufficient limit for child + // instantiate. This should fail during the charging for the instantiation in + // `RawMeter::charge_instantiate()` + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 2) + .data( + (0u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 1)) + .encode() + ) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Same as above but requires for single added storage + // item of 1 byte to be covered by the limit, which implies 3 more Balance. + // Now we set enough limit for the parent call, but insufficient limit for child + // instantiate. This should fail right after the constructor execution. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit + .data( + (1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 2)) + .encode() + ) + .build(), + >::StorageDepositLimitExhausted, + ); + // The charges made on the instantiation should be rolled back. + assert_eq!(::Currency::free_balance(&BOB), 1_000_000); + + // Set enough deposit limit for the child instantiate. This should succeed. + let result = builder::bare_call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(callee_info_len + 2 + ED + 4) + .data((1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3)).encode()) + .build(); + + let returned = result.result.unwrap(); + // All balance of the caller except ED has been transferred to the callee. + // No deposit has been taken from it. + assert_eq!(::Currency::free_balance(&caller_id), ED); + // Get address of the deployed contract. + 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(&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 + assert_eq!( + ::Currency::free_balance(&BOB), + 1_000_000 - (callee_info_len + 2 + ED + 3) + ); + // Check that deposit due to be charged still includes these 3 Balance + assert_eq!(result.storage_deposit.charge_or_zero(), (callee_info_len + 2 + ED + 3)) + }); + } + + #[test] + fn deposit_limit_honors_liquidity_restrictions() { + let (wasm, _code_hash) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let bobs_balance = 1_000; + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, bobs_balance); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let 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(&account_id), + info_deposit + min_balance + ); + + // check that the hold is honored + ::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &BOB, + bobs_balance - min_balance, + ) + .unwrap(); + assert_err_ignore_postinfo!( + builder::call(addr) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(10_000) + .data(100u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + assert_eq!(::Currency::free_balance(&BOB), min_balance); + }); + } + + #[test] + fn deposit_limit_honors_existential_deposit() { + let (wasm, _code_hash) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 300); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let 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(&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) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(10_000) + .data(100u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + assert_eq!(::Currency::free_balance(&BOB), 300); + }); + } + + #[test] + fn deposit_limit_honors_min_leftover() { + let (wasm, _code_hash) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000); + let min_balance = Contracts::min_balance(); + + // Instantiate the BOB contract. + let 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 and the + // storage deposit + assert_eq!(get_contract(&addr).total_deposit(), info_deposit); + assert_eq!( + ::Currency::total_balance(&account_id), + info_deposit + min_balance + ); + + // check that the minimum leftover (value send) is considered + // given the minimum deposit of 200 sending 750 will only leave + // 50 for the storage deposit. Which is not enough to store the 50 bytes + // as we also need 2 bytes for the item + assert_err_ignore_postinfo!( + builder::call(addr) + .origin(RuntimeOrigin::signed(BOB)) + .value(750) + .storage_deposit_limit(10_000) + .data(50u32.to_le_bytes().to_vec()) + .build(), + >::StorageDepositLimitExhausted, + ); + assert_eq!(::Currency::free_balance(&BOB), 1_000); + }); + } + + #[test] + fn locking_delegate_dependency_works() { + // set hash lock up deposit to 30%, to test deposit calculation. + CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); + + let (wasm_caller, self_code_hash) = compile_module("locking_delegate_dependency").unwrap(); + let callee_codes: Vec<_> = + (0..limits::DELEGATE_DEPENDENCIES + 1).map(|idx| dummy_unique(idx)).collect(); + let callee_hashes: Vec<_> = callee_codes + .iter() + .map(|c| sp_core::H256(sp_io::hashing::keccak_256(c))) + .collect(); + + // Define inputs with various actions to test locking / unlocking delegate_dependencies. + // See the contract for more details. + let noop_input = (0u32, callee_hashes[0]); + let lock_delegate_dependency_input = (1u32, callee_hashes[0]); + let unlock_delegate_dependency_input = (2u32, callee_hashes[0]); + let terminate_input = (3u32, callee_hashes[0]); + + // Instantiate the caller contract with the given input. + let instantiate = |input: &(u32, H256)| { + builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .origin(RuntimeOrigin::signed(ETH_ALICE)) + .data(input.encode()) + .build() + }; + + // Call contract with the given input. + 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); + + // Instantiate with lock_delegate_dependency should fail since the code is not yet on + // chain. + assert_err!( + instantiate(&lock_delegate_dependency_input).result, + Error::::CodeNotFound + ); + + // Upload all the delegated codes (they all have the same size) + let mut deposit = Default::default(); + for code in callee_codes.iter() { + let CodeUploadReturnValue { deposit: deposit_per_code, .. } = + Contracts::bare_upload_code( + RuntimeOrigin::signed(ETH_ALICE), + code.clone(), + deposit_limit::(), + ) + .unwrap(); + deposit = deposit_per_code; + } + + // Instantiate should now work. + 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); + + let dependency_deposit = &CodeHashLockupDepositPercent::get().mul_ceil(deposit); + assert_eq!( + contract.delegate_dependencies().get(&callee_hashes[0]), + Some(dependency_deposit) + ); + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &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(ETH_ALICE), callee_hashes[0]), + >::CodeInUse + ); + + // Locking an already existing dependency should fail. + assert_err!( + call(&addr_caller, &lock_delegate_dependency_input).result, + Error::::DelegateDependencyAlreadyExists + ); + + // Locking self should fail. + assert_err!( + call(&addr_caller, &(1u32, self_code_hash)).result, + Error::::CannotAddSelfAsDelegateDependency + ); + + // Locking more than the maximum allowed delegate_dependencies should fail. + for hash in &callee_hashes[1..callee_hashes.len() - 1] { + call(&addr_caller, &(1u32, *hash)).result.unwrap(); + } + assert_err!( + call(&addr_caller, &(1u32, *callee_hashes.last().unwrap())).result, + Error::::MaxDelegateDependenciesReached + ); + + // Unlocking all dependency should work. + for hash in &callee_hashes[..callee_hashes.len() - 1] { + call(&addr_caller, &(2u32, *hash)).result.unwrap(); + } + + // Dependency should be removed, and deposit should be returned. + let contract = test_utils::get_contract(&addr_caller); + assert!(contract.delegate_dependencies().is_empty()); + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &caller_account_id + ), + contract.storage_base_deposit() + ); + + // Removing a nonexistent dependency should fail. + assert_err!( + call(&addr_caller, &unlock_delegate_dependency_input).result, + Error::::DelegateDependencyNotFound + ); + + // Locking a dependency with a storage limit too low should fail. + assert_err!( + builder::bare_call(addr_caller) + .storage_deposit_limit(dependency_deposit - 1) + .data(lock_delegate_dependency_input.encode()) + .build() + .result, + Error::::StorageDepositLimitExhausted + ); + + // Since we unlocked the dependency we should now be able to remove the code. + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(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(ETH_ALICE), + callee_codes[0].clone(), + deposit_limit::(), + ) + .unwrap(); + call(&addr_caller, &lock_delegate_dependency_input).result.unwrap(); + + // Call terminate should work, and return the deposit. + let balance_before = test_utils::get_balance(Ð_ALICE); + assert_ok!(call(&addr_caller, &terminate_input).result); + assert_eq!( + test_utils::get_balance(Ð_ALICE), + ED + balance_before + contract.storage_base_deposit() + dependency_deposit + ); + + // Terminate should also remove the dependency, so we can remove the code. + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(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(); + + // Set hash lock up deposit to 30%, to test deposit calculation. + CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); + + // Test with both existing and uploaded code + for code in [Code::Upload(wasm.clone()), Code::Existing(code_hash)] { + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let lockup_deposit_percent = CodeHashLockupDepositPercent::get(); + + // Upload the dummy contract, + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + dummy_wasm.clone(), + deposit_limit::(), + ) + .unwrap(); + + // Upload `set_code_hash` contracts if using Code::Existing. + let add_upload_deposit = match code { + Code::Existing(_) => { + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + ) + .unwrap(); + false + }, + Code::Upload(_) => true, + }; + + // Instantiate the set_code_hash contract. + let res = builder::bare_instantiate(code).build(); + + let addr = res.result.unwrap().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(); + + // Check initial storage_deposit + // The base deposit should be: contract_info_storage_deposit + 30% * deposit + let deposit = + extra_deposit + base_deposit + lockup_deposit_percent.mul_ceil(upload_deposit); + + assert_eq!( + res.storage_deposit.charge_or_zero(), + deposit + Contracts::min_balance() + ); + + // call set_code_hash + builder::bare_call(addr) + .data(dummy_code_hash.encode()) + .build_and_unwrap_result(); + + // Check updated storage_deposit + let code_deposit = test_utils::get_code_deposit(&dummy_code_hash); + let deposit = base_deposit + lockup_deposit_percent.mul_ceil(code_deposit); + assert_eq!(test_utils::get_contract(&addr).storage_base_deposit(), deposit); + + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &account_id + ), + deposit + ); + }); + } + } + + #[test] + fn root_cannot_upload_code() { + let (wasm, _) = compile_module("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::upload_code(RuntimeOrigin::root(), wasm, deposit_limit::()), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn root_cannot_remove_code() { + let (_, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::remove_code(RuntimeOrigin::root(), code_hash), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn signed_cannot_set_code() { + let (_, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn none_cannot_call_code() { + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + builder::call(BOB_ADDR).origin(RuntimeOrigin::none()).build(), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn root_can_call() { + let (wasm, _) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); + + // Call the contract. + assert_ok!(builder::call(addr).origin(RuntimeOrigin::root()).build()); + }); + } + + #[test] + fn root_cannot_instantiate_with_code() { + let (wasm, _) = compile_module("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + builder::instantiate_with_code(wasm).origin(RuntimeOrigin::root()).build(), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn root_cannot_instantiate() { + let (_, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).origin(RuntimeOrigin::root()).build(), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn only_upload_origin_can_upload() { + let (wasm, _) = compile_module("dummy").unwrap(); + UploadAccount::set(Some(ALICE)); + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(&BOB, 1_000_000); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::root(), + wasm.clone(), + deposit_limit::(), + ), + DispatchError::BadOrigin + ); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(BOB), + wasm.clone(), + deposit_limit::(), + ), + DispatchError::BadOrigin + ); + + // Only alice is allowed to upload contract code. + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + )); + }); + } + + #[test] + fn only_instantiation_origin_can_instantiate() { + let (code, code_hash) = compile_module("dummy").unwrap(); + InstantiateAccount::set(Some(ALICE)); + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(&BOB, 1_000_000); + + assert_err_ignore_postinfo!( + builder::instantiate_with_code(code.clone()) + .origin(RuntimeOrigin::root()) + .build(), + DispatchError::BadOrigin + ); + + assert_err_ignore_postinfo!( + builder::instantiate_with_code(code.clone()) + .origin(RuntimeOrigin::signed(BOB)) + .build(), + DispatchError::BadOrigin + ); + + // Only Alice can instantiate + assert_ok!(builder::instantiate_with_code(code).build()); + + // Bob cannot instantiate with either `instantiate_with_code` or `instantiate`. + assert_err_ignore_postinfo!( + builder::instantiate(code_hash).origin(RuntimeOrigin::signed(BOB)).build(), + DispatchError::BadOrigin + ); + }); + } + + #[test] + fn balance_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(); + 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 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).value(value).build()); + + let value = 1; + // Calling with value will trap the contract. + assert_err_ignore_postinfo!( + builder::call(addr).value(value).build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn gas_consumed_is_linear_for_nested_calls() { + let (code, _code_hash) = compile_module("recurse").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let 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).data(i.encode()).build(); + assert_ok!(result.result); + result.gas_consumed + }) + .collect::>() + .try_into() + .unwrap() + }; + + let gas_per_recursion = gas_2.checked_sub(&gas_1).unwrap(); + assert_eq!(gas_max, gas_0 + gas_per_recursion * limits::CALL_STACK_DEPTH as u64); + }); + } + + #[test] + fn read_only_call_cannot_store() { + let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let 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!( + builder::call(addr_caller).data((&addr_callee, 100u32).encode()).build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn read_only_call_cannot_transfer() { + let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let 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!( + builder::call(addr_caller) + .data( + (addr_callee, pallet_revive_uapi::CallFlags::READ_ONLY.bits(), 100u64) + .encode() + ) + .build(), + >::StateChangeDenied + ); + }); + } + + #[test] + fn read_only_subsequent_call_cannot_store() { + let (wasm_read_only_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create contracts: Constructors do nothing. + let 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); + + // Read-only call fails when modifying storage. + assert_err_ignore_postinfo!( + builder::call(addr_caller) + .data((&addr_subsequent_caller, input).encode()) + .build(), + >::ContractTrapped + ); + }); + } + + #[test] + fn read_only_call_works() { + let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create both contracts: Constructors do nothing. + let 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); + }); + } +} diff --git a/substrate/frame/revive/src/tests/pallet_dummy.rs b/substrate/frame/revive/src/tests/pallet_dummy.rs new file mode 100644 index 0000000000000000000000000000000000000000..2af8475d17eda39854a8b82555b2102e9d67948f --- /dev/null +++ b/substrate/frame/revive/src/tests/pallet_dummy.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use pallet::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use frame_support::{ + dispatch::{Pays, PostDispatchInfo}, + ensure, + pallet_prelude::DispatchResultWithPostInfo, + weights::Weight, + }; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::call] + impl Pallet { + /// Dummy function that overcharges the predispatch weight, allowing us to test the correct + /// values of [`ContractResult::gas_consumed`] and [`ContractResult::gas_required`] in + /// tests. + #[pallet::call_index(1)] + #[pallet::weight(*pre_charge)] + pub fn overestimate_pre_charge( + origin: OriginFor, + pre_charge: Weight, + actual_weight: Weight, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + ensure!(pre_charge.any_gt(actual_weight), "pre_charge must be > actual_weight"); + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + } +} diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs new file mode 100644 index 0000000000000000000000000000000000000000..1e94d5cafb81344793ee19602d0c6c16bb818b05 --- /dev/null +++ b/substrate/frame/revive/src/tests/test_debug.rs @@ -0,0 +1,244 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; + +use crate::{ + debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, + primitives::ExecReturnValue, + test_utils::*, +}; +use frame_support::traits::Currency; +use sp_core::H160; +use std::cell::RefCell; + +#[derive(Clone, PartialEq, Eq, Debug)] +struct DebugFrame { + contract_address: sp_core::H160, + call: ExportedFunction, + input: Vec, + result: Option>, +} + +thread_local! { + static DEBUG_EXECUTION_TRACE: RefCell> = RefCell::new(Vec::new()); + static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); +} + +pub struct TestDebug; +pub struct TestCallSpan { + contract_address: sp_core::H160, + call: ExportedFunction, + input: Vec, +} + +impl Tracing for TestDebug { + type CallSpan = TestCallSpan; + + fn new_call_span( + contract_address: &crate::H160, + entry_point: ExportedFunction, + input_data: &[u8], + ) -> TestCallSpan { + DEBUG_EXECUTION_TRACE.with(|d| { + d.borrow_mut().push(DebugFrame { + contract_address: *contract_address, + call: entry_point, + input: input_data.to_vec(), + result: None, + }) + }); + TestCallSpan { + contract_address: *contract_address, + call: entry_point, + input: input_data.to_vec(), + } + } +} + +impl CallInterceptor for TestDebug { + fn intercept_call( + contract_address: &sp_core::H160, + _entry_point: ExportedFunction, + _input_data: &[u8], + ) -> Option { + INTERCEPTED_ADDRESS.with(|i| { + if i.borrow().as_ref() == Some(contract_address) { + Some(Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![] })) + } else { + None + } + }) + } +} + +impl CallSpan for TestCallSpan { + fn after_call(self, output: &ExecReturnValue) { + DEBUG_EXECUTION_TRACE.with(|d| { + d.borrow_mut().push(DebugFrame { + contract_address: self.contract_address, + call: self.call, + input: self.input, + result: Some(output.data.clone()), + }) + }); + } +} + +/// We can only run the tests if we have a riscv toolchain installed +#[cfg(feature = "riscv")] +mod run_tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn debugging_works() { + let (wasm_caller, _) = compile_module("call").unwrap(); + let (wasm_callee, _) = compile_module("store_call").unwrap(); + + fn current_stack() -> Vec { + DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone()) + } + + fn deploy(wasm: Vec) -> H160 { + Contracts::bare_instantiate( + RuntimeOrigin::signed(ALICE), + 0, + GAS_LIMIT, + deposit_limit::(), + Code::Upload(wasm), + vec![], + Some([0u8; 32]), + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .addr + } + + fn constructor_frame(contract_address: &H160, after: bool) -> DebugFrame { + DebugFrame { + contract_address: *contract_address, + call: ExportedFunction::Constructor, + input: vec![], + result: if after { Some(vec![]) } else { None }, + } + } + + fn call_frame(contract_address: &H160, args: Vec, after: bool) -> DebugFrame { + DebugFrame { + contract_address: *contract_address, + call: ExportedFunction::Call, + input: args, + result: if after { Some(vec![]) } else { None }, + } + } + + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + assert_eq!(current_stack(), vec![]); + + let addr_caller = deploy(wasm_caller); + let addr_callee = deploy(wasm_callee); + + assert_eq!( + current_stack(), + vec![ + constructor_frame(&addr_caller, false), + constructor_frame(&addr_caller, true), + constructor_frame(&addr_callee, false), + constructor_frame(&addr_callee, true), + ] + ); + + let main_args = (100u32, &addr_callee.clone()).encode(); + let inner_args = (100u32).encode(); + + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + addr_caller, + 0, + GAS_LIMIT, + deposit_limit::(), + main_args.clone() + )); + + let stack_top = current_stack()[4..].to_vec(); + assert_eq!( + stack_top, + vec![ + call_frame(&addr_caller, main_args.clone(), false), + call_frame(&addr_callee, inner_args.clone(), false), + call_frame(&addr_callee, inner_args, true), + call_frame(&addr_caller, main_args, true), + ] + ); + }); + } + + #[test] + fn call_interception_works() { + let (wasm, _) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::deposit_creating(&ALICE, 1_000_000); + + let account_id = Contracts::bare_instantiate( + RuntimeOrigin::signed(ALICE), + 0, + GAS_LIMIT, + deposit_limit::(), + Code::Upload(wasm), + vec![], + // some salt to ensure that the address of this contract is unique among all tests + Some([0x41; 32]), + DebugInfo::Skip, + CollectEvents::Skip, + ) + .result + .unwrap() + .addr; + + // no interception yet + assert_ok!(Contracts::call( + RuntimeOrigin::signed(ALICE), + account_id, + 0, + GAS_LIMIT, + deposit_limit::(), + vec![], + )); + + // intercept calls to this contract + INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id)); + + assert_err_ignore_postinfo!( + Contracts::call( + RuntimeOrigin::signed(ALICE), + account_id, + 0, + GAS_LIMIT, + deposit_limit::(), + vec![], + ), + >::ContractReverted, + ); + }); + } +} diff --git a/substrate/frame/revive/src/transient_storage.rs b/substrate/frame/revive/src/transient_storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..298e0296fe69e383567c951408fbf68155fc5810 --- /dev/null +++ b/substrate/frame/revive/src/transient_storage.rs @@ -0,0 +1,691 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module contains routines for accessing and altering a contract transient storage. + +use crate::{ + exec::{AccountIdOf, Key}, + storage::WriteOutcome, + Config, Error, +}; +use codec::Encode; +use core::marker::PhantomData; +use frame_support::DefaultNoBound; +use sp_runtime::{DispatchError, DispatchResult, Saturating}; +use sp_std::{collections::btree_map::BTreeMap, mem, vec::Vec}; + +/// Meter entry tracks transaction allocations. +#[derive(Default, Debug)] +pub struct MeterEntry { + /// Allocations made in the current transaction. + pub amount: u32, + /// Allocations limit in the current transaction. + pub limit: u32, +} + +impl MeterEntry { + /// Create a new entry. + fn new(limit: u32) -> Self { + Self { limit, amount: Default::default() } + } + + /// Check if the allocated amount exceeds the limit. + fn exceeds_limit(&self, amount: u32) -> bool { + self.amount.saturating_add(amount) > self.limit + } + + /// Absorb the allocation amount of the nested entry into the current entry. + fn absorb(&mut self, rhs: Self) { + self.amount.saturating_accrue(rhs.amount) + } +} + +// The storage meter enforces a limit for each transaction, +// which is calculated as free_storage * (1 - 1/16) for each subsequent frame. +#[derive(DefaultNoBound)] +pub struct StorageMeter { + nested_meters: Vec, + root_meter: MeterEntry, + _phantom: PhantomData, +} + +impl StorageMeter { + const STORAGE_FRACTION_DENOMINATOR: u32 = 16; + /// Create a new storage allocation meter. + fn new(memory_limit: u32) -> Self { + Self { root_meter: MeterEntry::new(memory_limit), ..Default::default() } + } + + /// Charge the allocated amount of transaction storage from the meter. + fn charge(&mut self, amount: u32) -> DispatchResult { + let meter = self.current_mut(); + if meter.exceeds_limit(amount) { + return Err(Error::::OutOfTransientStorage.into()); + } + meter.amount.saturating_accrue(amount); + Ok(()) + } + + /// Revert a transaction meter. + fn revert(&mut self) { + self.nested_meters.pop().expect( + "A call to revert a meter must be preceded by a corresponding call to start a meter; + the code within this crate makes sure that this is always the case; qed", + ); + } + + /// Start a transaction meter. + fn start(&mut self) { + let meter = self.current(); + let mut transaction_limit = meter.limit.saturating_sub(meter.amount); + if !self.nested_meters.is_empty() { + // Allow use of (1 - 1/STORAGE_FRACTION_DENOMINATOR) of free storage for subsequent + // calls. + transaction_limit.saturating_reduce( + transaction_limit.saturating_div(Self::STORAGE_FRACTION_DENOMINATOR), + ); + } + + self.nested_meters.push(MeterEntry::new(transaction_limit)); + } + + /// Commit a transaction meter. + fn commit(&mut self) { + let transaction_meter = self.nested_meters.pop().expect( + "A call to commit a meter must be preceded by a corresponding call to start a meter; + the code within this crate makes sure that this is always the case; qed", + ); + self.current_mut().absorb(transaction_meter) + } + + /// The total allocated amount of memory. + #[cfg(test)] + fn total_amount(&self) -> u32 { + self.nested_meters + .iter() + .fold(self.root_meter.amount, |acc, e| acc.saturating_add(e.amount)) + } + + /// A mutable reference to the current meter entry. + pub fn current_mut(&mut self) -> &mut MeterEntry { + self.nested_meters.last_mut().unwrap_or(&mut self.root_meter) + } + + /// A reference to the current meter entry. + pub fn current(&self) -> &MeterEntry { + self.nested_meters.last().unwrap_or(&self.root_meter) + } +} + +/// An entry representing a journal change. +struct JournalEntry { + key: Vec, + prev_value: Option>, +} + +impl JournalEntry { + /// Create a new change. + fn new(key: Vec, prev_value: Option>) -> Self { + Self { key, prev_value } + } + + /// Revert the change. + fn revert(self, storage: &mut Storage) { + storage.write(&self.key, self.prev_value); + } +} + +/// A journal containing transient storage modifications. +struct Journal(Vec); + +impl Journal { + /// Create a new journal. + fn new() -> Self { + Self(Default::default()) + } + + /// Add a change to the journal. + fn push(&mut self, entry: JournalEntry) { + self.0.push(entry); + } + + /// Length of the journal. + fn len(&self) -> usize { + self.0.len() + } + + /// Roll back all journal changes until the chackpoint + fn rollback(&mut self, storage: &mut Storage, checkpoint: usize) { + self.0.drain(checkpoint..).rev().for_each(|entry| entry.revert(storage)); + } +} + +/// Storage for maintaining the current transaction state. +#[derive(Default)] +struct Storage(BTreeMap, Vec>); + +impl Storage { + /// Read the storage entry. + fn read(&self, key: &Vec) -> Option> { + self.0.get(key).cloned() + } + + /// Write the storage entry. + fn write(&mut self, key: &Vec, value: Option>) -> Option> { + if let Some(value) = value { + // Insert storage entry. + self.0.insert(key.clone(), value) + } else { + // Remove storage entry. + self.0.remove(key) + } + } +} + +/// Transient storage behaves almost identically to regular storage but is discarded after each +/// transaction. It consists of a `BTreeMap` for the current state, a journal of all changes, and a +/// list of checkpoints. On entry to the `start_transaction` function, a marker (checkpoint) is +/// added to the list. New values are written to the current state, and the previous value is +/// recorded in the journal (`write`). When the `commit_transaction` function is called, the marker +/// to the journal index (checkpoint) of when that call was entered is discarded. +/// On `rollback_transaction`, all entries are reverted up to the last checkpoint. +pub struct TransientStorage { + // The storage and journal size is limited by the storage meter. + storage: Storage, + journal: Journal, + // The size of the StorageMeter is limited by the stack depth. + meter: StorageMeter, + // The size of the checkpoints is limited by the stack depth. + checkpoints: Vec, +} + +impl TransientStorage { + /// Create new transient storage with the supplied memory limit. + pub fn new(memory_limit: u32) -> Self { + TransientStorage { + storage: Default::default(), + journal: Journal::new(), + checkpoints: Default::default(), + meter: StorageMeter::new(memory_limit), + } + } + + /// Read the storage value. If the entry does not exist, `None` is returned. + pub fn read(&self, account: &AccountIdOf, key: &Key) -> Option> { + self.storage.read(&Self::storage_key(&account.encode(), &key.hash())) + } + + /// Write a value to storage. + /// + /// If the `value` is `None`, then the entry is removed. If `take` is true, + /// a [`WriteOutcome::Taken`] is returned instead of a [`WriteOutcome::Overwritten`]. + /// If the entry did not exist, [`WriteOutcome::New`] is returned. + pub fn write( + &mut self, + account: &AccountIdOf, + key: &Key, + value: Option>, + take: bool, + ) -> Result { + let key = Self::storage_key(&account.encode(), &key.hash()); + let prev_value = self.storage.read(&key); + // Skip if the same value is being set. + if prev_value != value { + // Calculate the allocation size. + if let Some(value) = &value { + // Charge the key, value and journal entry. + // If a new value is written, a new journal entry is created. The previous value is + // moved to the journal along with its key, and the new value is written to + // storage. + let key_len = key.capacity(); + let mut amount = value + .capacity() + .saturating_add(key_len) + .saturating_add(mem::size_of::()); + if prev_value.is_none() { + // Charge a new storage entry. + // If there was no previous value, a new entry is added to storage (BTreeMap) + // containing a Vec for the key and a Vec for the value. The value was already + // included in the amount. + amount.saturating_accrue(key_len.saturating_add(mem::size_of::>())); + } + self.meter.charge(amount as _)?; + } + self.storage.write(&key, value); + // Update the journal. + self.journal.push(JournalEntry::new(key, prev_value.clone())); + } + + Ok(match (take, prev_value) { + (_, None) => WriteOutcome::New, + (false, Some(prev_value)) => WriteOutcome::Overwritten(prev_value.len() as _), + (true, Some(prev_value)) => WriteOutcome::Taken(prev_value), + }) + } + + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes that are made after this call. + /// For every transaction there must be a matching call to either `rollback_transaction` + /// or `commit_transaction`. + pub fn start_transaction(&mut self) { + self.meter.start(); + self.checkpoints.push(self.journal.len()); + } + + /// Rollback the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are discarded. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + pub fn rollback_transaction(&mut self) { + let checkpoint = self + .checkpoints + .pop() + .expect( + "A call to rollback_transaction must be preceded by a corresponding call to start_transaction; + the code within this crate makes sure that this is always the case; qed" + ); + self.meter.revert(); + self.journal.rollback(&mut self.storage, checkpoint); + } + + /// Commit the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are committed. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + pub fn commit_transaction(&mut self) { + self.checkpoints + .pop() + .expect( + "A call to commit_transaction must be preceded by a corresponding call to start_transaction; + the code within this crate makes sure that this is always the case; qed" + ); + self.meter.commit(); + } + + /// The storage allocation meter used for transaction metering. + #[cfg(any(test, feature = "runtime-benchmarks"))] + pub fn meter(&mut self) -> &mut StorageMeter { + return &mut self.meter + } + + fn storage_key(account: &[u8], key: &[u8]) -> Vec { + let mut storage_key = Vec::with_capacity(account.len() + key.len()); + storage_key.extend_from_slice(&account); + storage_key.extend_from_slice(&key); + storage_key + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{test_utils::*, tests::Test, Error}; + use core::u32::MAX; + + // Calculate the allocation size for the given entry. + fn allocation_size(account: &AccountIdOf, key: &Key, value: Option>) -> u32 { + let mut storage: TransientStorage = TransientStorage::::new(MAX); + storage + .write(account, key, value, false) + .expect("Could not write to transient storage."); + storage.meter().current().amount + } + + #[test] + fn read_write_works() { + let mut storage: TransientStorage = TransientStorage::::new(2048); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![2]), true), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), Some(vec![3]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&ALICE, &Key::Fix([2; 32])), Some(vec![2])); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), Some(vec![3])); + // Overwrite values. + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![4, 5]), false), + Ok(WriteOutcome::Overwritten(1)) + ); + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), Some(vec![6, 7]), true), + Ok(WriteOutcome::Taken(vec![3])) + ); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&ALICE, &Key::Fix([2; 32])), Some(vec![4, 5])); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), Some(vec![6, 7])); + + // Check for an empty value. + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), Some(vec![]), true), + Ok(WriteOutcome::Taken(vec![6, 7])) + ); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), Some(vec![])); + + assert_eq!( + storage.write(&BOB, &Key::Fix([3; 32]), None, true), + Ok(WriteOutcome::Taken(vec![])) + ); + assert_eq!(storage.read(&BOB, &Key::Fix([3; 32])), None); + } + + #[test] + fn read_write_with_var_sized_keys_works() { + let mut storage = TransientStorage::::new(2048); + assert_eq!( + storage.write( + &ALICE, + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![1]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write( + &BOB, + &Key::try_from_var([2; 64].to_vec()).unwrap(), + Some(vec![2, 3]), + false + ), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.read(&ALICE, &Key::try_from_var([1; 64].to_vec()).unwrap()), + Some(vec![1]) + ); + assert_eq!( + storage.read(&BOB, &Key::try_from_var([2; 64].to_vec()).unwrap()), + Some(vec![2, 3]) + ); + // Overwrite values. + assert_eq!( + storage.write( + &ALICE, + &Key::try_from_var([1; 64].to_vec()).unwrap(), + Some(vec![4, 5]), + false + ), + Ok(WriteOutcome::Overwritten(1)) + ); + assert_eq!( + storage.read(&ALICE, &Key::try_from_var([1; 64].to_vec()).unwrap()), + Some(vec![4, 5]) + ); + } + + #[test] + fn rollback_transaction_works() { + let mut storage = TransientStorage::::new(1024); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.rollback_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), None) + } + + #[test] + fn commit_transaction_works() { + let mut storage = TransientStorage::::new(1024); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])) + } + + #[test] + fn overwrite_and_commmit_transaction_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1, 2]), false), + Ok(WriteOutcome::Overwritten(1)) + ); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1, 2])) + } + + #[test] + fn rollback_in_nested_transaction_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.rollback_transaction(); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&BOB, &Key::Fix([1; 32])), None) + } + + #[test] + fn commit_in_nested_transaction_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![2]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&CHARLIE, &Key::Fix([1; 32]), Some(vec![3]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + storage.commit_transaction(); + storage.commit_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), Some(vec![1])); + assert_eq!(storage.read(&BOB, &Key::Fix([1; 32])), Some(vec![2])); + assert_eq!(storage.read(&CHARLIE, &Key::Fix([1; 32])), Some(vec![3])); + } + + #[test] + fn rollback_all_transactions_works() { + let mut storage = TransientStorage::::new(1024); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![2]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&CHARLIE, &Key::Fix([1; 32]), Some(vec![3]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + storage.commit_transaction(); + storage.rollback_transaction(); + assert_eq!(storage.read(&ALICE, &Key::Fix([1; 32])), None); + assert_eq!(storage.read(&BOB, &Key::Fix([1; 32])), None); + assert_eq!(storage.read(&CHARLIE, &Key::Fix([1; 32])), None); + } + + #[test] + fn metering_transactions_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 2); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + let limit = storage.meter().current().limit; + storage.commit_transaction(); + + storage.start_transaction(); + assert_eq!(storage.meter().current().limit, limit - size); + assert_eq!(storage.meter().current().limit - storage.meter().current().amount, size); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + assert_eq!(storage.meter().current().amount, size); + storage.commit_transaction(); + assert_eq!(storage.meter().total_amount(), size * 2); + } + + #[test] + fn metering_nested_transactions_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 3); + + storage.start_transaction(); + let limit = storage.meter().current().limit; + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!(storage.meter().total_amount(), size); + assert!(storage.meter().current().limit < limit - size); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + assert_eq!(storage.meter().current().limit, limit); + assert_eq!(storage.meter().total_amount(), storage.meter().current().amount); + storage.commit_transaction(); + } + + #[test] + fn metering_transaction_fails() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size - 1); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Err(Error::::OutOfTransientStorage.into()) + ); + assert_eq!(storage.meter.current().amount, 0); + storage.commit_transaction(); + assert_eq!(storage.meter.total_amount(), 0); + } + + #[test] + fn metering_nested_transactions_fails() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 2); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Err(Error::::OutOfTransientStorage.into()) + ); + storage.commit_transaction(); + storage.commit_transaction(); + assert_eq!(storage.meter.total_amount(), size); + } + + #[test] + fn metering_nested_transaction_with_rollback_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 2); + + storage.start_transaction(); + let limit = storage.meter.current().limit; + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.rollback_transaction(); + + assert_eq!(storage.meter.total_amount(), 0); + assert_eq!(storage.meter.current().limit, limit); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + let amount = storage.meter().current().amount; + assert_eq!(storage.meter().total_amount(), amount); + storage.commit_transaction(); + } + + #[test] + fn metering_with_rollback_works() { + let size = allocation_size(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096])); + let mut storage = TransientStorage::::new(size * 5); + + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + let amount = storage.meter.total_amount(); + storage.start_transaction(); + assert_eq!( + storage.write(&ALICE, &Key::Fix([2; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.start_transaction(); + assert_eq!( + storage.write(&BOB, &Key::Fix([1; 32]), Some(vec![1u8; 4096]), false), + Ok(WriteOutcome::New) + ); + storage.commit_transaction(); + storage.rollback_transaction(); + assert_eq!(storage.meter.total_amount(), amount); + storage.commit_transaction(); + } +} diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..b8f6eef126b27f6ddf61c948358cf2ba90a4d9f2 --- /dev/null +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -0,0 +1,366 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides a means for executing contracts +//! represented in wasm. + +mod runtime; + +#[cfg(doc)] +pub use crate::wasm::runtime::SyscallDoc; + +#[cfg(test)] +pub use runtime::HIGHEST_API_VERSION; + +#[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] +pub use crate::wasm::runtime::{ReturnData, TrapReason}; + +pub use crate::wasm::runtime::{ApiVersion, Memory, Runtime, RuntimeCosts}; + +use crate::{ + address::AddressMapper, + exec::{ExecResult, Executable, ExportedFunction, Ext}, + gas::{GasMeter, Token}, + storage::meter::Diff, + weights::WeightInfo, + 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}; +use frame_support::{ + dispatch::DispatchResult, + ensure, + traits::{fungible::MutateHold, tokens::Precision::BestEffort}, +}; +use sp_core::{Get, U256}; +use sp_runtime::DispatchError; + +/// Validated Wasm module ready for execution. +/// This data structure is immutable once created and stored. +#[derive(Encode, Decode, scale_info::TypeInfo)] +#[codec(mel_bound())] +#[scale_info(skip_type_params(T))] +pub struct WasmBlob { + code: CodeVec, + // This isn't needed for contract execution and is not stored alongside it. + #[codec(skip)] + code_info: CodeInfo, + // This is for not calculating the hash every time we need it. + #[codec(skip)] + code_hash: sp_core::H256, +} + +/// Contract code related data, such as: +/// +/// - owner of the contract, i.e. account uploaded its code, +/// - storage deposit amount, +/// - reference count, +/// +/// It is stored in a separate storage entry to avoid loading the code when not necessary. +#[derive(Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] +#[codec(mel_bound())] +#[scale_info(skip_type_params(T))] +pub struct CodeInfo { + /// The account that has uploaded the contract code and hence is allowed to remove it. + owner: AccountIdOf, + /// The amount of balance that was deposited by the owner in order to store it on-chain. + #[codec(compact)] + deposit: BalanceOf, + /// The number of instantiated contracts that use this as their code. + #[codec(compact)] + refcount: u64, + /// Length of the code in bytes. + code_len: u32, + /// The API version that this contract operates under. + /// + /// This determines which host functions are available to the contract. This + /// prevents that new host functions become available to already deployed contracts. + api_version: u16, + /// The behaviour version that this contract operates under. + /// + /// Whenever any observeable change (with the exception of weights) are made we need + /// to make sure that already deployed contracts will not be affected. We do this by + /// exposing the old behaviour depending on the set behaviour version of the contract. + /// + /// As of right now this is a reserved field that is always set to 0. + behaviour_version: u16, +} + +impl ExportedFunction { + /// The wasm export name for the function. + fn identifier(&self) -> &str { + match self { + Self::Constructor => "deploy", + Self::Call => "call", + } + } +} + +/// Cost of code loading from storage. +#[cfg_attr(test, derive(Debug, PartialEq, Eq))] +#[derive(Clone, Copy)] +struct CodeLoadToken(u32); + +impl Token for CodeLoadToken { + fn weight(&self) -> Weight { + T::WeightInfo::call_with_code_per_byte(self.0) + .saturating_sub(T::WeightInfo::call_with_code_per_byte(0)) + } +} + +impl WasmBlob +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(), ""))?; + let code_len = code.len() as u32; + let bytes_added = code_len.saturating_add(>::max_encoded_len() as u32); + let deposit = Diff { bytes_added, items_added: 2, ..Default::default() } + .update_contract::(None) + .charge_or_zero(); + let code_info = CodeInfo { + owner, + deposit, + refcount: 0, + code_len, + api_version: API_VERSION, + behaviour_version: Default::default(), + }; + let code_hash = 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: sp_core::H256) -> DispatchResult { + >::try_mutate_exists(&code_hash, |existing| { + if let Some(code_info) = existing { + ensure!(code_info.refcount == 0, >::CodeInUse); + ensure!(&code_info.owner == origin, BadOrigin); + let _ = T::Currency::release( + &HoldReason::CodeUploadDepositReserve.into(), + &code_info.owner, + code_info.deposit, + BestEffort, + ); + let deposit_released = code_info.deposit; + let remover = T::AddressMapper::to_address(&code_info.owner); + + *existing = None; + >::remove(&code_hash); + >::deposit_event(Event::CodeRemoved { + code_hash, + deposit_released, + remover, + }); + Ok(()) + } else { + Err(>::CodeNotFound.into()) + } + }) + } + + /// Puts the module blob into storage, and returns the deposit collected for the storage. + pub fn store_code(&mut self) -> Result, Error> { + let code_hash = *self.code_hash(); + >::mutate(code_hash, |stored_code_info| { + match stored_code_info { + // Contract code is already stored in storage. Nothing to be done here. + Some(_) => Ok(Default::default()), + // Upload a new contract code. + // We need to store the code and its code_info, and collect the deposit. + // This `None` case happens only with freshly uploaded modules. This means that + // the `owner` is always the origin of the current transaction. + None => { + let deposit = self.code_info.deposit; + T::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &self.code_info.owner, + deposit, + ) + .map_err(|_| >::StorageDepositNotEnoughFunds)?; + + self.code_info.refcount = 0; + >::insert(code_hash, &self.code); + *stored_code_info = Some(self.code_info.clone()); + let uploader = T::AddressMapper::to_address(&self.code_info.owner); + >::deposit_event(Event::CodeStored { + code_hash, + deposit_held: deposit, + uploader, + }); + Ok(deposit) + }, + } + }) + } +} + +impl CodeInfo { + #[cfg(test)] + pub fn new(owner: T::AccountId) -> Self { + CodeInfo { + owner, + deposit: Default::default(), + refcount: 0, + code_len: 0, + api_version: API_VERSION, + behaviour_version: Default::default(), + } + } + + /// Returns reference count of the module. + pub fn refcount(&self) -> u64 { + self.refcount + } + + /// Return mutable reference to the refcount of the module. + pub fn refcount_mut(&mut self) -> &mut u64 { + &mut self.refcount + } + + /// Returns the deposit of the module. + pub fn deposit(&self) -> BalanceOf { + self.deposit + } +} + +pub struct PreparedCall<'a, E: Ext> { + module: polkavm::Module, + instance: polkavm::RawInstance, + runtime: Runtime<'a, E, polkavm::RawInstance>, + api_version: ApiVersion, +} + +impl<'a, E: Ext> PreparedCall<'a, E> +where + BalanceOf: Into, + BalanceOf: TryFrom, +{ + pub fn call(mut self) -> ExecResult { + let exec_result = loop { + let interrupt = self.instance.run(); + if let Some(exec_result) = self.runtime.handle_interrupt( + interrupt, + &self.module, + &mut self.instance, + self.api_version, + ) { + break exec_result + } + }; + let _ = self.runtime.ext().gas_meter_mut().sync_from_executor(self.instance.gas())?; + exec_result + } +} + +impl WasmBlob { + pub fn prepare_call>( + self, + mut runtime: Runtime, + entry_point: ExportedFunction, + api_version: ApiVersion, + ) -> Result, ExecError> { + let code = self.code.as_slice(); + + let mut config = polkavm::Config::default(); + config.set_backend(Some(polkavm::BackendKind::Interpreter)); + let engine = + polkavm::Engine::new(&config).expect("interpreter is available on all plattforms; qed"); + + let mut module_config = polkavm::ModuleConfig::new(); + module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync)); + let module = polkavm::Module::new(&engine, &module_config, code.into()).map_err(|err| { + log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}"); + Error::::CodeRejected + })?; + + let entry_program_counter = module + .exports() + .find(|export| export.symbol().as_bytes() == entry_point.identifier().as_bytes()) + .ok_or_else(|| >::CodeRejected)? + .program_counter(); + + let gas_limit_polkavm: polkavm::Gas = runtime.ext().gas_meter_mut().engine_fuel_left()?; + + let mut instance = module.instantiate().map_err(|err| { + log::debug!(target: LOG_TARGET, "failed to instantiate polkavm module: {err:?}"); + Error::::CodeRejected + })?; + + // Increment before execution so that the constructor sees the correct refcount + if let ExportedFunction::Constructor = entry_point { + E::increment_refcount(self.code_hash)?; + } + + instance.set_gas(gas_limit_polkavm); + instance.prepare_call_untyped(entry_program_counter, &[]); + + Ok(PreparedCall { module, instance, runtime, api_version }) + } +} + +impl Executable for WasmBlob +where + BalanceOf: Into + TryFrom, +{ + fn from_storage( + code_hash: sp_core::H256, + gas_meter: &mut GasMeter, + ) -> Result { + let code_info = >::get(code_hash).ok_or(Error::::CodeNotFound)?; + gas_meter.charge(CodeLoadToken(code_info.code_len))?; + let code = >::get(code_hash).ok_or(Error::::CodeNotFound)?; + Ok(Self { code, code_info, code_hash }) + } + + fn execute>( + self, + ext: &mut E, + function: ExportedFunction, + input_data: Vec, + ) -> ExecResult { + let api_version = if ::UnsafeUnstableInterface::get() { + ApiVersion::UnsafeNewest + } else { + ApiVersion::Versioned(self.code_info.api_version) + }; + let prepared_call = + self.prepare_call(Runtime::new(ext, input_data), function, api_version)?; + prepared_call.call() + } + + fn code(&self) -> &[u8] { + self.code.as_ref() + } + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..80daac8f9db37a15c5f5e3bd5911853ca47703ed --- /dev/null +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -0,0 +1,1980 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Environment definition of the wasm smart-contract runtime. + +use crate::{ + address::AddressMapper, + exec::{ExecError, ExecResult, Ext, Key}, + gas::{ChargedAmount, Token}, + limits, + primitives::ExecReturnValue, + weights::WeightInfo, + Config, Error, LOG_TARGET, SENTINEL, +}; +use alloc::{boxed::Box, vec, vec::Vec}; +use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; +use core::{fmt, marker::PhantomData}; +use frame_support::{ + dispatch::DispatchInfo, ensure, pallet_prelude::DispatchResultWithPostInfo, parameter_types, + traits::Get, weights::Weight, +}; +use pallet_revive_proc_macro::define_env; +use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags}; +use sp_core::{H160, H256, U256}; +use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; +use sp_runtime::{DispatchError, RuntimeDebug}; + +type CallOf = ::RuntimeCall; + +/// The maximum nesting depth a contract can use when encoding types. +const MAX_DECODE_NESTING: u32 = 256; + +/// Encode a `U256` into a 32 byte buffer. +fn as_bytes(u: U256) -> [u8; 32] { + let mut bytes = [0u8; 32]; + u.to_little_endian(&mut bytes); + bytes +} + +#[derive(Clone, Copy)] +pub enum ApiVersion { + /// Expose all APIs even unversioned ones. Only used for testing and benchmarking. + UnsafeNewest, + /// Only expose API's up to and including the specified version. + Versioned(u16), +} + +/// Abstraction over the memory access within syscalls. +/// +/// The reason for this abstraction is that we run syscalls on the host machine when +/// benchmarking them. In that case we have direct access to the contract's memory. However, when +/// running within PolkaVM we need to resort to copying as we can't map the contracts memory into +/// the host (as of now). +pub trait Memory { + /// Read designated chunk from the sandbox memory into the supplied buffer. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - requested buffer is not within the bounds of the sandbox memory. + fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError>; + + /// Write the given buffer to the designated location in the sandbox memory. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - designated area is not within the bounds of the sandbox memory. + fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError>; + + /// Read designated chunk from the sandbox memory. + /// + /// Returns `Err` if one of the following conditions occurs: + /// + /// - requested buffer is not within the bounds of the sandbox memory. + fn read(&self, ptr: u32, len: u32) -> Result, DispatchError> { + let mut buf = vec![0u8; len as usize]; + self.read_into_buf(ptr, buf.as_mut_slice())?; + Ok(buf) + } + + /// 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: + /// + /// - requested buffer is not within the bounds of the sandbox memory. + /// - the buffer contents cannot be decoded as the required type. + /// + /// # Note + /// + /// There must be an extra benchmark for determining the influence of `len` with + /// regard to the overall weight. + fn read_as_unbounded(&self, ptr: u32, len: u32) -> Result { + let buf = self.read(ptr, len)?; + let decoded = D::decode_all_with_depth_limit(MAX_DECODE_NESTING, &mut buf.as_ref()) + .map_err(|_| DispatchError::from(Error::::DecodingFailed))?; + Ok(decoded) + } + + /// Reads and decodes a type with a size fixed at compile time from contract memory. + /// + /// # Only use on fixed size types + /// + /// Don't use this for types where the encoded size is not fixed but merely bounded. Otherwise + /// this implementation will out of bound access the buffer declared by the guest. Some examples + /// of those bounded but not fixed types: Enums with data, `BoundedVec` or any compact encoded + /// integer. + /// + /// # Note + /// + /// The weight of reading a fixed value is included in the overall weight of any + /// contract callable function. + fn read_as(&self, ptr: u32) -> Result { + let buf = self.read(ptr, D::max_encoded_len() as u32)?; + let decoded = D::decode_with_depth_limit(MAX_DECODE_NESTING, &mut buf.as_ref()) + .map_err(|_| DispatchError::from(Error::::DecodingFailed))?; + Ok(decoded) + } +} + +/// Allows syscalls access to the PolkaVM instance they are executing in. +/// +/// In case a contract is executing within PolkaVM its `memory` argument will also implement +/// this trait. The benchmarking implementation of syscalls will only require `Memory` +/// to be implemented. +pub trait PolkaVmInstance: Memory { + fn gas(&self) -> polkavm::Gas; + fn set_gas(&mut self, gas: polkavm::Gas); + fn read_input_regs(&self) -> (u32, u32, u32, u32, u32, u32); + fn write_output(&mut self, output: u32); +} + +// Memory implementation used in benchmarking where guest memory is mapped into the host. +// +// Please note that we could optimize the `read_as_*` functions by decoding directly from +// memory without a copy. However, we don't do that because as it would change the behaviour +// of those functions: A `read_as` with a `len` larger than the actual type can succeed +// in the streaming implementation while it could fail with a segfault in the copy implementation. +#[cfg(feature = "runtime-benchmarks")] +impl Memory for [u8] { + fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> { + let ptr = ptr as usize; + let bound_checked = + self.get(ptr..ptr + buf.len()).ok_or_else(|| Error::::OutOfBounds)?; + buf.copy_from_slice(bound_checked); + Ok(()) + } + + fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> { + let ptr = ptr as usize; + let bound_checked = + self.get_mut(ptr..ptr + buf.len()).ok_or_else(|| Error::::OutOfBounds)?; + bound_checked.copy_from_slice(buf); + Ok(()) + } +} + +impl Memory for polkavm::RawInstance { + fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> { + self.read_memory_into(ptr, buf) + .map(|_| ()) + .map_err(|_| Error::::OutOfBounds.into()) + } + + fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> { + self.write_memory(ptr, buf).map_err(|_| Error::::OutOfBounds.into()) + } +} + +impl PolkaVmInstance for polkavm::RawInstance { + fn gas(&self) -> polkavm::Gas { + self.gas() + } + + fn set_gas(&mut self, gas: polkavm::Gas) { + self.set_gas(gas) + } + + fn read_input_regs(&self) -> (u32, u32, u32, u32, u32, u32) { + ( + self.reg(polkavm::Reg::A0), + self.reg(polkavm::Reg::A1), + self.reg(polkavm::Reg::A2), + self.reg(polkavm::Reg::A3), + self.reg(polkavm::Reg::A4), + self.reg(polkavm::Reg::A5), + ) + } + + fn write_output(&mut self, output: u32) { + self.set_reg(polkavm::Reg::A0, output); + } +} + +parameter_types! { + /// Getter types used by [`crate::SyscallDoc:call_runtime`] + const CallRuntimeFailed: ReturnErrorCode = ReturnErrorCode::CallRuntimeFailed; + /// Getter types used by [`crate::SyscallDoc::xcm_execute`] + const XcmExecutionFailed: ReturnErrorCode = ReturnErrorCode::XcmExecutionFailed; +} + +impl From for ReturnErrorCode { + fn from(from: ExecReturnValue) -> Self { + if from.flags.contains(ReturnFlags::REVERT) { + Self::CalleeReverted + } else { + Self::Success + } + } +} + +/// The data passed through when a contract uses `seal_return`. +#[derive(RuntimeDebug)] +pub struct ReturnData { + /// The flags as passed through by the contract. They are still unchecked and + /// will later be parsed into a `ReturnFlags` bitflags struct. + flags: u32, + /// The output buffer passed by the contract as return data. + data: Vec, +} + +/// Enumerates all possible reasons why a trap was generated. +/// +/// This is either used to supply the caller with more information about why an error +/// occurred (the SupervisorError variant). +/// The other case is where the trap does not constitute an error but rather was invoked +/// as a quick way to terminate the application (all other variants). +#[derive(RuntimeDebug)] +pub enum TrapReason { + /// The supervisor trapped the contract because of an error condition occurred during + /// execution in privileged code. + SupervisorError(DispatchError), + /// Signals that trap was generated in response to call `seal_return` host function. + Return(ReturnData), + /// Signals that a trap was generated in response to a successful call to the + /// `seal_terminate` host function. + Termination, +} + +impl> From for TrapReason { + fn from(from: T) -> Self { + Self::SupervisorError(from.into()) + } +} + +impl fmt::Display for TrapReason { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + Ok(()) + } +} + +#[cfg_attr(test, derive(Debug, PartialEq, Eq))] +#[derive(Copy, Clone)] +pub enum RuntimeCosts { + /// Base Weight of calling a host function. + HostFn, + /// Weight charged for copying data from the sandbox. + CopyFromContract(u32), + /// Weight charged for copying data to the sandbox. + CopyToContract(u32), + /// Weight of calling `seal_caller`. + Caller, + /// Weight of calling `seal_is_contract`. + IsContract, + /// Weight of calling `seal_code_hash`. + CodeHash, + /// Weight of calling `seal_own_code_hash`. + OwnCodeHash, + /// Weight of calling `seal_caller_is_origin`. + CallerIsOrigin, + /// Weight of calling `caller_is_root`. + CallerIsRoot, + /// Weight of calling `seal_address`. + Address, + /// Weight of calling `seal_gas_left`. + GasLeft, + /// Weight of calling `seal_balance`. + Balance, + /// Weight of calling `seal_balance_of`. + BalanceOf, + /// Weight of calling `seal_value_transferred`. + ValueTransferred, + /// Weight of calling `seal_minimum_balance`. + MinimumBalance, + /// Weight of calling `seal_block_number`. + BlockNumber, + /// Weight of calling `seal_now`. + Now, + /// Weight of calling `seal_weight_to_fee`. + WeightToFee, + /// Weight of calling `seal_terminate`, passing the number of locked dependencies. + Terminate(u32), + /// Weight of calling `seal_deposit_event` with the given number of topics and event size. + DepositEvent { num_topic: u32, len: u32 }, + /// Weight of calling `seal_debug_message` per byte of passed message. + DebugMessage(u32), + /// Weight of calling `seal_set_storage` for the given storage item sizes. + SetStorage { old_bytes: u32, new_bytes: u32 }, + /// Weight of calling `seal_clear_storage` per cleared byte. + ClearStorage(u32), + /// Weight of calling `seal_contains_storage` per byte of the checked item. + ContainsStorage(u32), + /// Weight of calling `seal_get_storage` with the specified size in storage. + GetStorage(u32), + /// Weight of calling `seal_take_storage` for the given size. + TakeStorage(u32), + /// Weight of calling `seal_set_transient_storage` for the given storage item sizes. + SetTransientStorage { old_bytes: u32, new_bytes: u32 }, + /// Weight of calling `seal_clear_transient_storage` per cleared byte. + ClearTransientStorage(u32), + /// Weight of calling `seal_contains_transient_storage` per byte of the checked item. + ContainsTransientStorage(u32), + /// Weight of calling `seal_get_transient_storage` with the specified size in storage. + GetTransientStorage(u32), + /// Weight of calling `seal_take_transient_storage` for the given size. + TakeTransientStorage(u32), + /// Weight of calling `seal_transfer`. + Transfer, + /// Base weight of calling `seal_call`. + CallBase, + /// Weight of calling `seal_delegate_call` for the given input size. + DelegateCallBase, + /// Weight of the transfer performed during a call. + CallTransferSurcharge, + /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. + CallInputCloned(u32), + /// Weight of calling `seal_instantiate` for the given input 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. + HashKeccak256(u32), + /// Weight of calling `seal_hash_blake2_256` for the given input size. + HashBlake256(u32), + /// Weight of calling `seal_hash_blake2_128` for the given input size. + HashBlake128(u32), + /// Weight of calling `seal_ecdsa_recover`. + EcdsaRecovery, + /// Weight of calling `seal_sr25519_verify` for the given input size. + Sr25519Verify(u32), + /// Weight charged by a chain extension through `seal_call_chain_extension`. + ChainExtension(Weight), + /// Weight charged for calling into the runtime. + CallRuntime(Weight), + /// Weight charged for calling xcm_execute. + CallXcmExecute(Weight), + /// Weight of calling `seal_set_code_hash` + SetCodeHash, + /// Weight of calling `ecdsa_to_eth_address` + EcdsaToEthAddress, + /// Weight of calling `lock_delegate_dependency` + LockDelegateDependency, + /// Weight of calling `unlock_delegate_dependency` + UnlockDelegateDependency, +} + +/// For functions that modify storage, benchmarks are performed with one item in the +/// storage. To account for the worst-case scenario, the weight of the overhead of +/// writing to or reading from full storage is included. For transient storage writes, +/// the rollback weight is added to reflect the worst-case scenario for this operation. +macro_rules! cost_storage { + (write_transient, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::rollback_transient_storage()) + .saturating_add(T::WeightInfo::set_transient_storage_full() + .saturating_sub(T::WeightInfo::set_transient_storage_empty())) + }; + + (read_transient, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::get_transient_storage_full() + .saturating_sub(T::WeightInfo::get_transient_storage_empty())) + }; + + (write, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::set_storage_full() + .saturating_sub(T::WeightInfo::set_storage_empty())) + }; + + (read, $name:ident $(, $arg:expr )*) => { + T::WeightInfo::$name($( $arg ),*) + .saturating_add(T::WeightInfo::get_storage_full() + .saturating_sub(T::WeightInfo::get_storage_empty())) + }; +} + +macro_rules! cost_args { + // cost_args!(name, a, b, c) -> T::WeightInfo::name(a, b, c).saturating_sub(T::WeightInfo::name(0, 0, 0)) + ($name:ident, $( $arg: expr ),+) => { + (T::WeightInfo::$name($( $arg ),+).saturating_sub(cost_args!(@call_zero $name, $( $arg ),+))) + }; + // Transform T::WeightInfo::name(a, b, c) into T::WeightInfo::name(0, 0, 0) + (@call_zero $name:ident, $( $arg:expr ),*) => { + T::WeightInfo::$name($( cost_args!(@replace_token $arg) ),*) + }; + // Replace the token with 0. + (@replace_token $_in:tt) => { 0 }; +} + +impl Token for RuntimeCosts { + fn influence_lowest_gas_limit(&self) -> bool { + match self { + &Self::CallXcmExecute(_) => false, + _ => true, + } + } + + fn weight(&self) -> Weight { + use self::RuntimeCosts::*; + match *self { + HostFn => cost_args!(noop_host_fn, 1), + CopyToContract(len) => T::WeightInfo::seal_input(len), + CopyFromContract(len) => T::WeightInfo::seal_return(len), + Caller => T::WeightInfo::seal_caller(), + IsContract => T::WeightInfo::seal_is_contract(), + CodeHash => T::WeightInfo::seal_code_hash(), + OwnCodeHash => T::WeightInfo::seal_own_code_hash(), + CallerIsOrigin => T::WeightInfo::seal_caller_is_origin(), + CallerIsRoot => T::WeightInfo::seal_caller_is_root(), + Address => T::WeightInfo::seal_address(), + GasLeft => T::WeightInfo::seal_gas_left(), + Balance => T::WeightInfo::seal_balance(), + BalanceOf => T::WeightInfo::seal_balance_of(), + ValueTransferred => T::WeightInfo::seal_value_transferred(), + MinimumBalance => T::WeightInfo::seal_minimum_balance(), + BlockNumber => T::WeightInfo::seal_block_number(), + Now => T::WeightInfo::seal_now(), + WeightToFee => T::WeightInfo::seal_weight_to_fee(), + Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies), + DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), + DebugMessage(len) => T::WeightInfo::seal_debug_message(len), + SetStorage { new_bytes, old_bytes } => + cost_storage!(write, seal_set_storage, new_bytes, old_bytes), + ClearStorage(len) => cost_storage!(write, seal_clear_storage, len), + ContainsStorage(len) => cost_storage!(read, seal_contains_storage, len), + GetStorage(len) => cost_storage!(read, seal_get_storage, len), + TakeStorage(len) => cost_storage!(write, seal_take_storage, len), + SetTransientStorage { new_bytes, old_bytes } => + cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes), + ClearTransientStorage(len) => + cost_storage!(write_transient, seal_clear_transient_storage, len), + ContainsTransientStorage(len) => + cost_storage!(read_transient, seal_contains_transient_storage, len), + GetTransientStorage(len) => + cost_storage!(read_transient, seal_get_transient_storage, len), + TakeTransientStorage(len) => + cost_storage!(write_transient, seal_take_transient_storage, len), + Transfer => T::WeightInfo::seal_transfer(), + CallBase => T::WeightInfo::seal_call(0, 0), + DelegateCallBase => T::WeightInfo::seal_delegate_call(), + CallTransferSurcharge => cost_args!(seal_call, 1, 0), + CallInputCloned(len) => cost_args!(seal_call, 0, len), + Instantiate { input_data_len } => 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), + HashBlake128(len) => T::WeightInfo::seal_hash_blake2_128(len), + EcdsaRecovery => T::WeightInfo::seal_ecdsa_recover(), + Sr25519Verify(len) => T::WeightInfo::seal_sr25519_verify(len), + ChainExtension(weight) | CallRuntime(weight) | CallXcmExecute(weight) => weight, + SetCodeHash => T::WeightInfo::seal_set_code_hash(), + EcdsaToEthAddress => T::WeightInfo::seal_ecdsa_to_eth_address(), + LockDelegateDependency => T::WeightInfo::lock_delegate_dependency(), + UnlockDelegateDependency => T::WeightInfo::unlock_delegate_dependency(), + } + } +} + +/// Same as [`Runtime::charge_gas`]. +/// +/// We need this access as a macro because sometimes hiding the lifetimes behind +/// a function won't work out. +macro_rules! charge_gas { + ($runtime:expr, $costs:expr) => {{ + $runtime.ext.gas_meter_mut().charge($costs) + }}; +} + +/// The kind of call that should be performed. +enum CallType { + /// Execute another instantiated contract + Call { callee_ptr: u32, value_ptr: u32, deposit_ptr: u32, weight: Weight }, + /// Execute deployed code in the context (storage, account ID, value) of the caller contract + DelegateCall { code_hash_ptr: u32 }, +} + +impl CallType { + fn cost(&self) -> RuntimeCosts { + match self { + CallType::Call { .. } => RuntimeCosts::CallBase, + CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase, + } + } +} + +/// This is only appropriate when writing out data of constant size that does not depend on user +/// input. In this case the costs for this copy was already charged as part of the token at +/// the beginning of the API entry point. +fn already_charged(_: u32) -> Option { + None +} + +/// Can only be used for one call. +pub struct Runtime<'a, E: Ext, M: ?Sized> { + ext: &'a mut E, + input_data: Option>, + chain_extension: Option::ChainExtension>>, + _phantom_data: PhantomData, +} + +impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { + pub fn handle_interrupt( + &mut self, + interrupt: Result, + module: &polkavm::Module, + instance: &mut M, + api_version: ApiVersion, + ) -> Option { + use polkavm::InterruptKind::*; + + match interrupt { + Err(error) => { + // in contrast to the other returns this "should" not happen: log level error + log::error!(target: LOG_TARGET, "polkavm execution error: {error}"); + Some(Err(Error::::ExecutionFailed.into())) + }, + Ok(Finished) => + Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })), + Ok(Trap) => Some(Err(Error::::ContractTrapped.into())), + Ok(Segfault(_)) => Some(Err(Error::::ExecutionFailed.into())), + Ok(NotEnoughGas) => Some(Err(Error::::OutOfGas.into())), + Ok(Step) => None, + Ok(Ecalli(idx)) => { + let Some(syscall_symbol) = module.imports().get(idx) else { + return Some(Err(>::InvalidSyscall.into())) + }; + match self.handle_ecall(instance, syscall_symbol.as_bytes(), api_version) { + Ok(None) => None, + Ok(Some(return_value)) => { + instance.write_output(return_value); + None + }, + Err(TrapReason::Return(ReturnData { flags, data })) => + match ReturnFlags::from_bits(flags) { + None => Some(Err(Error::::InvalidCallFlags.into())), + Some(flags) => Some(Ok(ExecReturnValue { flags, data })), + }, + Err(TrapReason::Termination) => Some(Ok(Default::default())), + Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())), + } + }, + } + } +} + +impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { + pub fn new(ext: &'a mut E, input_data: Vec) -> Self { + Self { + ext, + input_data: Some(input_data), + chain_extension: Some(Box::new(Default::default())), + _phantom_data: Default::default(), + } + } + + /// Get a mutable reference to the inner `Ext`. + /// + /// This is mainly for the chain extension to have access to the environment the + /// contract is executing in. + pub fn ext(&mut self) -> &mut E { + self.ext + } + + /// Charge the gas meter with the specified token. + /// + /// Returns `Err(HostError)` if there is not enough gas. + pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result { + charge_gas!(self, costs) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) { + self.ext.gas_meter_mut().adjust_gas(charged, actual_costs); + } + + /// Charge, Run and adjust gas, for executing the given dispatchable. + fn call_dispatchable>( + &mut self, + dispatch_info: DispatchInfo, + runtime_cost: impl Fn(Weight) -> RuntimeCosts, + run: impl FnOnce(&mut Self) -> DispatchResultWithPostInfo, + ) -> Result { + use frame_support::dispatch::extract_actual_weight; + let charged = self.charge_gas(runtime_cost(dispatch_info.weight))?; + let result = run(self); + let actual_weight = extract_actual_weight(&result, &dispatch_info); + self.adjust_gas(charged, runtime_cost(actual_weight)); + match result { + Ok(_) => Ok(ReturnErrorCode::Success), + Err(e) => { + if self.ext.debug_buffer_enabled() { + self.ext.append_debug_buffer("call failed with: "); + self.ext.append_debug_buffer(e.into()); + }; + Ok(ErrorReturnCode::get()) + }, + } + } + + /// Write the given buffer and its length to the designated locations in sandbox memory and + /// charge gas according to the token returned by `create_token`. + // + /// `out_ptr` is the location in sandbox memory where `buf` should be written to. + /// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the + /// length of the buffer located at `out_ptr`. If that buffer is large enough the actual + /// `buf.len()` is written to this location. + /// + /// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the + /// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying + /// output optional. For example to skip copying back the output buffer of an `seal_call` + /// when the caller is not interested in the result. + /// + /// `create_token` can optionally instruct this function to charge the gas meter with the token + /// it returns. `create_token` receives the variable amount of bytes that are about to be copied + /// by this function. + /// + /// In addition to the error conditions of `Memory::write` this functions returns + /// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`. + pub fn write_sandbox_output( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + buf: &[u8], + allow_skip: bool, + create_token: impl FnOnce(u32) -> Option, + ) -> Result<(), DispatchError> { + if allow_skip && out_ptr == SENTINEL { + return Ok(()) + } + + let buf_len = buf.len() as u32; + let len = memory.read_u32(out_len_ptr)?; + + if len < buf_len { + return Err(Error::::OutputBufferTooSmall.into()) + } + + if let Some(costs) = create_token(buf_len) { + self.charge_gas(costs)?; + } + + memory.write(out_ptr, buf)?; + memory.write(out_len_ptr, &buf_len.encode()) + } + + /// Same as `write_sandbox_output` but for static size output. + pub fn write_fixed_sandbox_output( + &mut self, + memory: &mut M, + out_ptr: u32, + buf: &[u8], + allow_skip: bool, + create_token: impl FnOnce(u32) -> Option, + ) -> Result<(), DispatchError> { + if allow_skip && out_ptr == SENTINEL { + return Ok(()) + } + + let buf_len = buf.len() as u32; + if let Some(costs) = create_token(buf_len) { + self.charge_gas(costs)?; + } + + memory.write(out_ptr, buf) + } + + /// Computes the given hash function on the supplied input. + /// + /// Reads from the sandboxed input buffer into an intermediate buffer. + /// Returns the result directly to the output buffer of the sandboxed memory. + /// + /// It is the callers responsibility to provide an output buffer that + /// is large enough to hold the expected amount of bytes returned by the + /// chosen hash function. + /// + /// # Note + /// + /// The `input` and `output` buffers may overlap. + fn compute_hash_on_intermediate_buffer( + &self, + memory: &mut M, + hash_fn: F, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), DispatchError> + where + F: FnOnce(&[u8]) -> R, + R: AsRef<[u8]>, + { + // Copy input into supervisor memory. + let input = memory.read(input_ptr, input_len)?; + // Compute the hash on the input buffer using the given hash function. + let hash = hash_fn(&input); + // Write the resulting hash back into the sandboxed output buffer. + memory.write(output_ptr, hash.as_ref())?; + Ok(()) + } + + /// Fallible conversion of `DispatchError` to `ReturnErrorCode`. + fn err_into_return_code(from: DispatchError) -> Result { + use ReturnErrorCode::*; + + let transfer_failed = Error::::TransferFailed.into(); + let no_code = Error::::CodeNotFound.into(); + let not_found = Error::::ContractNotFound.into(); + + match from { + x if x == transfer_failed => Ok(TransferFailed), + x if x == no_code => Ok(CodeNotFound), + x if x == not_found => Ok(NotCallable), + err => Err(err), + } + } + + /// Fallible conversion of a `ExecResult` to `ReturnErrorCode`. + fn exec_into_return_code(from: ExecResult) -> Result { + use crate::exec::ErrorOrigin::Callee; + + let ExecError { error, origin } = match from { + Ok(retval) => return Ok(retval.into()), + Err(err) => err, + }; + + match (error, origin) { + (_, Callee) => Ok(ReturnErrorCode::CalleeTrapped), + (err, _) => Self::err_into_return_code(err), + } + } + fn decode_key(&self, memory: &M, key_ptr: u32, key_len: u32) -> Result { + let res = match key_len { + SENTINEL => { + let mut buffer = [0u8; 32]; + memory.read_into_buf(key_ptr, buffer.as_mut())?; + Ok(Key::from_fixed(buffer)) + }, + len => { + ensure!(len <= limits::STORAGE_KEY_BYTES, Error::::DecodingFailed); + let key = memory.read(key_ptr, len)?; + Key::try_from_var(key) + }, + }; + + res.map_err(|_| Error::::DecodingFailed.into()) + } + + fn is_transient(flags: u32) -> Result { + StorageFlags::from_bits(flags) + .ok_or_else(|| >::InvalidStorageFlags.into()) + .map(|flags| flags.contains(StorageFlags::TRANSIENT)) + } + + fn set_storage( + &mut self, + memory: &M, + flags: u32, + key_ptr: u32, + key_len: u32, + value_ptr: u32, + value_len: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |new_bytes: u32, old_bytes: u32| { + if transient { + RuntimeCosts::SetTransientStorage { new_bytes, old_bytes } + } else { + RuntimeCosts::SetStorage { new_bytes, old_bytes } + } + }; + let max_size = self.ext.max_value_size(); + let charged = self.charge_gas(costs(value_len, self.ext.max_value_size()))?; + if value_len > max_size { + return Err(Error::::ValueTooLarge.into()) + } + let key = self.decode_key(memory, key_ptr, key_len)?; + let value = Some(memory.read(value_ptr, value_len)?); + let write_outcome = if transient { + self.ext.set_transient_storage(&key, value, false)? + } else { + self.ext.set_storage(&key, value, false)? + }; + self.adjust_gas(charged, costs(value_len, write_outcome.old_len())); + Ok(write_outcome.old_len_with_sentinel()) + } + + fn clear_storage( + &mut self, + memory: &M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::ClearTransientStorage(len) + } else { + RuntimeCosts::ClearStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.set_transient_storage(&key, None, false)? + } else { + self.ext.set_storage(&key, None, false)? + }; + self.adjust_gas(charged, costs(outcome.old_len())); + Ok(outcome.old_len_with_sentinel()) + } + + fn get_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::GetTransientStorage(len) + } else { + RuntimeCosts::GetStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.get_transient_storage(&key) + } else { + self.ext.get_storage(&key) + }; + if let Some(value) = outcome { + self.adjust_gas(charged, costs(value.len() as u32)); + self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &value, + false, + already_charged, + )?; + Ok(ReturnErrorCode::Success) + } else { + self.adjust_gas(charged, costs(0)); + Ok(ReturnErrorCode::KeyNotFound) + } + } + + fn contains_storage( + &mut self, + memory: &M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::ContainsTransientStorage(len) + } else { + RuntimeCosts::ContainsStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.get_transient_storage_size(&key) + } else { + self.ext.get_storage_size(&key) + }; + self.adjust_gas(charged, costs(outcome.unwrap_or(0))); + Ok(outcome.unwrap_or(SENTINEL)) + } + + fn take_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + let transient = Self::is_transient(flags)?; + let costs = |len| { + if transient { + RuntimeCosts::TakeTransientStorage(len) + } else { + RuntimeCosts::TakeStorage(len) + } + }; + let charged = self.charge_gas(costs(self.ext.max_value_size()))?; + let key = self.decode_key(memory, key_ptr, key_len)?; + let outcome = if transient { + self.ext.set_transient_storage(&key, None, true)? + } else { + self.ext.set_storage(&key, None, true)? + }; + + if let crate::storage::WriteOutcome::Taken(value) = outcome { + self.adjust_gas(charged, costs(value.len() as u32)); + self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &value, + false, + already_charged, + )?; + Ok(ReturnErrorCode::Success) + } else { + self.adjust_gas(charged, costs(0)); + Ok(ReturnErrorCode::KeyNotFound) + } + } + + fn call( + &mut self, + memory: &mut M, + flags: CallFlags, + call_type: CallType, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + self.charge_gas(call_type.cost())?; + + let input_data = if flags.contains(CallFlags::CLONE_INPUT) { + let input = self.input_data.as_ref().ok_or(Error::::InputForwarded)?; + charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?; + input.clone() + } else if flags.contains(CallFlags::FORWARD_INPUT) { + self.input_data.take().ok_or(Error::::InputForwarded)? + } else { + self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?; + memory.read(input_data_ptr, input_data_len)? + }; + + let call_outcome = match call_type { + CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { + let 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_u256(deposit_ptr)? + }; + let read_only = flags.contains(CallFlags::READ_ONLY); + 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. + if read_only || self.ext.is_read_only() { + return Err(Error::::StateChangeDenied.into()); + } + self.charge_gas(RuntimeCosts::CallTransferSurcharge)?; + } + self.ext.call( + weight, + deposit_limit, + &callee, + value, + input_data, + flags.contains(CallFlags::ALLOW_REENTRY), + read_only, + ) + }, + CallType::DelegateCall { code_hash_ptr } => { + if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) { + return Err(Error::::InvalidCallFlags.into()) + } + + let code_hash = memory.read_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 { + return Err(TrapReason::Return(ReturnData { + flags: return_value.flags.bits(), + data: return_value.data, + })) + } + } + + if let Ok(output) = &call_outcome { + self.write_sandbox_output( + memory, + output_ptr, + output_len_ptr, + &output.data, + true, + |len| Some(RuntimeCosts::CopyToContract(len)), + )?; + } + Ok(Self::exec_into_return_code(call_outcome)?) + } + + fn instantiate( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + weight: Weight, + deposit_ptr: u32, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + ) -> Result { + 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 { + let salt: [u8; 32] = memory.read_array(salt_ptr)?; + Some(salt) + }; + let instantiate_outcome = self.ext.instantiate( + weight, + deposit_limit, + code_hash, + value, + input_data, + salt.as_ref(), + ); + if let Ok((address, output)) = &instantiate_outcome { + if !output.flags.contains(ReturnFlags::REVERT) { + self.write_fixed_sandbox_output( + memory, + address_ptr, + &address.as_bytes(), + true, + already_charged, + )?; + } + self.write_sandbox_output( + memory, + output_ptr, + output_len_ptr, + &output.data, + true, + |len| Some(RuntimeCosts::CopyToContract(len)), + )?; + } + Ok(Self::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?) + } + + fn terminate(&mut self, memory: &M, beneficiary_ptr: u32) -> Result<(), TrapReason> { + let count = self.ext.locked_delegate_dependencies_count() as _; + self.charge_gas(RuntimeCosts::Terminate(count))?; + + let mut beneficiary = H160::zero(); + memory.read_into_buf(beneficiary_ptr, beneficiary.as_bytes_mut())?; + self.ext.terminate(&beneficiary)?; + Err(TrapReason::Termination) + } +} + +// This is the API exposed to contracts. +// +// # Note +// +// Any input that leads to a out of bound error (reading or writing) or failing to decode +// data passed to the supervisor will lead to a trap. This is not documented explicitly +// for every function. +#[define_env] +pub mod env { + /// Noop function used to benchmark the time it takes to execute an empty function. + #[cfg(feature = "runtime-benchmarks")] + fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> { + Ok(()) + } + + /// Set the value at the given key in the contract storage. + /// See [`pallet_revive_uapi::HostFn::set_storage_v2`] + #[api_version(0)] + #[mutating] + fn set_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + value_ptr: u32, + value_len: u32, + ) -> Result { + self.set_storage(memory, flags, key_ptr, key_len, value_ptr, value_len) + } + + /// Clear the value at the given key in the contract storage. + /// See [`pallet_revive_uapi::HostFn::clear_storage`] + #[api_version(0)] + #[mutating] + fn clear_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + self.clear_storage(memory, flags, key_ptr, key_len) + } + + /// Retrieve the value under the given key from storage. + /// See [`pallet_revive_uapi::HostFn::get_storage`] + #[api_version(0)] + fn get_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + self.get_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr) + } + + /// Checks whether there is a value stored under the given key. + /// See [`pallet_revive_uapi::HostFn::contains_storage`] + #[api_version(0)] + fn contains_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + ) -> Result { + self.contains_storage(memory, flags, key_ptr, key_len) + } + + /// Retrieve and remove the value under the given key from storage. + /// See [`pallet_revive_uapi::HostFn::take_storage`] + #[api_version(0)] + #[mutating] + fn take_storage( + &mut self, + memory: &mut M, + flags: u32, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result { + self.take_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr) + } + + /// Transfer some value to another account. + /// See [`pallet_revive_uapi::HostFn::transfer`]. + #[api_version(0)] + #[mutating] + fn transfer( + &mut self, + memory: &mut M, + address_ptr: u32, + value_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::Transfer)?; + 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), + Err(err) => { + let code = Self::err_into_return_code(err)?; + Ok(code) + }, + } + } + + /// Make a call to another contract. + /// See [`pallet_revive_uapi::HostFn::call`]. + #[api_version(0)] + fn call( + &mut self, + memory: &mut M, + flags: u32, + callee_ptr: u32, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_ptr: u32, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + self.call( + memory, + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::Call { + callee_ptr, + value_ptr, + deposit_ptr, + weight: Weight::from_parts(ref_time_limit, proof_size_limit), + }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + /// Execute code in the context (storage, caller, value) of the current contract. + /// See [`pallet_revive_uapi::HostFn::delegate_call`]. + #[api_version(0)] + fn delegate_call( + &mut self, + memory: &mut M, + flags: u32, + code_hash_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + self.call( + memory, + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::DelegateCall { code_hash_ptr }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + /// Instantiate a contract with the specified code hash. + /// See [`pallet_revive_uapi::HostFn::instantiate`]. + #[api_version(0)] + #[mutating] + fn instantiate( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_ptr: u32, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + ) -> Result { + self.instantiate( + memory, + code_hash_ptr, + Weight::from_parts(ref_time_limit, proof_size_limit), + deposit_ptr, + value_ptr, + input_data_ptr, + input_data_len, + address_ptr, + output_ptr, + output_len_ptr, + salt_ptr, + ) + } + + /// Remove the calling account and transfer remaining **free** balance. + /// See [`pallet_revive_uapi::HostFn::terminate`]. + #[api_version(0)] + #[mutating] + fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> { + self.terminate(memory, beneficiary_ptr) + } + + /// Stores the input passed by the caller into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::input`]. + #[api_version(0)] + fn input(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + if let Some(input) = self.input_data.take() { + self.write_sandbox_output(memory, out_ptr, out_len_ptr, &input, false, |len| { + Some(RuntimeCosts::CopyToContract(len)) + })?; + self.input_data = Some(input); + Ok(()) + } else { + Err(Error::::InputForwarded.into()) + } + } + + /// Cease contract execution and save a data buffer as a result of the execution. + /// See [`pallet_revive_uapi::HostFn::return_value`]. + #[api_version(0)] + fn seal_return( + &mut self, + memory: &mut M, + flags: u32, + data_ptr: u32, + data_len: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?; + Err(TrapReason::Return(ReturnData { flags, data: memory.read(data_ptr, data_len)? })) + } + + /// Stores the address of the caller into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::caller`]. + #[api_version(0)] + fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Caller)?; + let caller = ::AddressMapper::to_address(self.ext.caller().account_id()?); + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + caller.as_bytes(), + false, + already_charged, + )?) + } + + /// Checks whether a specified address belongs to a contract. + /// See [`pallet_revive_uapi::HostFn::is_contract`]. + #[api_version(0)] + fn is_contract(&mut self, memory: &mut M, account_ptr: u32) -> Result { + self.charge_gas(RuntimeCosts::IsContract)?; + let mut address = H160::zero(); + memory.read_into_buf(account_ptr, address.as_bytes_mut())?; + Ok(self.ext.is_contract(&address) as u32) + } + + /// Retrieve the code hash for a specified contract address. + /// See [`pallet_revive_uapi::HostFn::code_hash`]. + #[api_version(0)] + fn code_hash( + &mut self, + memory: &mut M, + addr_ptr: u32, + out_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::CodeHash)?; + 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_fixed_sandbox_output( + memory, + out_ptr, + &value.as_bytes(), + false, + already_charged, + )?; + Ok(ReturnErrorCode::Success) + } else { + Ok(ReturnErrorCode::KeyNotFound) + } + } + + /// Retrieve the code hash of the currently executing contract. + /// See [`pallet_revive_uapi::HostFn::own_code_hash`]. + #[api_version(0)] + fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::OwnCodeHash)?; + let code_hash = *self.ext.own_code_hash(); + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + code_hash.as_bytes(), + false, + already_charged, + )?) + } + + /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// See [`pallet_revive_uapi::HostFn::caller_is_origin`]. + #[api_version(0)] + fn caller_is_origin(&mut self, _memory: &mut M) -> Result { + self.charge_gas(RuntimeCosts::CallerIsOrigin)?; + Ok(self.ext.caller_is_origin() as u32) + } + + /// Checks whether the caller of the current contract is root. + /// See [`pallet_revive_uapi::HostFn::caller_is_root`]. + #[api_version(0)] + fn caller_is_root(&mut self, _memory: &mut M) -> Result { + self.charge_gas(RuntimeCosts::CallerIsRoot)?; + Ok(self.ext.caller_is_root() as u32) + } + + /// Stores the address of the current contract into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::address`]. + #[api_version(0)] + fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Address)?; + let address = self.ext.address(); + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + address.as_bytes(), + false, + already_charged, + )?) + } + + /// Stores the price for the specified amount of weight into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::weight_to_fee`]. + #[api_version(0)] + fn weight_to_fee( + &mut self, + memory: &mut M, + ref_time_limit: u64, + proof_size_limit: u64, + out_ptr: u32, + ) -> Result<(), TrapReason> { + let weight = Weight::from_parts(ref_time_limit, proof_size_limit); + self.charge_gas(RuntimeCosts::WeightToFee)?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &self.ext.get_weight_price(weight).encode(), + false, + already_charged, + )?) + } + + /// Stores the amount of weight left into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::weight_left`]. + #[api_version(0)] + fn weight_left( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::GasLeft)?; + let gas_left = &self.ext.gas_meter().gas_left().encode(); + Ok(self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + gas_left, + false, + already_charged, + )?) + } + + /// Stores the *free* balance of the current account into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::balance`]. + #[api_version(0)] + fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Balance)?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.balance()), + false, + already_charged, + )?) + } + + /// Stores the *free* balance of the supplied address into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::balance`]. + #[api_version(0)] + fn balance_of( + &mut self, + memory: &mut M, + addr_ptr: u32, + out_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::BalanceOf)?; + let mut address = H160::zero(); + memory.read_into_buf(addr_ptr, address.as_bytes_mut())?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.balance_of(&address)), + false, + already_charged, + )?) + } + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::value_transferred`]. + #[api_version(0)] + fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::ValueTransferred)?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.value_transferred()), + false, + already_charged, + )?) + } + + /// Load the latest block timestamp into the supplied buffer + /// See [`pallet_revive_uapi::HostFn::now`]. + #[api_version(0)] + fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::Now)?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.now()), + false, + already_charged, + )?) + } + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::minimum_balance`]. + #[api_version(0)] + fn minimum_balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::MinimumBalance)?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.minimum_balance()), + false, + already_charged, + )?) + } + + /// Deposit a contract event with the data buffer and optional list of topics. + /// See [pallet_revive_uapi::HostFn::deposit_event] + #[api_version(0)] + #[mutating] + fn deposit_event( + &mut self, + memory: &mut M, + topics_ptr: u32, + num_topic: u32, + data_ptr: u32, + data_len: u32, + ) -> Result<(), TrapReason> { + 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 = match num_topic { + 0 => Vec::new(), + _ => { + 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 + }, + }; + + 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) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::BlockNumber)?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.block_number()), + false, + already_charged, + )?) + } + + /// Computes the SHA2 256-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_sha2_256`]. + #[api_version(0)] + fn hash_sha2_256( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashSha256(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, sha2_256, input_ptr, input_len, output_ptr, + )?) + } + + /// Computes the KECCAK 256-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_keccak_256`]. + #[api_version(0)] + fn hash_keccak_256( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, keccak_256, input_ptr, input_len, output_ptr, + )?) + } + + /// Computes the BLAKE2 256-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_blake2_256`]. + #[api_version(0)] + fn hash_blake2_256( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashBlake256(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, blake2_256, input_ptr, input_len, output_ptr, + )?) + } + + /// Computes the BLAKE2 128-bit hash on the given input buffer. + /// See [`pallet_revive_uapi::HostFn::hash_blake2_128`]. + #[api_version(0)] + fn hash_blake2_128( + &mut self, + memory: &mut M, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::HashBlake128(input_len))?; + Ok(self.compute_hash_on_intermediate_buffer( + memory, blake2_128, input_ptr, input_len, output_ptr, + )?) + } + + /// Call into the chain extension provided by the chain if any. + /// See [`pallet_revive_uapi::HostFn::call_chain_extension`]. + fn call_chain_extension( + &mut self, + memory: &mut M, + id: u32, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> Result { + use crate::chain_extension::{ChainExtension, Environment, RetVal}; + if !::ChainExtension::enabled() { + return Err(Error::::NoChainExtension.into()); + } + let mut chain_extension = self.chain_extension.take().expect( + "Constructor initializes with `Some`. This is the only place where it is set to `None`.\ + It is always reset to `Some` afterwards. qed", + ); + let env = + Environment::new(self, memory, id, input_ptr, input_len, output_ptr, output_len_ptr); + let ret = match chain_extension.call(env)? { + RetVal::Converging(val) => Ok(val), + RetVal::Diverging { flags, data } => + Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })), + }; + self.chain_extension = Some(chain_extension); + ret + } + + /// Emit a custom debug message. + /// See [`pallet_revive_uapi::HostFn::debug_message`]. + #[api_version(0)] + fn debug_message( + &mut self, + memory: &mut M, + str_ptr: u32, + str_len: u32, + ) -> Result { + let str_len = str_len.min(limits::DEBUG_BUFFER_BYTES); + self.charge_gas(RuntimeCosts::DebugMessage(str_len))?; + if self.ext.append_debug_buffer("") { + let data = memory.read(str_ptr, str_len)?; + if let Some(msg) = core::str::from_utf8(&data).ok() { + self.ext.append_debug_buffer(msg); + } + Ok(ReturnErrorCode::Success) + } else { + Ok(ReturnErrorCode::LoggingDisabled) + } + } + + /// Call some dispatchable of the runtime. + /// See [`frame_support::traits::call_runtime`]. + #[mutating] + fn call_runtime( + &mut self, + memory: &mut M, + call_ptr: u32, + call_len: u32, + ) -> Result { + use frame_support::dispatch::GetDispatchInfo; + self.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; + let call: ::RuntimeCall = memory.read_as_unbounded(call_ptr, call_len)?; + self.call_dispatchable::( + call.get_dispatch_info(), + RuntimeCosts::CallRuntime, + |runtime| runtime.ext.call_runtime(call), + ) + } + + /// Execute an XCM program locally, using the contract's address as the origin. + /// See [`pallet_revive_uapi::HostFn::execute_xcm`]. + #[mutating] + fn xcm_execute( + &mut self, + memory: &mut M, + msg_ptr: u32, + msg_len: u32, + ) -> Result { + use frame_support::dispatch::DispatchInfo; + use xcm::VersionedXcm; + use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo}; + + self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; + let message: VersionedXcm> = memory.read_as_unbounded(msg_ptr, msg_len)?; + + let execute_weight = + <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); + let weight = self.ext.gas_meter().gas_left().max(execute_weight); + let dispatch_info = DispatchInfo { weight, ..Default::default() }; + + self.call_dispatchable::( + dispatch_info, + RuntimeCosts::CallXcmExecute, + |runtime| { + let origin = crate::RawOrigin::Signed(runtime.ext.account_id().clone()).into(); + let weight_used = <::Xcm>::execute( + origin, + Box::new(message), + weight.saturating_sub(execute_weight), + )?; + + Ok(Some(weight_used.saturating_add(execute_weight)).into()) + }, + ) + } + + /// Send an XCM program from the contract to the specified destination. + /// See [`pallet_revive_uapi::HostFn::send_xcm`]. + #[mutating] + fn xcm_send( + &mut self, + memory: &mut M, + dest_ptr: u32, + dest_len: u32, + msg_ptr: u32, + msg_len: u32, + output_ptr: u32, + ) -> Result { + use xcm::{VersionedLocation, VersionedXcm}; + use xcm_builder::{SendController, SendControllerWeightInfo}; + + self.charge_gas(RuntimeCosts::CopyFromContract(dest_len))?; + let dest: VersionedLocation = memory.read_as_unbounded(dest_ptr, dest_len)?; + + self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; + let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?; + + let weight = <::Xcm as SendController<_>>::WeightInfo::send(); + self.charge_gas(RuntimeCosts::CallRuntime(weight))?; + let origin = crate::RawOrigin::Signed(self.ext.account_id().clone()).into(); + + match <::Xcm>::send(origin, dest.into(), message.into()) { + Ok(message_id) => { + memory.write(output_ptr, &message_id.encode())?; + Ok(ReturnErrorCode::Success) + }, + Err(e) => { + if self.ext.append_debug_buffer("") { + self.ext.append_debug_buffer("seal0::xcm_send failed with: "); + self.ext.append_debug_buffer(e.into()); + }; + Ok(ReturnErrorCode::XcmSendFailed) + }, + } + } + + /// Recovers the ECDSA public key from the given message hash and signature. + /// See [`pallet_revive_uapi::HostFn::ecdsa_recover`]. + #[api_version(0)] + fn ecdsa_recover( + &mut self, + memory: &mut M, + signature_ptr: u32, + message_hash_ptr: u32, + output_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::EcdsaRecovery)?; + + let mut signature: [u8; 65] = [0; 65]; + memory.read_into_buf(signature_ptr, &mut signature)?; + let mut message_hash: [u8; 32] = [0; 32]; + memory.read_into_buf(message_hash_ptr, &mut message_hash)?; + + let result = self.ext.ecdsa_recover(&signature, &message_hash); + + match result { + Ok(pub_key) => { + // Write the recovered compressed ecdsa public key back into the sandboxed output + // buffer. + memory.write(output_ptr, pub_key.as_ref())?; + + Ok(ReturnErrorCode::Success) + }, + Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), + } + } + + /// Verify a sr25519 signature + /// See [`pallet_revive_uapi::HostFn::sr25519_verify`]. + #[api_version(0)] + fn sr25519_verify( + &mut self, + memory: &mut M, + signature_ptr: u32, + pub_key_ptr: u32, + message_len: u32, + message_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?; + + let mut signature: [u8; 64] = [0; 64]; + memory.read_into_buf(signature_ptr, &mut signature)?; + + let mut pub_key: [u8; 32] = [0; 32]; + memory.read_into_buf(pub_key_ptr, &mut pub_key)?; + + let message: Vec = memory.read(message_ptr, message_len)?; + + if self.ext.sr25519_verify(&signature, &message, &pub_key) { + Ok(ReturnErrorCode::Success) + } else { + Ok(ReturnErrorCode::Sr25519VerifyFailed) + } + } + + /// Replace the contract code at the specified address with new code. + /// See [`pallet_revive_uapi::HostFn::set_code_hash`]. + #[api_version(0)] + #[mutating] + fn set_code_hash( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::SetCodeHash)?; + let code_hash: 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)?; + Ok(code) + }, + Ok(()) => Ok(ReturnErrorCode::Success), + } + } + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// See [`pallet_revive_uapi::HostFn::ecdsa_to_eth_address`]. + #[api_version(0)] + fn ecdsa_to_eth_address( + &mut self, + memory: &mut M, + key_ptr: u32, + out_ptr: u32, + ) -> Result { + self.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; + let mut compressed_key: [u8; 33] = [0; 33]; + memory.read_into_buf(key_ptr, &mut compressed_key)?; + let result = self.ext.ecdsa_to_eth_address(&compressed_key); + match result { + Ok(eth_address) => { + memory.write(out_ptr, eth_address.as_ref())?; + Ok(ReturnErrorCode::Success) + }, + Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), + } + } + + /// Adds a new delegate dependency to the contract. + /// See [`pallet_revive_uapi::HostFn::lock_delegate_dependency`]. + #[api_version(0)] + #[mutating] + fn lock_delegate_dependency( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::LockDelegateDependency)?; + let code_hash = memory.read_h256(code_hash_ptr)?; + self.ext.lock_delegate_dependency(code_hash)?; + Ok(()) + } + + /// Removes the delegate dependency from the contract. + /// see [`pallet_revive_uapi::HostFn::unlock_delegate_dependency`]. + #[api_version(0)] + #[mutating] + fn unlock_delegate_dependency( + &mut self, + memory: &mut M, + code_hash_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; + let code_hash = memory.read_h256(code_hash_ptr)?; + self.ext.unlock_delegate_dependency(&code_hash)?; + Ok(()) + } +} diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..b66c28bdf7d86b12f77845f051134185276ee3a5 --- /dev/null +++ b/substrate/frame/revive/src/weights.rs @@ -0,0 +1,1770 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_revive` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-07-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/production/substrate-node +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_revive +// --chain=dev +// --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/contracts/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_revive`. +pub trait WeightInfo { + fn on_process_deletion_queue_batch() -> Weight; + fn on_initialize_per_trie_key(k: u32, ) -> Weight; + fn call_with_code_per_byte(c: 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 remove_code() -> Weight; + fn set_code() -> Weight; + fn noop_host_fn(r: u32, ) -> Weight; + fn seal_caller() -> Weight; + fn seal_is_contract() -> Weight; + fn seal_code_hash() -> Weight; + fn seal_own_code_hash() -> Weight; + fn seal_caller_is_origin() -> Weight; + fn seal_caller_is_root() -> Weight; + fn seal_address() -> Weight; + fn seal_gas_left() -> Weight; + fn seal_balance() -> Weight; + fn seal_balance_of() -> Weight; + fn seal_value_transferred() -> Weight; + fn seal_minimum_balance() -> Weight; + fn seal_block_number() -> Weight; + fn seal_now() -> Weight; + fn seal_weight_to_fee() -> Weight; + fn seal_input(n: u32, ) -> Weight; + fn seal_return(n: u32, ) -> Weight; + fn seal_terminate(n: u32, ) -> Weight; + fn seal_random() -> Weight; + fn seal_deposit_event(t: u32, n: u32, ) -> Weight; + fn seal_debug_message(i: u32, ) -> Weight; + fn get_storage_empty() -> Weight; + fn get_storage_full() -> Weight; + fn set_storage_empty() -> Weight; + fn set_storage_full() -> Weight; + fn seal_set_storage(n: u32, o: u32, ) -> Weight; + fn seal_clear_storage(n: u32, ) -> Weight; + fn seal_get_storage(n: u32, ) -> Weight; + fn seal_contains_storage(n: u32, ) -> Weight; + fn seal_take_storage(n: u32, ) -> Weight; + fn set_transient_storage_empty() -> Weight; + fn set_transient_storage_full() -> Weight; + fn get_transient_storage_empty() -> Weight; + fn get_transient_storage_full() -> Weight; + fn rollback_transient_storage() -> Weight; + fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight; + fn seal_clear_transient_storage(n: u32, ) -> Weight; + fn seal_get_transient_storage(n: u32, ) -> Weight; + fn seal_contains_transient_storage(n: u32, ) -> Weight; + fn seal_take_transient_storage(n: u32, ) -> Weight; + fn seal_transfer() -> Weight; + fn seal_call(t: u32, i: u32, ) -> Weight; + fn seal_delegate_call() -> Weight; + fn seal_instantiate(i: u32) -> Weight; + fn seal_hash_sha2_256(n: u32, ) -> Weight; + fn seal_hash_keccak_256(n: u32, ) -> Weight; + fn seal_hash_blake2_256(n: u32, ) -> Weight; + fn seal_hash_blake2_128(n: u32, ) -> Weight; + fn seal_sr25519_verify(n: u32, ) -> Weight; + fn seal_ecdsa_recover() -> Weight; + fn seal_ecdsa_to_eth_address() -> Weight; + fn seal_set_code_hash() -> Weight; + fn lock_delegate_dependency() -> Weight; + fn unlock_delegate_dependency() -> Weight; + fn seal_reentrance_count() -> Weight; + fn seal_account_reentrance_count() -> Weight; + fn seal_instantiation_nonce() -> Weight; + fn instr_i64_load_store(r: u32, ) -> Weight; +} + +/// Weights for `pallet_revive` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:0) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn on_process_deletion_queue_batch() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 1_915_000 picoseconds. + Weight::from_parts(1_986_000, 1627) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `k` is `[0, 1024]`. + fn on_initialize_per_trie_key(k: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `452 + k * (69 ±0)` + // Estimated: `442 + k * (70 ±0)` + // Minimum execution time: 11_103_000 picoseconds. + Weight::from_parts(11_326_000, 442) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn call_with_code_per_byte(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `800 + c * (1 ±0)` + // Estimated: `4266 + c * (1 ±0)` + // Minimum execution time: 247_545_000 picoseconds. + Weight::from_parts(268_016_699, 4266) + // Standard Error: 4 + .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:2 w:2) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate_with_code(c: u32, i: u32) -> 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(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate(i: u32) -> 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(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn call() -> Weight { + // Proof Size summary in bytes: + // Measured: `826` + // Estimated: `4291` + // Minimum execution time: 165_067_000 picoseconds. + Weight::from_parts(168_582_000, 4291) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 229_454_000 picoseconds. + Weight::from_parts(251_495_551, 3607) + // Standard Error: 71 + .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_relaxed(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 240_390_000 picoseconds. + Weight::from_parts(273_854_266, 3607) + // Standard Error: 243 + .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn remove_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3780` + // Minimum execution time: 39_374_000 picoseconds. + Weight::from_parts(40_247_000, 3780) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 24_473_000 picoseconds. + Weight::from_parts(25_890_000, 6492) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// The range of component `r` is `[0, 1600]`. + fn noop_host_fn(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_528_000 picoseconds. + Weight::from_parts(9_301_010, 0) + // Standard Error: 98 + .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) + } + fn seal_caller() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(678_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_is_contract() -> Weight { + // Proof Size summary in bytes: + // Measured: `354` + // Estimated: `3819` + // Minimum execution time: 6_107_000 picoseconds. + Weight::from_parts(6_235_000, 3819) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `3912` + // Minimum execution time: 7_316_000 picoseconds. + Weight::from_parts(7_653_000, 3912) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn seal_own_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 721_000 picoseconds. + Weight::from_parts(764_000, 0) + } + fn seal_caller_is_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 369_000 picoseconds. + Weight::from_parts(417_000, 0) + } + fn seal_caller_is_root() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(349_000, 0) + } + fn seal_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(628_000, 0) + } + fn seal_gas_left() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(730_000, 0) + } + fn seal_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 4_361_000 picoseconds. + Weight::from_parts(4_577_000, 0) + } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn seal_balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 3_751_000 picoseconds. + Weight::from_parts(3_874_000, 3517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn seal_value_transferred() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 560_000 picoseconds. + Weight::from_parts(603_000, 0) + } + fn seal_minimum_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(610_000, 0) + } + fn seal_block_number() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(583_000, 0) + } + fn seal_now() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 550_000 picoseconds. + Weight::from_parts(602_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) + fn seal_weight_to_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `67` + // Estimated: `1552` + // Minimum execution time: 4_065_000 picoseconds. + Weight::from_parts(4_291_000, 1552) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_input(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 487_000 picoseconds. + Weight::from_parts(517_000, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_return(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(372_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) + } + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::DeletionQueue` (r:0 w:1) + /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// The range of component `n` is `[0, 32]`. + fn seal_terminate(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `319 + n * (78 ±0)` + // Estimated: `3784 + n * (2553 ±0)` + // Minimum execution time: 13_251_000 picoseconds. + Weight::from_parts(15_257_892, 3784) + // Standard Error: 7_089 + .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2553).saturating_mul(n.into())) + } + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `Measured`) + fn seal_random() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `1561` + // Minimum execution time: 3_434_000 picoseconds. + Weight::from_parts(3_605_000, 1561) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `System::EventTopics` (r:4 w:4) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `t` is `[0, 4]`. + /// The range of component `n` is `[0, 16384]`. + fn seal_deposit_event(t: u32, n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `990 + t * (2475 ±0)` + // Minimum execution time: 3_668_000 picoseconds. + Weight::from_parts(3_999_591, 990) + // Standard Error: 5_767 + .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) + } + /// The range of component `i` is `[0, 1048576]`. + fn seal_debug_message(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 443_000 picoseconds. + Weight::from_parts(472_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 13_752_000 picoseconds. + Weight::from_parts(14_356_000, 16618) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 43_444_000 picoseconds. + Weight::from_parts(45_087_000, 26628) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(16_010_000, 16618) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 47_020_000 picoseconds. + Weight::from_parts(50_152_000, 26628) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `250 + o * (1 ±0)` + // Estimated: `249 + o * (1 ±0)` + // Minimum execution time: 8_824_000 picoseconds. + Weight::from_parts(8_915_233, 249) + // Standard Error: 1 + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_133_000 picoseconds. + Weight::from_parts(7_912_778, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_get_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_746_000 picoseconds. + Weight::from_parts(7_647_236, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_247_000 picoseconds. + Weight::from_parts(6_952_661, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_take_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_428_000 picoseconds. + Weight::from_parts(8_384_015, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + fn set_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_478_000 picoseconds. + Weight::from_parts(1_533_000, 0) + } + fn set_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_485_000 picoseconds. + Weight::from_parts(2_728_000, 0) + } + fn get_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_195_000 picoseconds. + Weight::from_parts(3_811_000, 0) + } + fn get_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_902_000 picoseconds. + Weight::from_parts(4_118_000, 0) + } + fn rollback_transient_storage() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_571_000 picoseconds. + Weight::from_parts(1_662_000, 0) + } + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_250_000 picoseconds. + Weight::from_parts(2_465_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_012_000 picoseconds. + Weight::from_parts(2_288_004, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_get_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_906_000 picoseconds. + Weight::from_parts(2_121_040, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_736_000 picoseconds. + Weight::from_parts(1_954_728, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_take_transient_storage(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_872_000 picoseconds. + Weight::from_parts(8_125_644, 0) + } + fn seal_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 8_489_000 picoseconds. + Weight::from_parts(8_791_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `t` is `[0, 1]`. + /// The range of component `i` is `[0, 1048576]`. + fn seal_call(t: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `620 + t * (280 ±0)` + // Estimated: `4085 + t * (2182 ±0)` + // Minimum execution time: 122_759_000 picoseconds. + Weight::from_parts(120_016_020, 4085) + // Standard Error: 173_118 + .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2182).saturating_mul(t.into())) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_delegate_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 111_566_000 picoseconds. + Weight::from_parts(115_083_000, 3895) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `i` is `[0, 983040]`. + /// The range of component `s` is `[0, 983040]`. + fn seal_instantiate(i: u32) -> 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(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_sha2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 966_000 picoseconds. + Weight::from_parts(9_599_151, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_keccak_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_416_000 picoseconds. + Weight::from_parts(10_964_255, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 821_000 picoseconds. + Weight::from_parts(6_579_283, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_128(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 773_000 picoseconds. + Weight::from_parts(10_990_209, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 125697]`. + fn seal_sr25519_verify(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 43_195_000 picoseconds. + Weight::from_parts(41_864_855, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) + } + fn seal_ecdsa_recover() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 47_747_000 picoseconds. + Weight::from_parts(49_219_000, 0) + } + fn seal_ecdsa_to_eth_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_854_000 picoseconds. + Weight::from_parts(12_962_000, 0) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_set_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 17_868_000 picoseconds. + Weight::from_parts(18_486_000, 3895) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn lock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3820` + // Minimum execution time: 8_393_000 picoseconds. + Weight::from_parts(8_640_000, 3820) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + fn unlock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3558` + // Minimum execution time: 7_489_000 picoseconds. + Weight::from_parts(7_815_000, 3558) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn seal_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 299_000 picoseconds. + Weight::from_parts(339_000, 0) + } + fn seal_account_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 324_000 picoseconds. + Weight::from_parts(380_000, 0) + } + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn seal_instantiation_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `219` + // Estimated: `1704` + // Minimum execution time: 2_768_000 picoseconds. + Weight::from_parts(3_025_000, 1704) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// The range of component `r` is `[0, 5000]`. + fn instr_i64_load_store(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766_000 picoseconds. + Weight::from_parts(722_169, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:0) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn on_process_deletion_queue_batch() -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `1627` + // Minimum execution time: 1_915_000 picoseconds. + Weight::from_parts(1_986_000, 1627) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `k` is `[0, 1024]`. + fn on_initialize_per_trie_key(k: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `452 + k * (69 ±0)` + // Estimated: `442 + k * (70 ±0)` + // Minimum execution time: 11_103_000 picoseconds. + Weight::from_parts(11_326_000, 442) + // Standard Error: 2_291 + .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) + .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn call_with_code_per_byte(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `800 + c * (1 ±0)` + // Estimated: `4266 + c * (1 ±0)` + // Minimum execution time: 247_545_000 picoseconds. + Weight::from_parts(268_016_699, 4266) + // Standard Error: 4 + .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:2 w:2) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate_with_code(c: u32, i: u32) -> 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(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:1) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// The range of component `i` is `[0, 1048576]`. + /// The range of component `s` is `[0, 1048576]`. + fn instantiate(i: u32) -> 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(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn call() -> Weight { + // Proof Size summary in bytes: + // Measured: `826` + // Estimated: `4291` + // Minimum execution time: 165_067_000 picoseconds. + Weight::from_parts(168_582_000, 4291) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_enforced(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 229_454_000 picoseconds. + Weight::from_parts(251_495_551, 3607) + // Standard Error: 71 + .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// The range of component `c` is `[0, 125952]`. + fn upload_code_determinism_relaxed(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `3607` + // Minimum execution time: 240_390_000 picoseconds. + Weight::from_parts(273_854_266, 3607) + // Standard Error: 243 + .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:0 w:1) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn remove_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `315` + // Estimated: `3780` + // Minimum execution time: 39_374_000 picoseconds. + Weight::from_parts(40_247_000, 3780) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) + /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn set_code() -> Weight { + // Proof Size summary in bytes: + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 24_473_000 picoseconds. + Weight::from_parts(25_890_000, 6492) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// The range of component `r` is `[0, 1600]`. + fn noop_host_fn(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_528_000 picoseconds. + Weight::from_parts(9_301_010, 0) + // Standard Error: 98 + .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) + } + fn seal_caller() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(678_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_is_contract() -> Weight { + // Proof Size summary in bytes: + // Measured: `354` + // Estimated: `3819` + // Minimum execution time: 6_107_000 picoseconds. + Weight::from_parts(6_235_000, 3819) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + fn seal_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `447` + // Estimated: `3912` + // Minimum execution time: 7_316_000 picoseconds. + Weight::from_parts(7_653_000, 3912) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn seal_own_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 721_000 picoseconds. + Weight::from_parts(764_000, 0) + } + fn seal_caller_is_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 369_000 picoseconds. + Weight::from_parts(417_000, 0) + } + fn seal_caller_is_root() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(349_000, 0) + } + fn seal_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(628_000, 0) + } + fn seal_gas_left() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(730_000, 0) + } + fn seal_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 4_361_000 picoseconds. + Weight::from_parts(4_577_000, 0) + } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn seal_balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 3_751_000 picoseconds. + Weight::from_parts(3_874_000, 3517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn seal_value_transferred() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 560_000 picoseconds. + Weight::from_parts(603_000, 0) + } + fn seal_minimum_balance() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(610_000, 0) + } + fn seal_block_number() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(583_000, 0) + } + fn seal_now() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 550_000 picoseconds. + Weight::from_parts(602_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) + fn seal_weight_to_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `67` + // Estimated: `1552` + // Minimum execution time: 4_065_000 picoseconds. + Weight::from_parts(4_291_000, 1552) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_input(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 487_000 picoseconds. + Weight::from_parts(517_000, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048572]`. + fn seal_return(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 318_000 picoseconds. + Weight::from_parts(372_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) + } + /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) + /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::DeletionQueue` (r:0 w:1) + /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// The range of component `n` is `[0, 32]`. + fn seal_terminate(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `319 + n * (78 ±0)` + // Estimated: `3784 + n * (2553 ±0)` + // Minimum execution time: 13_251_000 picoseconds. + Weight::from_parts(15_257_892, 3784) + // Standard Error: 7_089 + .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2553).saturating_mul(n.into())) + } + /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) + /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `Measured`) + fn seal_random() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `1561` + // Minimum execution time: 3_434_000 picoseconds. + Weight::from_parts(3_605_000, 1561) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `System::EventTopics` (r:4 w:4) + /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `t` is `[0, 4]`. + /// The range of component `n` is `[0, 16384]`. + fn seal_deposit_event(t: u32, n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `990 + t * (2475 ±0)` + // Minimum execution time: 3_668_000 picoseconds. + Weight::from_parts(3_999_591, 990) + // Standard Error: 5_767 + .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) + } + /// The range of component `i` is `[0, 1048576]`. + fn seal_debug_message(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 443_000 picoseconds. + Weight::from_parts(472_000, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 13_752_000 picoseconds. + Weight::from_parts(14_356_000, 16618) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn get_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 43_444_000 picoseconds. + Weight::from_parts(45_087_000, 26628) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `16618` + // Estimated: `16618` + // Minimum execution time: 15_616_000 picoseconds. + Weight::from_parts(16_010_000, 16618) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `26628` + // Estimated: `26628` + // Minimum execution time: 47_020_000 picoseconds. + Weight::from_parts(50_152_000, 26628) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `250 + o * (1 ±0)` + // Estimated: `249 + o * (1 ±0)` + // Minimum execution time: 8_824_000 picoseconds. + Weight::from_parts(8_915_233, 249) + // Standard Error: 1 + .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_133_000 picoseconds. + Weight::from_parts(7_912_778, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_get_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_746_000 picoseconds. + Weight::from_parts(7_647_236, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 6_247_000 picoseconds. + Weight::from_parts(6_952_661, 248) + // Standard Error: 1 + .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[0, 16384]`. + fn seal_take_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `248 + n * (1 ±0)` + // Estimated: `248 + n * (1 ±0)` + // Minimum execution time: 7_428_000 picoseconds. + Weight::from_parts(8_384_015, 248) + // Standard Error: 2 + .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + fn set_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_478_000 picoseconds. + Weight::from_parts(1_533_000, 0) + } + fn set_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_485_000 picoseconds. + Weight::from_parts(2_728_000, 0) + } + fn get_transient_storage_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_195_000 picoseconds. + Weight::from_parts(3_811_000, 0) + } + fn get_transient_storage_full() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_902_000 picoseconds. + Weight::from_parts(4_118_000, 0) + } + fn rollback_transient_storage() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_571_000 picoseconds. + Weight::from_parts(1_662_000, 0) + } + /// The range of component `n` is `[0, 16384]`. + /// The range of component `o` is `[0, 16384]`. + fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_250_000 picoseconds. + Weight::from_parts(2_465_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_clear_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_012_000 picoseconds. + Weight::from_parts(2_288_004, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_get_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_906_000 picoseconds. + Weight::from_parts(2_121_040, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_contains_transient_storage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_736_000 picoseconds. + Weight::from_parts(1_954_728, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 16384]`. + fn seal_take_transient_storage(_n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_872_000 picoseconds. + Weight::from_parts(8_125_644, 0) + } + fn seal_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `140` + // Estimated: `0` + // Minimum execution time: 8_489_000 picoseconds. + Weight::from_parts(8_791_000, 0) + } + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `t` is `[0, 1]`. + /// The range of component `i` is `[0, 1048576]`. + fn seal_call(t: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `620 + t * (280 ±0)` + // Estimated: `4085 + t * (2182 ±0)` + // Minimum execution time: 122_759_000 picoseconds. + Weight::from_parts(120_016_020, 4085) + // Standard Error: 173_118 + .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2182).saturating_mul(t.into())) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_delegate_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 111_566_000 picoseconds. + Weight::from_parts(115_083_000, 3895) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) + /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `i` is `[0, 983040]`. + /// The range of component `s` is `[0, 983040]`. + fn seal_instantiate(i: u32) -> 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(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_sha2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 966_000 picoseconds. + Weight::from_parts(9_599_151, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_keccak_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_416_000 picoseconds. + Weight::from_parts(10_964_255, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_256(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 821_000 picoseconds. + Weight::from_parts(6_579_283, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 1048576]`. + fn seal_hash_blake2_128(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 773_000 picoseconds. + Weight::from_parts(10_990_209, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) + } + /// The range of component `n` is `[0, 125697]`. + fn seal_sr25519_verify(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 43_195_000 picoseconds. + Weight::from_parts(41_864_855, 0) + // Standard Error: 9 + .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) + } + fn seal_ecdsa_recover() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 47_747_000 picoseconds. + Weight::from_parts(49_219_000, 0) + } + fn seal_ecdsa_to_eth_address() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 12_854_000 picoseconds. + Weight::from_parts(12_962_000, 0) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Contracts::PristineCode` (r:1 w:0) + /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + fn seal_set_code_hash() -> Weight { + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `3895` + // Minimum execution time: 17_868_000 picoseconds. + Weight::from_parts(18_486_000, 3895) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + fn lock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3820` + // Minimum execution time: 8_393_000 picoseconds. + Weight::from_parts(8_640_000, 3820) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + fn unlock_delegate_dependency() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3558` + // Minimum execution time: 7_489_000 picoseconds. + Weight::from_parts(7_815_000, 3558) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + fn seal_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 299_000 picoseconds. + Weight::from_parts(339_000, 0) + } + fn seal_account_reentrance_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 324_000 picoseconds. + Weight::from_parts(380_000, 0) + } + /// Storage: `Contracts::Nonce` (r:1 w:0) + /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + fn seal_instantiation_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `219` + // Estimated: `1704` + // Minimum execution time: 2_768_000 picoseconds. + Weight::from_parts(3_025_000, 1704) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// The range of component `r` is `[0, 5000]`. + fn instr_i64_load_store(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 766_000 picoseconds. + Weight::from_parts(722_169, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) + } +} diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..862bf36f07cd0c561da56a53931be408a3adf793 --- /dev/null +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "pallet-revive-uapi" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "Exposes all the host functions that a contract can import." + +[lints] +workspace = true + +[dependencies] +paste = { workspace = true } +bitflags = { workspace = true } +scale-info = { features = ["derive"], optional = true, workspace = true } +codec = { features = [ + "derive", + "max-encoded-len", +], optional = true, workspace = true } + +[target.'cfg(target_arch = "riscv32")'.dependencies] +polkavm-derive = { version = "0.10.0" } + +[package.metadata.docs.rs] +default-target = ["wasm32-unknown-unknown"] + +[features] +default = ["scale"] +scale = ["dep:codec", "scale-info"] diff --git a/substrate/frame/revive/uapi/src/flags.rs b/substrate/frame/revive/uapi/src/flags.rs new file mode 100644 index 0000000000000000000000000000000000000000..763a89d6c030489b3867417357c8310cbfa686f3 --- /dev/null +++ b/substrate/frame/revive/uapi/src/flags.rs @@ -0,0 +1,90 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bitflags::bitflags; + +bitflags! { + /// Flags used by a contract to customize exit behaviour. + #[cfg_attr(feature = "scale", derive(codec::Encode, codec::Decode, scale_info::TypeInfo))] + #[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; + } +} + +bitflags! { + /// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`. + pub struct CallFlags: u32 { + /// Forward the input of current function to the callee. + /// + /// Supplied input pointers are ignored when set. + /// + /// # Note + /// + /// A forwarding call will consume the current contracts input. Any attempt to + /// access the input after this call returns will lead to [`Error::InputForwarded`]. + /// It does not matter if this is due to calling `seal_input` or trying another + /// forwarding call. Consider using [`Self::CLONE_INPUT`] in order to preserve + /// the input. + const FORWARD_INPUT = 0b0000_0001; + /// Identical to [`Self::FORWARD_INPUT`] but without consuming the input. + /// + /// This adds some additional weight costs to the call. + /// + /// # Note + /// + /// This implies [`Self::FORWARD_INPUT`] and takes precedence when both are set. + const CLONE_INPUT = 0b0000_0010; + /// Do not return from the call but rather return the result of the callee to the + /// callers caller. + /// + /// # Note + /// + /// This makes the current contract completely transparent to its caller by replacing + /// this contracts potential output by the callee ones. Any code after `seal_call` + /// can be safely considered unreachable. + const TAIL_CALL = 0b0000_0100; + /// Allow the callee to reenter into the current contract. + /// + /// Without this flag any reentrancy into the current contract that originates from + /// the callee (or any of its callees) is denied. This includes the first callee: + /// You cannot call into yourself with this flag set. + /// + /// # Note + /// + /// For `seal_delegate_call` should be always unset, otherwise + /// [`Error::InvalidCallFlags`] is returned. + const ALLOW_REENTRY = 0b0000_1000; + /// Indicates that the callee is restricted from modifying the state during call execution, + /// equivalent to Ethereum's STATICCALL. + /// + /// # Note + /// + /// For `seal_delegate_call` should be always unset, otherwise + /// [`Error::InvalidCallFlags`] is returned. + const READ_ONLY = 0b0001_0000; + } +} + +bitflags! { + /// Flags used by a contract to customize storage behaviour. + pub struct StorageFlags: u32 { + /// Access the transient storage instead of the persistent one. + const TRANSIENT = 0x0000_0001; + } +} diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs new file mode 100644 index 0000000000000000000000000000000000000000..538de7ea251d25975ca2059efff11946e99f49b1 --- /dev/null +++ b/substrate/frame/revive/uapi/src/host.rs @@ -0,0 +1,625 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::{CallFlags, Result, ReturnFlags, StorageFlags}; +use paste::paste; + +#[cfg(target_arch = "riscv32")] +mod riscv32; + +macro_rules! hash_fn { + ( $name:ident, $bytes:literal ) => { + paste! { + #[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."] + #[doc = "\n# Notes\n"] + #[doc = "- The `input` and `output` buffer may overlap."] + #[doc = "- The output buffer is expected to hold at least " $bytes " bits."] + #[doc = "- It is the callers responsibility to provide an output buffer that is large enough to hold the expected amount of bytes returned by the hash function."] + #[doc = "\n# Parameters\n"] + #[doc = "- `input`: The input data buffer."] + #[doc = "- `output`: The output buffer to write the hash result to."] + fn [](input: &[u8], output: &mut [u8; $bytes]); + } + }; +} + +/// Implements [`HostFn`] when compiled on supported architectures (RISC-V). +pub enum HostFnImpl {} + +/// Defines all the host apis available to contracts. +pub trait HostFn: private::Sealed { + /// Stores the address of the current contract into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the address. + fn address(output: &mut [u8; 20]); + + /// Lock a new delegate dependency to the contract. + /// + /// Traps if the maximum number of delegate_dependencies is reached or if + /// the delegate dependency already exists. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps + /// otherwise. + fn lock_delegate_dependency(code_hash: &[u8; 32]); + + /// Stores the **reducible** balance of the current account into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the balance. + fn balance(output: &mut [u8; 32]); + + /// Stores the **reducible** balance of the supplied address into the supplied buffer. + /// + /// # Parameters + /// + /// - `addr`: The target address of which to retreive the free balance. + /// - `output`: A reference to the output data buffer to write the balance. + fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]); + + /// Stores the current block number of the current contract into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the block number. + fn block_number(output: &mut [u8; 32]); + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// # Parameters + /// + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. 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. + /// + /// # Errors + /// + /// An error means that the call wasn't successful output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] + fn call( + flags: CallFlags, + callee: &[u8; 20], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8; 32]>, + value: &[u8; 32], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Call into the chain extension provided by the chain if any. + /// + /// Handling of the input values is up to the specific chain extension and so is the + /// return value. The extension can decide to use the inputs as primitive inputs or as + /// in/out arguments by interpreting them as pointers. Any caller of this function + /// must therefore coordinate with the chain that it targets. + /// + /// # Note + /// + /// If no chain extension exists the contract will trap with the `NoChainExtension` + /// module error. + /// + /// # Parameters + /// + /// - `func_id`: The function id of the chain extension. + /// - `input`: The input data buffer. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. + /// + /// # Return + /// + /// The chain extension returned value, if executed successfully. + fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32; + + /// Call some dispatchable of the runtime. + /// + /// # Parameters + /// + /// - `call`: The call data. + /// + /// # Return + /// + /// Returns `Error::Success` when the dispatchable was successfully executed and + /// returned `Ok`. When the dispatchable was executed but returned an error + /// `Error::CallRuntimeFailed` is returned. The full error is not + /// provided because it is not guaranteed to be stable. + /// + /// # Comparison with `ChainExtension` + /// + /// Just as a chain extension this API allows the runtime to extend the functionality + /// of contracts. While making use of this function is generally easier it cannot be + /// used in all cases. Consider writing a chain extension if you need to do perform + /// one of the following tasks: + /// + /// - Return data. + /// - Provide functionality **exclusively** to contracts. + /// - Provide custom weights. + /// - Avoid the need to keep the `Call` data structure stable. + fn call_runtime(call: &[u8]) -> Result; + + /// Stores the address of the caller into the supplied buffer. + /// + /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the + /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then + /// the address of the contract will be returned. + /// + /// If there is no address associated with the caller (e.g. because the caller is root) then + /// it traps with `BadOrigin`. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the caller address. + fn caller(output: &mut [u8; 20]); + + /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// + /// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract + /// is being called by a contract or a plain account. The reason is that it performs better + /// since it does not need to do any storage lookups. + /// + /// # Return + /// + /// A return value of `true` indicates that this contract is being called by a plain account + /// and `false` indicates that the caller is another contract. + fn caller_is_origin() -> bool; + + /// Checks whether the caller of the current contract is root. + /// + /// Note that only the origin of the call stack can be root. Hence this function returning + /// `true` implies that the contract is being called by the origin. + /// + /// A return value of `true` indicates that this contract is being called by a root origin, + /// and `false` indicates that the caller is a signed origin. + fn caller_is_root() -> u32; + + /// Clear the value at the given key in the contract storage. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option; + + /// Retrieve the code hash for a specified contract address. + /// + /// # Parameters + /// + /// - `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(addr: &[u8; 20], output: &mut [u8; 32]) -> Result; + + /// Checks whether there is a value stored under the given key. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option; + + /// Emit a custom debug message. + /// + /// No newlines are added to the supplied message. + /// Specifying invalid UTF-8 just drops the message with no trap. + /// + /// This is a no-op if debug message recording is disabled which is always the case + /// when the code is executing on-chain. The message is interpreted as UTF-8 and + /// appended to the debug buffer which is then supplied to the calling RPC client. + /// + /// # Note + /// + /// Even though no action is taken when debug message recording is disabled there is still + /// a non trivial overhead (and weight cost) associated with calling this function. Contract + /// languages should remove calls to this function (either at runtime or compile time) when + /// not being executed as an RPC. For example, they could allow users to disable logging + /// through compile time flags (cargo features) for on-chain deployment. Additionally, the + /// return value of this function can be cached in order to prevent further calls at runtime. + fn debug_message(str: &[u8]) -> Result; + + /// Execute code in the context (storage, caller, value) of the current contract. + /// + /// Reentrancy protection is always disabled since the callee is allowed + /// to modify the callers storage. This makes going through a reentrancy attack + /// unnecessary for the callee when it wants to exploit the caller. + /// + /// # Parameters + /// + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `code_hash`: The hash of the code to be executed. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. + /// + /// # Errors + /// + /// An error means that the call wasn't successful and no output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn delegate_call( + flags: CallFlags, + code_hash: &[u8; 32], + input_data: &[u8], + output: Option<&mut &mut [u8]>, + ) -> Result; + + /// Deposit a contract event with the data buffer and optional list of topics. There is a limit + /// on the maximum number of topics specified by `event_topics`. + /// + /// There should not be any duplicates in `topics`. + /// + /// # Parameters + /// + /// - `topics`: The topics list. 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. + /// + /// Writes the public key into the given output buffer. + /// Assumes the secp256k1 curve. + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message_hash`: The message hash bytes. + /// - `output`: A reference to the output data buffer to write the public key. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result; + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// it into the supplied buffer. + /// + /// # Parameters + /// + /// - `pubkey`: The public key bytes. + /// - `output`: A reference to the output data buffer to write the address. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; + + /// Stores the amount of weight left into the supplied buffer. + /// The data is encoded as Weight. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the weight left. + fn weight_left(output: &mut &mut [u8]); + + /// Retrieve the value under the given key from storage. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] + fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result; + + hash_fn!(sha2_256, 32); + hash_fn!(keccak_256, 32); + hash_fn!(blake2_256, 32); + hash_fn!(blake2_128, 16); + + /// Stores the input passed by the caller into the supplied buffer. + /// + /// # Note + /// + /// This function traps if: + /// - the input is larger than the available space. + /// - the input was previously forwarded by a [`call()`][`Self::call()`]. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the input data. + fn input(output: &mut &mut [u8]); + + /// Instantiate a contract with the specified code hash. + /// + /// This function creates an account and executes the constructor defined in the code specified + /// by the code hash. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the code to be instantiated. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. 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. + /// - `output`: A reference to the return value buffer to write the constructor output buffer. + /// If `None` is provided then the output buffer is not copied. + /// - `salt`: The salt bytes to use for this instantiation. + /// + /// # Errors + /// + /// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration for more + /// information on those errors. Here we only note things specific to this function. + /// + /// An error means that the account wasn't created and no address or output buffer + /// is returned unless stated otherwise. + /// + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn instantiate( + code_hash: &[u8; 32], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8; 32]>, + value: &[u8; 32], + input: &[u8], + address: Option<&mut [u8; 20]>, + output: Option<&mut &mut [u8]>, + salt: Option<&[u8; 32]>, + ) -> Result; + + /// Checks whether a specified address belongs to a contract. + /// + /// # Parameters + /// + /// - `address`: The address to check + /// + /// # Return + /// + /// Returns `true` if the address belongs to a contract. + fn is_contract(address: &[u8; 20]) -> bool; + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the minimum balance. + 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; 32]); + + /// Load the latest block timestamp into the supplied buffer + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the timestamp. + fn now(output: &mut [u8; 32]); + + /// Removes the delegate dependency from the contract. + /// + /// Traps if the delegate dependency does not exist. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps + /// otherwise. + fn unlock_delegate_dependency(code_hash: &[u8; 32]); + + /// Cease contract execution and save a data buffer as a result of the execution. + /// + /// This function never returns as it stops execution of the caller. + /// This is the only way to return a data buffer to the caller. Returning from + /// execution without calling this function is equivalent to calling: + /// ```nocompile + /// return_value(ReturnFlags::empty(), &[]) + /// ``` + /// + /// Using an unnamed non empty `ReturnFlags` triggers a trap. + /// + /// # Parameters + /// + /// - `flags`: Flag used to signal special return conditions to the supervisor. See + /// [`ReturnFlags`] for a documentation of the supported flags. + /// - `return_value`: The return value buffer. + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; + + /// Replace the contract code at the specified address with new code. + /// + /// # Note + /// + /// There are a couple of important considerations which must be taken into account when + /// using this API: + /// + /// 1. The storage at the code address will remain untouched. This means that contract + /// developers must ensure that the storage layout of the new code is compatible with that of + /// the old code. + /// + /// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another + /// way, when using this API you lose the guarantee that an address always identifies a specific + /// code hash. + /// + /// 3. If a contract calls into itself after changing its code the new call would use + /// the new code. However, if the original caller panics after returning from the sub call it + /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next + /// caller would use the old code. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps + /// otherwise. + /// + /// # Errors + /// + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] + fn set_code_hash(code_hash: &[u8; 32]) -> Result; + + /// Set the value at the given key in the contract storage. + /// + /// The key and value lengths must not exceed the maximums defined by the contracts module + /// parameters. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// - `encoded_value`: The storage value. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn set_storage(flags: StorageFlags, key: &[u8], value: &[u8]) -> Option; + + /// Verify a sr25519 signature + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message`: The message bytes. + /// + /// # Errors + /// + /// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed] + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; + + /// Retrieve and remove the value under the given key from storage. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] + fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Transfer some amount of funds into the specified account. + /// + /// # Parameters + /// + /// - `address`: The address of the account to transfer funds to. + /// - `value`: The U256 value to transfer. + /// + /// # Errors + /// + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result; + + /// Remove the calling account and transfer remaining **free** balance. + /// + /// This function never returns. Either the termination was successful and the + /// execution of the destroyed contract is halted. Or it failed during the termination + /// which is considered fatal and results in a trap + rollback. + /// + /// # Parameters + /// + /// - `beneficiary`: The address of the beneficiary account + /// + /// # 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; 20]) -> !; + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the transferred value. + fn value_transferred(output: &mut [u8; 32]); + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// + /// # 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 [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 + /// the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// - `output`: A reference to the output data buffer to write the [Outcome](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/enum.Outcome.html) + /// + /// # Return + /// + /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM + /// execution fails, `ReturnCode::XcmExecutionFailed` is returned + fn xcm_execute(msg: &[u8]) -> Result; + + /// Send an XCM program from the contract to the specified destination. + /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that + /// the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `dest`: The XCM destination, should be decodable as [VersionedLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedLocation.html), + /// traps otherwise. + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// + /// # Return + /// + /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM + /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result; +} + +mod private { + pub trait Sealed {} + impl Sealed for super::HostFnImpl {} +} diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs new file mode 100644 index 0000000000000000000000000000000000000000..0bb0ede4543bddb7c8856c9a76c2369ea02355f3 --- /dev/null +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -0,0 +1,550 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused_variables)] + +use crate::{ + host::{CallFlags, HostFn, HostFnImpl, Result, StorageFlags}, + ReturnFlags, +}; + +mod sys { + use crate::ReturnCode; + + #[polkavm_derive::polkavm_define_abi] + mod abi {} + + impl abi::FromHost for ReturnCode { + type Regs = (u32,); + + fn from_host((a0,): Self::Regs) -> Self { + ReturnCode(a0) + } + } + + #[polkavm_derive::polkavm_import(abi = self::abi)] + extern "C" { + pub fn set_storage( + flags: u32, + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> ReturnCode; + pub fn clear_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode; + pub fn get_storage( + flags: u32, + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn contains_storage(flags: u32, key_ptr: *const u8, key_len: u32) -> ReturnCode; + pub fn take_storage( + flags: u32, + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn transfer(address_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; + pub fn call(ptr: *const u8) -> ReturnCode; + pub fn delegate_call( + flags: u32, + code_hash_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn instantiate(ptr: *const u8) -> ReturnCode; + pub fn terminate(beneficiary_ptr: *const u8); + pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); + pub fn caller(out_ptr: *mut u8); + pub fn is_contract(account_ptr: *const u8) -> ReturnCode; + 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); + pub fn weight_to_fee(ref_time: u64, proof_size: u64, out_ptr: *mut u8); + pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn balance(out_ptr: *mut u8); + pub fn balance_of(addr_ptr: *const u8, out_ptr: *mut u8); + pub fn value_transferred(out_ptr: *mut u8); + pub fn now(out_ptr: *mut u8); + pub fn minimum_balance(out_ptr: *mut u8); + pub fn deposit_event( + topics_ptr: *const [u8; 32], + num_topic: u32, + data_ptr: *const u8, + data_len: 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); + pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + pub fn call_chain_extension( + id: u32, + input_ptr: *const u8, + input_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode; + pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; + pub fn ecdsa_recover( + signature_ptr: *const u8, + message_hash_ptr: *const u8, + out_ptr: *mut u8, + ) -> ReturnCode; + pub fn sr25519_verify( + signature_ptr: *const u8, + pub_key_ptr: *const u8, + message_len: u32, + message_ptr: *const u8, + ) -> ReturnCode; + pub fn set_code_hash(code_hash_ptr: *const u8) -> ReturnCode; + pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode; + pub fn instantiation_nonce() -> u64; + pub fn lock_delegate_dependency(code_hash_ptr: *const u8); + pub fn unlock_delegate_dependency(code_hash_ptr: *const u8); + pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32) -> ReturnCode; + pub fn xcm_send( + dest_ptr: *const u8, + dest_len: *const u8, + msg_ptr: *const u8, + msg_len: u32, + out_ptr: *mut u8, + ) -> ReturnCode; + } +} + +/// A macro to implement all Host functions with a signature of `fn(&mut [u8; n])`. +macro_rules! impl_wrapper_for { + (@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 { + ( $name:ident, $bytes_result:literal ) => { + paste::item! { + fn [](input: &[u8], output: &mut [u8; $bytes_result]) { + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } + } + } + }; +} + +#[inline(always)] +fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { + debug_assert!(new_len <= output.len()); + let tmp = core::mem::take(output); + *output = &mut tmp[..new_len]; +} + +#[inline(always)] +fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) { + match data { + Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), + None => (crate::SENTINEL as _, 0), + } +} + +#[inline(always)] +fn ptr_or_sentinel(data: &Option<&[u8; 32]>) -> *const u8 { + match data { + Some(ref data) => data.as_ptr(), + None => crate::SENTINEL as _, + } +} + +impl HostFn for HostFnImpl { + fn instantiate( + code_hash: &[u8; 32], + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: Option<&[u8; 32]>, + value: &[u8; 32], + input: &[u8], + mut address: Option<&mut [u8; 20]>, + mut output: Option<&mut &mut [u8]>, + salt: Option<&[u8; 32]>, + ) -> Result { + 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 { + code_hash: *const u8, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: *const u8, + value: *const u8, + input: *const u8, + input_len: u32, + address: *const u8, + output: *mut u8, + output_len: *mut u32, + salt: *const u8, + } + let args = Args { + code_hash: code_hash.as_ptr(), + ref_time_limit, + proof_size_limit, + deposit_limit: deposit_limit_ptr, + value: value.as_ptr(), + input: input.as_ptr(), + input_len: input.len() as _, + address, + output: output_ptr, + output_len: &mut output_len as *mut _, + salt: salt_ptr, + }; + + let ret_code = { unsafe { sys::instantiate(&args as *const Args as *const _) } }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn call( + flags: CallFlags, + callee: &[u8; 20], + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: Option<&[u8; 32]>, + value: &[u8; 32], + input: &[u8], + mut output: Option<&mut &mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + #[repr(packed)] + #[allow(dead_code)] + struct Args { + flags: u32, + callee: *const u8, + ref_time_limit: u64, + proof_size_limit: u64, + deposit_limit: *const u8, + value: *const u8, + input: *const u8, + input_len: u32, + output: *mut u8, + output_len: *mut u32, + } + let args = Args { + flags: flags.bits(), + callee: callee.as_ptr(), + ref_time_limit, + proof_size_limit, + deposit_limit: deposit_limit_ptr, + value: value.as_ptr(), + input: input.as_ptr(), + input_len: input.len() as _, + output: output_ptr, + output_len: &mut output_len as *mut _, + }; + + let ret_code = { unsafe { sys::call(&args as *const Args as *const _) } }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn caller_is_root() -> u32 { + unsafe { sys::caller_is_root() }.into_u32() + } + + fn delegate_call( + flags: CallFlags, + code_hash: &[u8; 32], + input: &[u8], + mut output: Option<&mut &mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let ret_code = { + unsafe { + sys::delegate_call( + flags.bits(), + code_hash.as_ptr(), + input.as_ptr(), + input.len() as u32, + output_ptr, + &mut output_len, + ) + } + }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn transfer(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; 32]], data: &[u8]) { + unsafe { + sys::deposit_event( + topics.as_ptr(), + topics.len() as u32, + data.as_ptr(), + data.len() as u32, + ) + } + } + + fn set_storage(flags: StorageFlags, key: &[u8], encoded_value: &[u8]) -> Option { + let ret_code = unsafe { + sys::set_storage( + flags.bits(), + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; + ret_code.into() + } + + fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option { + let ret_code = unsafe { sys::clear_storage(flags.bits(), key.as_ptr(), key.len() as u32) }; + ret_code.into() + } + + fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option { + let ret_code = + unsafe { sys::contains_storage(flags.bits(), key.as_ptr(), key.len() as u32) }; + ret_code.into() + } + + fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_code = { + unsafe { + sys::get_storage( + flags.bits(), + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + + fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_code = { + unsafe { + sys::take_storage( + flags.bits(), + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + + fn debug_message(str: &[u8]) -> Result { + let ret_code = unsafe { sys::debug_message(str.as_ptr(), str.len() as u32) }; + ret_code.into() + } + + fn terminate(beneficiary: &[u8; 20]) -> ! { + unsafe { sys::terminate(beneficiary.as_ptr()) } + panic!("terminate does not return"); + } + + fn call_chain_extension(func_id: u32, input: &[u8], mut output: Option<&mut &mut [u8]>) -> u32 { + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); + let ret_code = { + unsafe { + sys::call_chain_extension( + func_id, + input.as_ptr(), + input.len() as u32, + output_ptr, + &mut output_len, + ) + } + }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into_u32() + } + + fn input(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { sys::input(output.as_mut_ptr(), &mut output_len) }; + } + extract_from_slice(output, output_len as usize); + } + + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { + unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) } + panic!("seal_return does not return"); + } + + fn call_runtime(call: &[u8]) -> Result { + let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; + ret_code.into() + } + + impl_wrapper_for! { + [u8; 32] => block_number, balance, value_transferred, now, minimum_balance; + [u8; 20] => address, caller; + } + + fn weight_left(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + 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); + impl_hash_fn!(keccak_256, 32); + impl_hash_fn!(blake2_256, 32); + impl_hash_fn!(blake2_128, 16); + + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result { + let ret_code = unsafe { + sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) + }; + ret_code.into() + } + + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { + let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; + ret_code.into() + } + + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { + let ret_code = unsafe { + sys::sr25519_verify( + signature.as_ptr(), + pub_key.as_ptr(), + message.len() as u32, + message.as_ptr(), + ) + }; + ret_code.into() + } + + fn is_contract(address: &[u8; 20]) -> bool { + let ret_val = unsafe { sys::is_contract(address.as_ptr()) }; + ret_val.into_bool() + } + + fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) { + unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) }; + } + + fn caller_is_origin() -> bool { + let ret_val = unsafe { sys::caller_is_origin() }; + ret_val.into_bool() + } + + 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(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; 32]) { + unsafe { sys::own_code_hash(output.as_mut_ptr()) } + } + + fn lock_delegate_dependency(code_hash: &[u8; 32]) { + unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) } + } + + fn unlock_delegate_dependency(code_hash: &[u8; 32]) { + unsafe { sys::unlock_delegate_dependency(code_hash.as_ptr()) } + } + + fn xcm_execute(msg: &[u8]) -> Result { + let ret_code = unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _) }; + ret_code.into() + } + + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { + let ret_code = unsafe { + sys::xcm_send( + dest.as_ptr(), + dest.len() as _, + msg.as_ptr(), + msg.len() as _, + output.as_mut_ptr(), + ) + }; + ret_code.into() + } +} diff --git a/substrate/frame/revive/uapi/src/lib.rs b/substrate/frame/revive/uapi/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..e660ce36ef75eb846155a3dd08db28f3f980f697 --- /dev/null +++ b/substrate/frame/revive/uapi/src/lib.rs @@ -0,0 +1,131 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! External C API to communicate with substrate contracts runtime module. +//! +//! Refer to substrate FRAME contract module for more documentation. + +#![no_std] + +mod flags; +pub use flags::*; +mod host; + +pub use host::{HostFn, HostFnImpl}; + +macro_rules! define_error_codes { + ( + $( + $( #[$attr:meta] )* + $name:ident = $discr:literal, + )* + ) => { + /// Every error that can be returned to a contract when it calls any of the host functions. + #[derive(Debug, PartialEq, Eq)] + #[repr(u32)] + pub enum ReturnErrorCode { + /// API call successful. + Success = 0, + $( + $( #[$attr] )* + $name = $discr, + )* + /// Returns if an unknown error was received from the host module. + Unknown, + } + + impl From for Result { + fn from(return_code: ReturnCode) -> Self { + match return_code.0 { + 0 => Ok(()), + $( + $discr => Err(ReturnErrorCode::$name), + )* + _ => Err(ReturnErrorCode::Unknown), + } + } + } + }; +} + +impl From for u32 { + fn from(code: ReturnErrorCode) -> u32 { + code as u32 + } +} + +define_error_codes! { + /// The called function trapped and has its state changes reverted. + /// In this case no output buffer is returned. + /// Can only be returned from `call` and `instantiate`. + CalleeTrapped = 1, + /// The called function ran to completion but decided to revert its state. + /// An output buffer is returned when one was supplied. + /// Can only be returned from `call` and `instantiate`. + CalleeReverted = 2, + /// The passed key does not exist in storage. + KeyNotFound = 3, + /// Transfer failed for other not further specified reason. Most probably + /// reserved or locked balance of the sender that was preventing the transfer. + TransferFailed = 4, + /// No code could be found at the supplied code hash. + CodeNotFound = 5, + /// The account that was called is no contract. + NotCallable = 6, + /// The call to `debug_message` had no effect because debug message + /// recording was disabled. + LoggingDisabled = 7, + /// The call dispatched by `call_runtime` was executed but returned an error. + CallRuntimeFailed = 8, + /// ECDSA public key recovery failed. Most probably wrong recovery id or signature. + EcdsaRecoveryFailed = 9, + /// sr25519 signature verification failed. + Sr25519VerifyFailed = 10, + /// The `xcm_execute` call failed. + XcmExecutionFailed = 11, + /// The `xcm_send` call failed. + XcmSendFailed = 12, +} + +/// The raw return code returned by the host side. +#[repr(transparent)] +pub struct ReturnCode(u32); + +/// Used as a sentinel value when reading and writing contract memory. +/// +/// We use this value to signal `None` to a contract when only a primitive is +/// allowed and we don't want to go through encoding a full Rust type. +/// Using `u32::Max` is a safe sentinel because contracts are never +/// allowed to use such a large amount of resources. So this value doesn't +/// make sense for a memory location or length. +const SENTINEL: u32 = u32::MAX; + +impl From for Option { + fn from(code: ReturnCode) -> Self { + (code.0 < SENTINEL).then_some(code.0) + } +} + +impl ReturnCode { + /// Returns the raw underlying `u32` representation. + pub fn into_u32(self) -> u32 { + self.0 + } + /// Returns the underlying `u32` converted into `bool`. + pub fn into_bool(self) -> bool { + self.0.ne(&0) + } +} + +type Result = core::result::Result<(), ReturnErrorCode>; diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index 235d75a4b7520e18eeacdad49f466146fc11a5b1..e5fb15cdd07cb2657e599069f803a6cf5a4c50db 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -177,8 +177,15 @@ pub mod runtime { pub use frame_executive::*; /// Macro to amalgamate the runtime into `struct Runtime`. + /// + /// Consider using the new version of this [`frame_construct_runtime`]. pub use frame_support::construct_runtime; + /// Macro to amalgamate the runtime into `struct Runtime`. + /// + /// This is the newer version of [`construct_runtime`]. + pub use frame_support::runtime as frame_construct_runtime; + /// Macro to easily derive the `Config` trait of various pallet for `Runtime`. pub use frame_support::derive_impl; @@ -186,12 +193,18 @@ pub mod runtime { // TODO: using linking in the Get in the line above triggers an ICE :/ pub use frame_support::{ord_parameter_types, parameter_types}; + /// For building genesis config. + pub use frame_support::genesis_builder_helper::{build_state, get_preset}; + /// Const types that can easily be used in conjuncture with `Get`. pub use frame_support::traits::{ ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, }; + /// Used for simple fee calculation. + pub use frame_support::weights::{self, FixedFee, NoFee}; + /// Primary types used to parameterize `EnsureOrigin` and `EnsureRootWithArg`. pub use frame_system::{ EnsureNever, EnsureNone, EnsureRoot, EnsureRootWithSuccess, EnsureSigned, @@ -201,11 +214,16 @@ pub mod runtime { /// Types to define your runtime version. pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion}; + #[cfg(feature = "std")] + pub use sp_version::NativeVersion; + /// Macro to implement runtime APIs. pub use sp_api::impl_runtime_apis; - #[cfg(feature = "std")] - pub use sp_version::NativeVersion; + // Types often used in the runtime APIs. + pub use sp_core::OpaqueMetadata; + pub use sp_inherents::{CheckInherentsResult, InherentData}; + pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; } /// Types and traits for runtimes that implement runtime APIs. @@ -223,11 +241,6 @@ pub mod runtime { // moved to file similarly. #[allow(ambiguous_glob_reexports)] pub mod apis { - // Types often used in the runtime APIs. - pub use sp_core::OpaqueMetadata; - pub use sp_inherents::{CheckInherentsResult, InherentData}; - pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; - pub use frame_system_rpc_runtime_api::*; pub use sp_api::{self, *}; pub use sp_block_builder::*; diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 79f9d298ada788ff7ee4278bfb4290eaf44e3c10..8a4482f52ad5e1a05b9d1c2ef3155c7bc0e7eeb3 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -1027,7 +1027,8 @@ pub mod pallet { 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 value = value.min(stash_balance); 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 cbefd96017017154b10375e8fc556518bf24ad6d..54eb290ca6cf3321729a3f9bba6d10a69e9a547a 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 associated_types = expand_pallet_metadata_associated_types(runtime, decl); @@ -59,7 +59,7 @@ pub fn expand_runtime_metadata( #attr } }); - + let deprecation_info = expand_pallet_metadata_deprecation(runtime, decl); quote! { #attr #scrate::__private::metadata_ir::PalletMetadataIR { @@ -72,6 +72,7 @@ pub fn expand_runtime_metadata( error: #errors, docs: #docs, associated_types: #associated_types, + deprecation_info: #deprecation_info, } } }) @@ -202,7 +203,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") { @@ -222,9 +222,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 { @@ -232,6 +230,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..3e38adcc3c59eed6b64dd4d747d33a73414400e7 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, /// The doc literals pub docs: Vec, + /// attributes + pub attrs: Vec, } impl Pallet { @@ -650,7 +652,7 @@ enum PalletsConversion { /// incrementally from last explicit or 0. fn convert_pallets(pallets: Vec) -> 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 +680,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result) -> syn::Result) -> syn::Result (), @@ -738,7 +740,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result) -> syn::Result>>()?; diff --git a/substrate/frame/support/procedural/src/deprecation.rs b/substrate/frame/support/procedural/src/deprecation.rs new file mode 100644 index 0000000000000000000000000000000000000000..bdf996d21229f6d4f81213d3e522b89cfcd8d869 --- /dev/null +++ b/substrate/frame/support/procedural/src/deprecation.rs @@ -0,0 +1,181 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 proc_macro2::TokenStream; +use quote::quote; +use syn::{ + punctuated::Punctuated, spanned::Spanned, Error, Expr, ExprLit, Lit, Meta, MetaNameValue, + Result, Token, Variant, +}; + +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_::__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/lib.rs b/substrate/frame/support/procedural/src/lib.rs index 05f6333cd275eead826046c58dfd97962f95ec45..1d0f5d2a69e2f59769ec3ed56519d04d0e8b43f7 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; @@ -812,7 +813,7 @@ pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { `RuntimeTask`, `RuntimeOrigin`, `RuntimeParameters` or `PalletInfo`", ) .to_compile_error() - .into() + .into(); } tokens } @@ -1230,7 +1231,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/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..346dff46f12e83772af13a89766cc6a340f8f0a7 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,7 +395,7 @@ 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)); }, } @@ -409,18 +406,18 @@ impl CallDef { 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)) + 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)) + 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 +430,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 +444,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 +460,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 2f8a0923b6c52b9e8a6545a8cd884dc9d195f252..713c58f201f82a8f9dcd595d3a776eedde512d4d 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/config.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/config.rs @@ -101,6 +101,8 @@ pub struct ConstMetadataDef { pub type_: syn::Type, /// The doc associated pub doc: Vec, + /// attributes + pub attrs: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { @@ -123,22 +125,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() }) } } @@ -245,7 +247,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 @@ -253,7 +255,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 @@ -267,7 +269,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_ @@ -279,7 +281,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) { @@ -287,7 +289,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) @@ -299,7 +301,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 = @@ -372,12 +374,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())?; @@ -392,7 +394,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() { @@ -436,7 +438,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)?); @@ -480,13 +482,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; @@ -497,13 +499,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; }, @@ -559,7 +561,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 6f5a599b08ee7ed8b2ef6c1850c0d8ac103b80a0..5a7ccf0e5c6496d2365b2ad5310134fc8d1899f8 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -77,7 +77,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 @@ -223,7 +222,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)?; @@ -286,7 +285,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 { }, _ => (), @@ -315,7 +314,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; @@ -338,7 +337,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, @@ -346,7 +345,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; @@ -409,7 +408,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" @@ -788,7 +787,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/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..de1efa267c899d133d3b881bcb1e3b616bf4780b 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", - )) + )); }; } @@ -91,6 +91,7 @@ impl Pallet { cfg_pattern, pallet_parts, docs, + attrs: item.attrs.clone(), }) } } diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 2d882dd224f523c9d810e31e18e499cf69730dbb..232e52b267be8bff733ec5781d3fe9acc86811fa 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 @@ -1943,6 +1949,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. @@ -1984,6 +1999,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; /// Selectively includes associated types in the metadata. @@ -2115,6 +2139,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`. @@ -2247,6 +2277,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. @@ -2465,6 +2501,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::{ diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index 7f74614695640dfd07771f689bb93971199baf10..0eabf9d0ee162a5f4bc887119a6c9923aa8e7f1a 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -673,7 +673,8 @@ pub trait SteppedMigrations { /// The `n`th [`SteppedMigration::id`]. /// - /// Is guaranteed to return `Some` if `n < Self::len()`. + /// Is guaranteed to return `Some` if `n < Self::len()`. Calling this with any index larger or + /// equal to `Self::len()` MUST return `None`. fn nth_id(n: u32) -> Option>; /// The [`SteppedMigration::max_steps`] of the `n`th migration. @@ -777,8 +778,10 @@ impl SteppedMigrations for T { 1 } - fn nth_id(_n: u32) -> Option> { - Some(T::id().encode()) + fn nth_id(n: u32) -> Option> { + n.is_zero() + .then_some(T::id().encode()) + .defensive_proof("nth_id should only be called with n==0") } fn nth_max_steps(n: u32) -> Option> { diff --git a/substrate/frame/support/src/storage/generator/map.rs b/substrate/frame/support/src/storage/generator/map.rs index 7a74308fdeac16ac208f67b5208dadd7406e29fa..e905df41a5a66646e816693b24c3eae5f5d5ce88 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); }, }; 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..c70d9de54467a5f421197bf3620226dfb5925161 100644 --- a/substrate/frame/support/src/storage/types/double_map.rs +++ b/substrate/frame/support/src/storage/types/double_map.rs @@ -732,7 +732,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 +749,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -985,8 +990,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 +1016,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -1017,6 +1031,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..9ee012f8628645b8292986848691418b55381ca4 100755 --- a/substrate/frame/support/src/storage/types/nmap.rs +++ b/substrate/frame/support/src/storage/types/nmap.rs @@ -583,7 +583,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 +600,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -820,8 +825,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 +848,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -846,6 +860,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); @@ -1020,8 +1035,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 +1061,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1052,6 +1076,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); @@ -1261,8 +1286,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 +1313,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1295,6 +1329,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..5e1bcc777df4ff3bb364721b87990439fd88e0b9 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; } diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index a423656c394f28158da2aedd5d552814ccabb3c5..6e59ff9d030be6a494b85836e524c7a81bb3cee7 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -92,6 +92,8 @@ pub use hooks::{ pub mod schedule; mod storage; +#[cfg(feature = "experimental")] +pub use storage::MaybeConsideration; pub use storage::{ Consideration, Footprint, Incrementable, Instance, LinearStoragePrice, PartialStorageInfoTrait, StorageInfo, StorageInfoTrait, StorageInstance, TrackedStorageKey, WhitelistedStorageKeys, @@ -130,6 +132,9 @@ pub mod dynamic_params; pub mod tasks; pub use tasks::Task; +mod proving; +pub use proving::*; + #[cfg(feature = "try-runtime")] mod try_runtime; #[cfg(feature = "try-runtime")] diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index 7c8c22d1ae5a3f49906b71890758608857332534..492475d6f63c66fc4ac55eb9a67e8ec534db3df5 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -48,6 +48,10 @@ impl VariantCount for () { const VARIANT_COUNT: u32 = 0; } +impl VariantCount for u8 { + const VARIANT_COUNT: u32 = 256; +} + /// Adapter for `Get` to access `VARIANT_COUNT` from `trait pub trait VariantCount {`. pub struct VariantCountOf(core::marker::PhantomData); impl Get for VariantCountOf { diff --git a/substrate/frame/support/src/traits/proving.rs b/substrate/frame/support/src/traits/proving.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc44f4cd68e7ad4de12895e9cbebc3a4ca6890f4 --- /dev/null +++ b/substrate/frame/support/src/traits/proving.rs @@ -0,0 +1,135 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides functionality for verifying proofs. + +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use sp_core::Hasher; + +/// Something that can verify the existence of some data in a given proof. +pub trait VerifyExistenceProof { + /// The proof type. + type Proof; + /// The hash type. + type Hash; + + /// Verify the given `proof`. + /// + /// Ensures that the `proof` was build for `root` and returns the proved data. + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, ()>; +} + +/// Implements [`VerifyExistenceProof`] using a binary merkle tree. +pub struct BinaryMerkleTreeProver(core::marker::PhantomData); + +impl VerifyExistenceProof for BinaryMerkleTreeProver +where + H::Out: Decode + Encode, +{ + type Proof = binary_merkle_tree::MerkleProof>; + type Hash = H::Out; + + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, ()> { + if proof.root != *root { + return Err(()); + } + + if binary_merkle_tree::verify_proof::( + &proof.root, + proof.proof, + proof.number_of_leaves, + proof.leaf_index, + &proof.leaf, + ) { + Ok(proof.leaf) + } else { + Err(()) + } + } +} + +/// Proof used by [`SixteenPatriciaMerkleTreeProver`] for [`VerifyExistenceProof`]. +#[derive(Encode, Decode)] +pub struct SixteenPatriciaMerkleTreeExistenceProof { + /// The key of the value to prove. + pub key: Vec, + /// The value for that the existence is proved. + pub value: Vec, + /// The encoded nodes to prove the existence of the data under `key`. + pub proof: Vec>, +} + +/// Implements [`VerifyExistenceProof`] using a 16-patricia merkle tree. +pub struct SixteenPatriciaMerkleTreeProver(core::marker::PhantomData); + +impl VerifyExistenceProof for SixteenPatriciaMerkleTreeProver { + type Proof = SixteenPatriciaMerkleTreeExistenceProof; + type Hash = H::Out; + + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, ()> { + sp_trie::verify_trie_proof::, _, _, _>( + &root, + &proof.proof, + [&(&proof.key, Some(&proof.value))], + ) + .map_err(drop) + .map(|_| proof.value) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_runtime::{proving_trie::BasicProvingTrie, traits::BlakeTwo256}; + + #[test] + fn verify_binary_merkle_tree_prover_works() { + let proof = binary_merkle_tree::merkle_proof::( + vec![b"hey".encode(), b"yes".encode()], + 1, + ); + let root = proof.root; + + assert_eq!( + BinaryMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + b"yes".encode() + ); + } + + #[test] + fn verify_sixteen_patricia_merkle_tree_prover_works() { + let trie = BasicProvingTrie::::generate_for(vec![ + (0u32, &b"hey"[..]), + (1u32, &b"yes"[..]), + ]) + .unwrap(); + let proof = trie.create_single_value_proof(1u32).unwrap(); + let root = *trie.root(); + + let proof = SixteenPatriciaMerkleTreeExistenceProof { + key: 1u32.encode(), + value: b"yes"[..].encode(), + proof, + }; + + assert_eq!( + SixteenPatriciaMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + b"yes"[..].encode() + ); + } +} diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index a954af14d259bc49310913e151dfe8c26180dedc..2b8e437073894cc03634bbd950313f541b59fe76 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -201,7 +201,7 @@ where } /// Some sort of cost taken from account temporarily in order to offset the cost to the chain of -/// holding some data `Footprint` (e.g. [`Footprint`]) in state. +/// holding some data [`Footprint`] in state. /// /// The cost may be increased, reduced or dropped entirely as the footprint changes. /// @@ -217,16 +217,14 @@ pub trait Consideration: Member + FullCodec + TypeInfo + MaxEncodedLen { /// Create a ticket for the `new` footprint attributable to `who`. This ticket *must* ultimately - /// be consumed through `update` or `drop` once the footprint changes or is removed. `None` - /// implies no cost for a given footprint. - fn new(who: &AccountId, new: Footprint) -> Result, DispatchError>; + /// be consumed through `update` or `drop` once the footprint changes or is removed. + fn new(who: &AccountId, new: Footprint) -> Result; /// Optionally consume an old ticket and alter the footprint, enforcing the new cost to `who` - /// and returning the new ticket (or an error if there was an issue). `None` implies no cost for - /// a given footprint. + /// and returning the new ticket (or an error if there was an issue). /// /// For creating tickets and dropping them, you can use the simpler `new` and `drop` instead. - fn update(self, who: &AccountId, new: Footprint) -> Result, DispatchError>; + fn update(self, who: &AccountId, new: Footprint) -> Result; /// Consume a ticket for some `old` footprint attributable to `who` which should now been freed. fn drop(self, who: &AccountId) -> Result<(), DispatchError>; @@ -239,18 +237,42 @@ pub trait Consideration: fn burn(self, _: &AccountId) { let _ = self; } + /// Ensure that creating a ticket for a given account and footprint will be successful if done + /// immediately after this call. + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &AccountId, new: Footprint); } impl Consideration for () { - fn new(_: &A, _: F) -> Result, DispatchError> { - Ok(Some(())) + fn new(_: &A, _: F) -> Result { + Ok(()) } - fn update(self, _: &A, _: F) -> Result, DispatchError> { - Ok(Some(())) + fn update(self, _: &A, _: F) -> Result<(), DispatchError> { + Ok(()) } fn drop(self, _: &A) -> Result<(), DispatchError> { Ok(()) } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &A, _: F) {} +} + +#[cfg(feature = "experimental")] +/// An extension of the [`Consideration`] trait that allows for the management of tickets that may +/// represent no cost. While the [`MaybeConsideration`] still requires proper handling, it +/// introduces the ability to determine if a ticket represents no cost and can be safely forgotten +/// without any side effects. +pub trait MaybeConsideration: Consideration { + /// Returns `true` if this [`Consideration`] represents a no-cost ticket and can be forgotten + /// without any side effects. + fn is_none(&self) -> bool; +} + +#[cfg(feature = "experimental")] +impl MaybeConsideration for () { + fn is_none(&self) -> bool { + true + } } macro_rules! impl_incrementable { diff --git a/substrate/frame/support/src/traits/tokens/fungible/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/mod.rs index f40e494b930d5f836c47b2700d4ff58b336c7fea..c67755e133bf40764fd5970539659da6a8264aea 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/mod.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/mod.rs @@ -164,6 +164,8 @@ use codec::{Decode, Encode, MaxEncodedLen}; use core::marker::PhantomData; use frame_support_procedural::{CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::TypeInfo; +#[cfg(feature = "runtime-benchmarks")] +use sp_runtime::Saturating; use super::{ Fortitude::{Force, Polite}, @@ -184,6 +186,8 @@ use sp_core::Get; use sp_runtime::{traits::Convert, DispatchError}; pub use union_of::{NativeFromLeft, NativeOrWithId, UnionOf}; +#[cfg(feature = "experimental")] +use crate::traits::MaybeConsideration; use crate::{ ensure, traits::{Consideration, Footprint}, @@ -209,38 +213,49 @@ pub struct FreezeConsideration(F::Balance, PhantomData ( where F: MutateFreeze; impl< - A: 'static, - F: 'static + MutateFreeze, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateFreeze, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateFreeze + Mutate, R: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for FreezeConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - F::increase_frozen(&R::get(), who, new)?; - Ok(Some(Self(new, PhantomData))) - } + F::increase_frozen(&R::get(), who, new)?; + Ok(Self(new, PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { + fn update(self, who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); if self.0 > new { F::decrease_frozen(&R::get(), who, self.0 - new)?; } else if new > self.0 { F::increase_frozen(&R::get(), who, new - self.0)?; } - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(new, PhantomData))) - } + Ok(Self(new, PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { F::decrease_frozen(&R::get(), who, self.0).map(|_| ()) } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); + } +} +#[cfg(feature = "experimental")] +impl< + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateFreeze, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateFreeze + Mutate, + R: 'static + Get, + D: 'static + Convert, + Fp: 'static, + > MaybeConsideration for FreezeConsideration +{ + fn is_none(&self) -> bool { + self.0.is_zero() + } } /// Consideration method using a `fungible` balance frozen as the cost exacted for the footprint. @@ -263,34 +278,27 @@ pub struct HoldConsideration( where F: MutateHold; impl< - A: 'static, - F: 'static + MutateHold, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateHold, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateHold + Mutate, R: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for HoldConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - F::hold(&R::get(), who, new)?; - Ok(Some(Self(new, PhantomData))) - } + F::hold(&R::get(), who, new)?; + Ok(Self(new, PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { + fn update(self, who: &A, footprint: Fp) -> Result { let new = D::convert(footprint); if self.0 > new { F::release(&R::get(), who, self.0 - new, BestEffort)?; } else if new > self.0 { F::hold(&R::get(), who, new - self.0)?; } - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(new, PhantomData))) - } + Ok(Self(new, PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { F::release(&R::get(), who, self.0, BestEffort).map(|_| ()) @@ -298,6 +306,24 @@ impl< fn burn(self, who: &A) { let _ = F::burn_held(&R::get(), who, self.0, BestEffort, Force); } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); + } +} +#[cfg(feature = "experimental")] +impl< + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateHold, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateHold + Mutate, + R: 'static + Get, + D: 'static + Convert, + Fp: 'static, + > MaybeConsideration for HoldConsideration +{ + fn is_none(&self) -> bool { + self.0.is_zero() + } } /// Basic consideration method using a `fungible` balance frozen as the cost exacted for the @@ -321,34 +347,28 @@ impl< #[codec(mel_bound())] pub struct LoneFreezeConsideration(PhantomData (A, Fx, Rx, D, Fp)>); impl< - A: 'static, - Fx: 'static + MutateFreeze, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] Fx: 'static + MutateFreeze, + #[cfg(feature = "runtime-benchmarks")] Fx: 'static + MutateFreeze + Mutate, Rx: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for LoneFreezeConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { ensure!(Fx::balance_frozen(&Rx::get(), who).is_zero(), DispatchError::Unavailable); - let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - Fx::set_frozen(&Rx::get(), who, new, Polite).map(|_| Some(Self(PhantomData))) - } + Fx::set_frozen(&Rx::get(), who, D::convert(footprint), Polite).map(|_| Self(PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { - let new = D::convert(footprint); - let _ = Fx::set_frozen(&Rx::get(), who, new, Polite)?; - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(PhantomData))) - } + fn update(self, who: &A, footprint: Fp) -> Result { + Fx::set_frozen(&Rx::get(), who, D::convert(footprint), Polite).map(|_| Self(PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { Fx::thaw(&Rx::get(), who).map(|_| ()) } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = Fx::mint_into(who, Fx::minimum_balance().saturating_add(D::convert(fp))); + } } /// Basic consideration method using a `fungible` balance placed on hold as the cost exacted for the @@ -372,30 +392,20 @@ impl< #[codec(mel_bound())] pub struct LoneHoldConsideration(PhantomData (A, Fx, Rx, D, Fp)>); impl< - A: 'static, - F: 'static + MutateHold, + A: 'static + Eq, + #[cfg(not(feature = "runtime-benchmarks"))] F: 'static + MutateHold, + #[cfg(feature = "runtime-benchmarks")] F: 'static + MutateHold + Mutate, R: 'static + Get, D: 'static + Convert, Fp: 'static, > Consideration for LoneHoldConsideration { - fn new(who: &A, footprint: Fp) -> Result, DispatchError> { + fn new(who: &A, footprint: Fp) -> Result { ensure!(F::balance_on_hold(&R::get(), who).is_zero(), DispatchError::Unavailable); - let new = D::convert(footprint); - if new.is_zero() { - Ok(None) - } else { - F::set_on_hold(&R::get(), who, new).map(|_| Some(Self(PhantomData))) - } + F::set_on_hold(&R::get(), who, D::convert(footprint)).map(|_| Self(PhantomData)) } - fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { - let new = D::convert(footprint); - let _ = F::set_on_hold(&R::get(), who, new)?; - if new.is_zero() { - Ok(None) - } else { - Ok(Some(Self(PhantomData))) - } + fn update(self, who: &A, footprint: Fp) -> Result { + F::set_on_hold(&R::get(), who, D::convert(footprint)).map(|_| Self(PhantomData)) } fn drop(self, who: &A) -> Result<(), DispatchError> { F::release_all(&R::get(), who, BestEffort).map(|_| ()) @@ -403,4 +413,8 @@ impl< fn burn(self, who: &A) { let _ = F::burn_all_held(&R::get(), who, BestEffort, Force); } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(who: &A, fp: Fp) { + let _ = F::mint_into(who, F::minimum_balance().saturating_add(D::convert(fp))); + } } diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index 9fa1df862097f48e9555f27ad097b84e9ee30021..52d3e8c014b317261746371d8ff99acc66f2a517 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -98,7 +98,7 @@ pub enum WithdrawConsequence { /// There has been an overflow in the system. This is indicative of a corrupt state and /// likely unrecoverable. Overflow, - /// Not enough of the funds in the account are unavailable for withdrawal. + /// Not enough of the funds in the account are available for withdrawal. Frozen, /// Account balance would reduce to zero, potentially destroying it. The parameter is the /// amount of balance which is destroyed. 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/construct_runtime_ui/undefined_event_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr index 6f412fe89eab7eec7cf045a786cb178d80fb6591..0f7afb2b9901eb683d690c07495864570572a104 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 @@ -34,3 +34,23 @@ help: consider importing one of these items | 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 items + | +18 + use frame_support_test::Event; + | +18 + use frame_system::Event; + | diff --git a/substrate/frame/support/test/tests/enum_deprecation.rs b/substrate/frame/support/test/tests/enum_deprecation.rs new file mode 100644 index 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..7f1ce0556eabd56c33f0146ffffe6ea4cfaba991 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -14,6 +14,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; use frame_support::{ assert_ok, derive_impl, @@ -212,6 +215,7 @@ pub mod pallet { /// call foo doc comment put in metadata #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(*foo as u64, 0))] + #[deprecated = "test"] pub fn foo( origin: OriginFor, #[pallet::compact] foo: u32, @@ -272,6 +276,7 @@ pub mod pallet { pub enum Error { /// error doc comment put in metadata InsufficientProposersBalance, + #[deprecated = "test"] NonExistentStorageValue, Code(u8), #[codec(skip)] @@ -283,6 +288,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] + #[deprecated = "test"] pub enum Event where T::AccountId: SomeAssociation1 + From, @@ -486,7 +492,7 @@ pub mod pallet { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType5); // Test for where clause if matches!(call, Call::foo_storage_layer { .. }) { - return Ok(ValidTransaction::default()) + return Ok(ValidTransaction::default()); } Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } @@ -553,6 +559,7 @@ pub mod pallet { // Test that a pallet with non generic event and generic genesis_config is correctly handled // and that a pallet with the attribute without_storage_info is correctly handled. #[frame_support::pallet] +#[deprecated = "test"] pub mod pallet2 { use super::{SomeAssociation1, SomeType1, UpdateStorageVersion}; use frame_support::pallet_prelude::*; @@ -2478,3 +2485,56 @@ fn test_error_feature_parsing() { pallet::Error::__Ignore(_, _) => (), } } + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + let example2 = pallets[1].clone(); + { + // Example2 pallet is deprecated + assert_eq!( + &DeprecationStatusIR::Deprecated { note: "test", since: None }, + &example2.deprecation_info + ) + } + { + // Example pallet calls is fully and partially deprecated + let meta = &example.calls.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet errors are partially and fully deprecated + let meta = &example.error.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(2), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + DeprecationInfoIR::ItemDeprecated(DeprecationStatusIR::Deprecated { + note: "test", + since: None + }), + meta.deprecation_info + ); + } + { + // Example2 pallet events are not deprecated + let meta = example2.event.unwrap(); + assert_eq!(DeprecationInfoIR::NotDeprecated, meta.deprecation_info); + } +} diff --git a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs index 326f3530e26ecc75c30420f5e1f4575f381fbf08..81cdd40d1bcf5938fe5195cf3e7ad101ee5bedd6 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_explicit.rs @@ -19,8 +19,6 @@ use frame_support::derive_impl; mod common; -use common::outer_enums::{pallet, pallet2}; - pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; @@ -75,8 +73,10 @@ frame_support::construct_runtime!( } ); +#[cfg(feature = "experimental")] #[test] fn module_error_outer_enum_expand_explicit() { + use common::outer_enums::{pallet, pallet2}; // The Runtime has *all* parts explicitly defined. // Check that all error types are propagated @@ -90,9 +90,7 @@ fn module_error_outer_enum_expand_explicit() { frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), frame_system::Error::MultiBlockMigrationsOngoing => (), - #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), - #[cfg(feature = "experimental")] frame_system::Error::FailedTask => (), frame_system::Error::NothingAuthorized => (), frame_system::Error::Unauthorized => (), diff --git a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs index 4149c4880cca29585d5d4dc45ec190f34e90d858..d2e759640c7328d8f768f65bb6a4f172c47613bb 100644 --- a/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs +++ b/substrate/frame/support/test/tests/pallet_outer_enums_implicit.rs @@ -19,8 +19,6 @@ use frame_support::derive_impl; mod common; -use common::outer_enums::{pallet, pallet2}; - pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; @@ -75,8 +73,10 @@ frame_support::construct_runtime!( } ); +#[cfg(feature = "experimental")] #[test] fn module_error_outer_enum_expand_implicit() { + use common::outer_enums::{pallet, pallet2}; // The Runtime has *all* parts implicitly defined. // Check that all error types are propagated @@ -90,9 +90,7 @@ fn module_error_outer_enum_expand_implicit() { frame_system::Error::NonZeroRefCount => (), frame_system::Error::CallFiltered => (), frame_system::Error::MultiBlockMigrationsOngoing => (), - #[cfg(feature = "experimental")] frame_system::Error::InvalidTask => (), - #[cfg(feature = "experimental")] frame_system::Error::FailedTask => (), frame_system::Error::NothingAuthorized => (), frame_system::Error::Unauthorized => (), diff --git a/substrate/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/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 0c6ff2cb8ddbaf0c1f2634eca4cfa0d6853721b1..662b7f1a94bfd3d0a159e84a8646f6612ac1503d 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. @@ -917,7 +921,7 @@ pub mod pallet { /// Total length (in bytes) for all extrinsics put together, for the current block. #[pallet::storage] - pub(super) type AllExtrinsicsLen = StorageValue<_, u32>; + pub type AllExtrinsicsLen = StorageValue<_, u32>; /// Map of block numbers to block hashes. #[pallet::storage] @@ -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..534ba1e863fc1cb294500e1c1daed2888a72dfd4 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -789,7 +789,10 @@ fn extrinsics_root_is_calculated_correctly() { System::note_finished_extrinsics(); let header = System::finalize(); - let ext_root = extrinsics_data_root::(vec![vec![1], vec![2]]); + let ext_root = extrinsics_data_root::( + vec![vec![1], vec![2]], + sp_core::storage::StateVersion::V0, + ); assert_eq!(ext_root, *header.extrinsics_root()); }); } diff --git a/substrate/frame/treasury/src/benchmarking.rs b/substrate/frame/treasury/src/benchmarking.rs index 63978c94e682f8cca84146e69c4b2e17d06ae637..0bac78503f41969e070436091819c143bc7697aa 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!(>::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); @@ -130,7 +137,7 @@ mod benchmarks { T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let (_, value, beneficiary_lookup) = setup_proposal::(SEED); Treasury::::spend_local(origin, value, beneficiary_lookup)?; - let proposal_id = Treasury::::proposal_count() - 1; + let proposal_id = ProposalCount::::get() - 1; let reject_origin = T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; @@ -155,6 +162,8 @@ mod benchmarks { Ok(()) } + /// This benchmark is short-circuited if `SpendOrigin` cannot provide + /// a successful origin, in which case `spend` is un-callable and can use weight=0. #[benchmark] fn spend() -> Result<(), BenchmarkError> { let origin = @@ -190,85 +199,135 @@ mod benchmarks { #[benchmark] fn payout() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, beneficiary, beneficiary_lookup) = create_spend_arguments::(SEED); T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; + + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind.clone()), + amount, + Box::new(beneficiary_lookup), + None, + )?; + + true + } else { + false + }; + T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); let caller: T::AccountId = account("caller", 0, SEED); - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), 0u32); - - let id = match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); - id - }, - _ => panic!("No payout attempt made"), - }; - assert_last_event::(Event::Paid { index: 0, payment_id: id }.into()); - assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); + #[block] + { + let res = Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } + + if spend_exists { + let id = match Spends::::get(0).unwrap().status { + PaymentState::Attempted { id, .. } => { + assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); + id + }, + _ => panic!("No payout attempt made"), + }; + assert_last_event::(Event::Paid { index: 0, payment_id: id }.into()); + assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); + } + Ok(()) } #[benchmark] fn check_status() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, beneficiary, beneficiary_lookup) = create_spend_arguments::(SEED); + T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); + T::Paymaster::ensure_successful(&beneficiary, asset_kind.clone(), amount); let caller: T::AccountId = account("caller", 0, SEED); - Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; - match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - T::Paymaster::ensure_concluded(id); - }, - _ => panic!("No payout attempt made"), + + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind), + amount, + Box::new(beneficiary_lookup), + None, + )?; + + Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; + match Spends::::get(0).unwrap().status { + PaymentState::Attempted { id, .. } => { + T::Paymaster::ensure_concluded(id); + }, + _ => panic!("No payout attempt made"), + }; + + true + } else { + false }; - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), 0u32); + #[block] + { + let res = + Treasury::::check_status(RawOrigin::Signed(caller.clone()).into(), 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } if let Some(s) = Spends::::get(0) { assert!(!matches!(s.status, PaymentState::Attempted { .. })); } + Ok(()) } #[benchmark] fn void_spend() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, _, beneficiary_lookup) = create_spend_arguments::(SEED); T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - assert!(Spends::::get(0).is_some()); + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind.clone()), + amount, + Box::new(beneficiary_lookup), + None, + )?; + assert!(Spends::::get(0).is_some()); + + true + } else { + false + }; + let origin = T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - #[extrinsic_call] - _(origin as T::RuntimeOrigin, 0u32); + #[block] + { + let res = Treasury::::void_spend(origin as T::RuntimeOrigin, 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } assert!(Spends::::get(0).is_none()); Ok(()) @@ -279,4 +338,15 @@ mod benchmarks { crate::tests::ExtBuilder::default().build(), crate::tests::Test ); + + mod no_spend_origin_tests { + use super::*; + + impl_benchmark_test_suite!( + Treasury, + crate::tests::ExtBuilder::default().spend_origin_succesful_origin_err().build(), + crate::tests::Test, + benchmarks_path = benchmarking + ); + } } diff --git a/substrate/frame/treasury/src/lib.rs b/substrate/frame/treasury/src/lib.rs index 3954489a2d156696efe0173aea74ec0583c16ae9..edb39f2306421a75f09690be3b64feb4c187c998 100644 --- a/substrate/frame/treasury/src/lib.rs +++ b/substrate/frame/treasury/src/lib.rs @@ -100,7 +100,7 @@ use frame_support::{ ReservableCurrency, WithdrawReasons, }, weights::Weight, - PalletId, + BoundedVec, PalletId, }; pub use pallet::*; @@ -278,12 +278,10 @@ pub mod pallet { /// Number of proposals that have been made. #[pallet::storage] - #[pallet::getter(fn proposal_count)] - pub(crate) type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; + pub type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; /// Proposals that have been made. #[pallet::storage] - #[pallet::getter(fn proposals)] pub type Proposals, I: 'static = ()> = StorageMap< _, Twox64Concat, @@ -299,7 +297,6 @@ pub mod pallet { /// Proposal indices that have been approved but not yet awarded. #[pallet::storage] - #[pallet::getter(fn approvals)] pub type Approvals, I: 'static = ()> = StorageValue<_, BoundedVec, ValueQuery>; @@ -335,7 +332,7 @@ pub mod pallet { impl, I: 'static> BuildGenesisConfig for GenesisConfig { fn build(&self) { // Create Treasury account - let account_id = >::account_id(); + let account_id = Pallet::::account_id(); let min = T::Currency::minimum_balance(); if T::Currency::free_balance(&account_id) < min { let _ = T::Currency::make_free_balance_be(&account_id, min); @@ -501,7 +498,7 @@ pub mod pallet { .unwrap_or(Ok(()))?; let beneficiary = T::Lookup::lookup(beneficiary)?; - let proposal_index = Self::proposal_count(); + let proposal_index = ProposalCount::::get(); Approvals::::try_append(proposal_index) .map_err(|_| Error::::TooManyApprovals)?; let proposal = Proposal { @@ -794,6 +791,21 @@ impl, I: 'static> Pallet { T::PalletId::get().into_account_truncating() } + /// Public function to proposal_count storage. + pub fn proposal_count() -> ProposalIndex { + ProposalCount::::get() + } + + /// Public function to proposals storage. + pub fn proposals(index: ProposalIndex) -> Option>> { + Proposals::::get(index) + } + + /// Public function to approvals storage. + pub fn approvals() -> BoundedVec { + Approvals::::get() + } + /// Spend some money! returns number of approvals before spend. pub fn spend_funds() -> Weight { let mut total_weight = Weight::zero(); @@ -803,15 +815,15 @@ impl, I: 'static> Pallet { let account_id = Self::account_id(); let mut missed_any = false; - let mut imbalance = >::zero(); + let mut imbalance = PositiveImbalanceOf::::zero(); let proposals_len = Approvals::::mutate(|v| { let proposals_approvals_len = v.len() as u32; v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. - if let Some(p) = Self::proposals(index) { + if let Some(p) = Proposals::::get(index) { if p.value <= budget_remaining { budget_remaining -= p.value; - >::remove(index); + Proposals::::remove(index); // return their deposit. let err_amount = T::Currency::unreserve(&p.proposer, p.bond); @@ -982,6 +994,6 @@ where { type Type = ::AccountId; fn get() -> Self::Type { - >::account_id() + crate::Pallet::::account_id() } } diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index f38a06f1fdf410a3d33f8484a5571a4e5e43c90e..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:: { @@ -219,7 +236,7 @@ fn get_payment_id(i: SpendIndex) -> Option { fn genesis_config_works() { ExtBuilder::default().build().execute_with(|| { assert_eq!(Treasury::pot(), 0); - assert_eq!(Treasury::proposal_count(), 0); + assert_eq!(ProposalCount::::get(), 0); }); } @@ -437,9 +454,9 @@ fn remove_already_removed_approval_fails() { assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 100, 3)); - assert_eq!(Treasury::approvals(), vec![0]); + assert_eq!(Approvals::::get(), vec![0]); assert_ok!(Treasury::remove_approval(RuntimeOrigin::root(), 0)); - assert_eq!(Treasury::approvals(), vec![]); + assert_eq!(Approvals::::get(), vec![]); assert_noop!( Treasury::remove_approval(RuntimeOrigin::root(), 0), diff --git a/substrate/frame/try-runtime/Cargo.toml b/substrate/frame/try-runtime/Cargo.toml index 2bd791f52385d0785babde5d5f77e15115e86213..7f7d1f2b50e05fa2351123006c86d24fbdc382cd 100644 --- a/substrate/frame/try-runtime/Cargo.toml +++ b/substrate/frame/try-runtime/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage.workspace = true repository.workspace = true -description = "FRAME pallet for democracy" +description = "Supporting types for try-runtime, testing and dry-running commands." [lints] workspace = true diff --git a/substrate/frame/utility/src/lib.rs b/substrate/frame/utility/src/lib.rs index 3ce5b4ff86496d48ffa60d953b86d56880ec65c5..ed5544fe55ca4f28007c9f16bc95987e570a70bf 100644 --- a/substrate/frame/utility/src/lib.rs +++ b/substrate/frame/utility/src/lib.rs @@ -74,7 +74,7 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{dispatch::DispatchClass, pallet_prelude::*}; use frame_system::pallet_prelude::*; #[pallet::pallet] @@ -183,21 +183,8 @@ pub mod pallet { /// event is deposited. #[pallet::call_index(0)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::batch(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn batch( @@ -233,13 +220,13 @@ pub mod pallet { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. - return Ok(Some(base_weight + weight).into()) + return Ok(Some(base_weight.saturating_add(weight)).into()) } Self::deposit_event(Event::ItemCompleted); } Self::deposit_event(Event::BatchCompleted); let base_weight = T::WeightInfo::batch(calls_len as u32); - Ok(Some(base_weight + weight).into()) + Ok(Some(base_weight.saturating_add(weight)).into()) } /// Send a call through an indexed pseudonym of the sender. @@ -305,21 +292,8 @@ pub mod pallet { /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(2)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn batch_all( @@ -359,7 +333,7 @@ pub mod pallet { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. - err.post_info = Some(base_weight + weight).into(); + err.post_info = Some(base_weight.saturating_add(weight)).into(); err })?; Self::deposit_event(Event::ItemCompleted); @@ -414,21 +388,8 @@ pub mod pallet { /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(4)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::force_batch(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::force_batch(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn force_batch( @@ -494,6 +455,27 @@ pub mod pallet { res.map(|_| ()).map_err(|e| e.error) } } + + impl Pallet { + /// Get the accumulated `weight` and the dispatch class for the given `calls`. + fn weight_and_dispatch_class( + calls: &[::RuntimeCall], + ) -> (Weight, DispatchClass) { + let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()); + let (dispatch_weight, dispatch_class) = dispatch_infos.fold( + (Weight::zero(), DispatchClass::Operational), + |(total_weight, dispatch_class): (Weight, DispatchClass), di| { + ( + total_weight.saturating_add(di.weight), + // If not all are `Operational`, we want to use `DispatchClass::Normal`. + if di.class == DispatchClass::Normal { di.class } else { dispatch_class }, + ) + }, + ); + + (dispatch_weight, dispatch_class) + } + } } /// A pallet identifier. These are per pallet and should be stored in a registry somewhere. 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/whitelist/Cargo.toml b/substrate/frame/whitelist/Cargo.toml index e910fc323714277c6128c41a5303986ada707436..a347174ed2ebee4fc1cc02e545b4e57c90372e24 100644 --- a/substrate/frame/whitelist/Cargo.toml +++ b/substrate/frame/whitelist/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage.workspace = true repository.workspace = true -description = "FRAME pallet for whitelisting call, and dispatch from specific origin" +description = "FRAME pallet for whitelisting calls, and dispatching from a specific origin" [lints] workspace = true diff --git a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs index 2c423f8c28dd435599e1cc5b2d5a49807fd73abb..21397abc8fc62ad1fab87a1be9bad78973e76fd3 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -471,7 +471,7 @@ fn extend_with_api_version(mut trait_: Path, version: Option) -> Path { v } else { // nothing to do - return trait_ + return trait_; }; let trait_name = &mut trait_ @@ -762,7 +762,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { error.combine(Error::new(other_span, "First trait implementation.")); - return Err(error) + return Err(error); } let id: Path = parse_quote!( #path ID ); @@ -892,7 +892,7 @@ fn extract_cfg_api_version(attrs: &Vec, span: Span) -> Result, span: Span) -> Result Each runtime API can have only one version.", API_VERSION_ATTRIBUTE ), - )) + )); } // Parse the runtime version if there exists one. diff --git a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs index 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..94da6748cbdb36070c34d48b9b2405518b4aad52 100644 --- a/substrate/primitives/api/proc-macro/src/utils.rs +++ b/substrate/primitives/api/proc-macro/src/utils.rs @@ -21,8 +21,9 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use syn::{ - parse_quote, spanned::Spanned, token::And, Attribute, Error, FnArg, GenericArgument, Ident, - ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath, + parse_quote, punctuated::Punctuated, spanned::Spanned, token::And, Attribute, Error, Expr, + ExprLit, FnArg, GenericArgument, Ident, ImplItem, ItemImpl, Lit, Meta, MetaNameValue, Pat, + Path, PathArguments, Result, ReturnType, Signature, Token, Type, TypePath, }; /// Generates the access to the `sc_client` crate. @@ -33,7 +34,7 @@ pub fn generate_crate_access() -> TokenStream { let renamed_name = Ident::new(&renamed_name, Span::call_site()); quote!(#renamed_name::__private) }, - Err(e) => + Err(e) => { if let Ok(FoundCrate::Name(name)) = crate_name(&"polkadot-sdk-frame").or_else(|_| crate_name(&"frame")) { @@ -47,7 +48,8 @@ pub fn generate_crate_access() -> TokenStream { } else { let err = Error::new(Span::call_site(), e).to_compile_error(); quote!( #err ) - }, + } + }, } } @@ -144,7 +146,7 @@ pub fn extract_parameter_names_types_and_borrows( return Err(Error::new(input.span(), "`self` parameter not supported!")), FnArg::Receiver(recv) => if recv.mutability.is_some() || recv.reference.is_none() { - return Err(Error::new(recv.span(), "Only `&self` is supported!")) + return Err(Error::new(recv.span(), "Only `&self` is supported!")); }, } } @@ -284,6 +286,85 @@ pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } +fn deprecation_msg_formatter(msg: &str) -> String { + format!( + r#"{msg} + help: the following are the possible correct uses +| +| #[deprecated = "reason"] +| +| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] +| +| #[deprecated] +|"# + ) +} + +fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result { + match &attr.meta { + Meta::List(meta_list) => { + let parsed = meta_list + .parse_args_with(Punctuated::::parse_terminated) + .map_err(|e| Error::new(attr.span(), e.to_string()))?; + let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| { + let value = match &item.value { + Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit), + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter( + "Invalid deprecation attribute: expected string literal", + ), + )), + }?; + if item.path.is_ident("note") { + acc.0.replace(value); + } else if item.path.is_ident("since") { + acc.1.replace(value); + } + Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc) + })?; + note.map_or_else( + || Err(Error::new(attr.span(), deprecation_msg_formatter( + "Invalid deprecation attribute: missing `note`"))), + |note| { + let since = if let Some(str) = since { + quote! { Some(#str) } + } else { + quote! { None } + }; + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }}; + Ok(doc) + }, + ) + }, + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }), + .. + }) => { + // #[deprecated = "lit"] + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } }; + Ok(doc) + }, + Meta::Path(_) => { + // #[deprecated] + Ok(quote! { #crate_::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }) + }, + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"), + )), + } +} + +/// collects deprecation attribute if its present. +pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result { + attrs + .iter() + .find(|a| a.path().is_ident("deprecated")) + .map(|a| parse_deprecated_meta(&crate_, a)) + .unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::DeprecationStatusIR::NotDeprecated})) +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -330,4 +411,38 @@ mod tests { assert_eq!(cfg_std, filtered[0]); assert_eq!(cfg_benchmarks, filtered[1]); } + + #[test] + fn check_deprecated_attr() { + const FIRST: &'static str = "hello"; + const SECOND: &'static str = "WORLD"; + + let simple: Attribute = parse_quote!(#[deprecated]); + let simple_path: Attribute = parse_quote!(#[deprecated = #FIRST]); + let meta_list: Attribute = parse_quote!(#[deprecated(note = #FIRST)]); + let meta_list_with_since: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND)]); + let extra_fields: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND, extra = "Test")]); + assert_eq!( + get_deprecation("e! { crate }, &[simple]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[simple_path]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list_with_since]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[extra_fields]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + } } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index 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/tests/ui/deprecation_info.rs b/substrate/primitives/api/test/tests/ui/deprecation_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..61f93fcd921dbe6ba988e8a65659393c1f4e9d22 --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.rs @@ -0,0 +1,29 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +sp_api::decl_runtime_apis! { + pub trait Api { + #[deprecated(unknown_kw = "test")] + fn test(); + #[deprecated(since = 5)] + fn test2(); + #[deprecated = 5] + fn test3(); + } +} + +fn main() {} diff --git a/substrate/primitives/api/test/tests/ui/deprecation_info.stderr b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr new file mode 100644 index 0000000000000000000000000000000000000000..2466c3ea5d50962e2d53331542a4e350bc363ebe --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr @@ -0,0 +1,25 @@ +error: Invalid deprecation attribute: missing `note` + help: the following are the possible correct uses + | + | #[deprecated = "reason"] + | + | #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | + | #[deprecated] + | + --> tests/ui/deprecation_info.rs:20:3 + | +20 | #[deprecated(unknown_kw = "test")] + | ^ + +error[E0541]: unknown meta item 'unknown_kw' + --> tests/ui/deprecation_info.rs:20:16 + | +20 | #[deprecated(unknown_kw = "test")] + | ^^^^^^^^^^^^^^^^^^^ expected one of `since`, `note` + +error[E0565]: literal in `deprecated` value must be a string + --> tests/ui/deprecation_info.rs:22:24 + | +22 | #[deprecated(since = 5)] + | ^ diff --git a/substrate/primitives/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 f31aa5756ba267f732fdb72e5feba614d6ff0688..57ddab9a70ceacb430cdcf65fd2af1cfbc05ec77 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -26,6 +26,7 @@ sp-io = { workspace = true } sp-mmr-primitives = { workspace = true } sp-runtime = { workspace = true } sp-keystore = { workspace = true } +sp-weights = { workspace = true } strum = { features = ["derive"], workspace = true } lazy_static = { optional = true, workspace = true } @@ -48,6 +49,7 @@ std = [ "sp-keystore/std", "sp-mmr-primitives/std", "sp-runtime/std", + "sp-weights/std", "strum/std", ] diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs index 6ec4a727e276503cac7e9a1fb200090d5272a90a..e977fb0ea25f6a2bcdd1d7fadb0364332a1f710d 100644 --- a/substrate/primitives/consensus/beefy/src/lib.rs +++ b/substrate/primitives/consensus/beefy/src/lib.rs @@ -56,6 +56,7 @@ use sp_runtime::{ traits::{Hash, Header as HeaderT, Keccak256, NumberFor}, OpaqueValue, }; +use sp_weights::Weight; /// Key type for BEEFY module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BEEFY; @@ -460,6 +461,15 @@ pub trait AncestryHelper { ) -> bool; } +/// Weight information for the logic in `AncestryHelper`. +pub trait AncestryHelperWeightInfo: AncestryHelper
{ + /// Weight info for the `AncestryHelper::extract_validation_context()` method. + fn extract_validation_context() -> Weight; + + /// Weight info for the `AncestryHelper::is_non_canonical()` method. + fn is_non_canonical(proof: &>::Proof) -> Weight; +} + /// An opaque type used to represent the key ownership proof at the runtime API /// boundary. The inner value is an encoded representation of the actual key /// ownership proof which will be parameterized when defining the runtime. At diff --git a/substrate/primitives/consensus/slots/src/lib.rs b/substrate/primitives/consensus/slots/src/lib.rs index eb3b3d3a449f2f8279f7183446941c4dedd2610a..dfa46fcf2571f3681e7de48995bef035daba0ba2 100644 --- a/substrate/primitives/consensus/slots/src/lib.rs +++ b/substrate/primitives/consensus/slots/src/lib.rs @@ -91,6 +91,13 @@ impl Slot { Slot(timestamp.as_millis() / slot_duration.as_millis()) } + /// Timestamp of the start of the slot. + /// + /// Returns `None` if would overflow for given `SlotDuration`. + pub fn timestamp(&self, slot_duration: SlotDuration) -> Option { + slot_duration.as_millis().checked_mul(self.0).map(Timestamp::new) + } + /// Saturating addition. pub fn saturating_add>(self, rhs: T) -> Self { Self(self.0.saturating_add(rhs.into())) diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index 2cbac305b4d97438389b288c804152462769501e..b33609464fc192d69fda8b0d19f6ddc5ed237916 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -62,6 +62,10 @@ pub type Result = core::result::Result<(), sp_runtime::RuntimeString>; /// The type representing preset ID. pub type PresetId = sp_runtime::RuntimeString; +/// The default `development` preset used to communicate with the runtime via +/// [`GenesisBuilder`] interface. +pub const DEV_RUNTIME_PRESET: &'static str = "development"; + sp_api::decl_runtime_apis! { /// API to interact with RuntimeGenesisConfig for the runtime pub trait GenesisBuilder { diff --git a/substrate/primitives/merkle-mountain-range/src/lib.rs b/substrate/primitives/merkle-mountain-range/src/lib.rs index 3740047e027829eb471a61244b460cdd58fe8cd5..061e5dbb6c7d896ea2e18e1cb1b07beaff7c670d 100644 --- a/substrate/primitives/merkle-mountain-range/src/lib.rs +++ b/substrate/primitives/merkle-mountain-range/src/lib.rs @@ -144,7 +144,7 @@ impl FullLeaf for OpaqueLeaf { /// /// It is different from [`OpaqueLeaf`], because it does implement `Codec` /// and the encoding has to match raw `Vec` encoding. -#[derive(codec::Encode, codec::Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] pub struct EncodableOpaqueLeaf(pub Vec); impl EncodableOpaqueLeaf { diff --git a/substrate/primitives/merkle-mountain-range/src/utils.rs b/substrate/primitives/merkle-mountain-range/src/utils.rs index 72674e24a272849fcabf16f243764dbb2858c0fa..2460af4b800fc689cd0097cf131d82c0ede5f4ee 100644 --- a/substrate/primitives/merkle-mountain-range/src/utils.rs +++ b/substrate/primitives/merkle-mountain-range/src/utils.rs @@ -91,7 +91,7 @@ impl NodesUtils { Self::leaf_node_index_to_leaf_index(rightmost_leaf_pos) } - // Translate a _leaf_ `NodeIndex` to its `LeafIndex`. + /// Translate a _leaf_ `NodeIndex` to its `LeafIndex`. fn leaf_node_index_to_leaf_index(pos: NodeIndex) -> LeafIndex { if pos == 0 { return 0 @@ -100,8 +100,13 @@ impl NodesUtils { (pos + peaks.len() as u64) >> 1 } - // Starting from any node position get position of rightmost leaf; this is the leaf - // responsible for the addition of node `pos`. + /// Translate a `LeafIndex` to its _leaf_ `NodeIndex`. + pub fn leaf_index_to_leaf_node_index(leaf_index: NodeIndex) -> LeafIndex { + helper::leaf_index_to_pos(leaf_index) + } + + /// Starting from any node position get position of rightmost leaf; this is the leaf + /// responsible for the addition of node `pos`. fn rightmost_leaf_node_index_from_pos(pos: NodeIndex) -> NodeIndex { pos - (helper::pos_height_in_tree(pos) as u64) } diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index 9001d4c034e9f68413c2d1ceb09e1e338586b287..da4f5d7f3711cdf6b85773daa0b90469129b0d49 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), } } } @@ -134,6 +140,8 @@ pub struct PalletMetadataIR { pub index: u8, /// Pallet documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletMetadataIR { @@ -150,6 +158,7 @@ impl IntoPortable for PalletMetadataIR { associated_types: registry.map_into_portable(self.associated_types), index: self.index, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -271,6 +280,8 @@ pub struct StorageEntryMetadataIR { pub default: Vec, /// Storage entry documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for StorageEntryMetadataIR { @@ -283,6 +294,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), } } } @@ -357,19 +369,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), + } } } @@ -378,19 +389,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), + } } } @@ -405,6 +415,8 @@ pub struct PalletConstantMetadataIR { pub value: Vec, /// Documentation of the constant. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletConstantMetadataIR { @@ -416,6 +428,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), } } } @@ -425,19 +438,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), + } } } @@ -477,3 +489,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/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index 55e9f4b319954f7d0585d66d00b72a340f292d9f..800bf4bd073741afc1794a3b6c4bbc66678230da 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -32,6 +32,7 @@ sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-std = { workspace = true } +sp-trie = { workspace = true } sp-weights = { workspace = true } docify = { workspace = true } tracing = { workspace = true, features = ["log"], default-features = false } @@ -69,6 +70,7 @@ std = [ "sp-state-machine/std", "sp-std/std", "sp-tracing/std", + "sp-trie/std", "sp-weights/std", "tracing/std", ] diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index d313d23395a0fa1b655dccaf94e30cacc1ac5c2a..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}; @@ -438,10 +439,10 @@ impl TryFrom for ecdsa::Public { #[cfg(feature = "std")] impl std::fmt::Display for MultiSigner { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match *self { - Self::Ed25519(ref who) => write!(fmt, "ed25519: {}", who), - Self::Sr25519(ref who) => write!(fmt, "sr25519: {}", who), - Self::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who), + match self { + Self::Ed25519(who) => write!(fmt, "ed25519: {}", who), + Self::Sr25519(who) => write!(fmt, "sr25519: {}", who), + Self::Ecdsa(who) => write!(fmt, "ecdsa: {}", who), } } } @@ -449,23 +450,14 @@ impl std::fmt::Display for MultiSigner { impl Verify for MultiSignature { type Signer = MultiSigner; fn verify>(&self, mut msg: L, signer: &AccountId32) -> bool { - match (self, signer) { - (Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) { - Ok(signer) => sig.verify(msg, &signer), - Err(()) => false, - }, - (Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) { - Ok(signer) => sig.verify(msg, &signer), - Err(()) => false, - }, - (Self::Ecdsa(ref sig), who) => { + let who: [u8; 32] = *signer.as_ref(); + match self { + Self::Ed25519(sig) => sig.verify(msg, &who.into()), + Self::Sr25519(sig) => sig.verify(msg, &who.into()), + Self::Ecdsa(sig) => { let m = sp_io::hashing::blake2_256(msg.get()); - match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { - Ok(pubkey) => - &sp_io::hashing::blake2_256(pubkey.as_ref()) == - >::as_ref(who), - _ => false, - } + sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) + .map_or(false, |pubkey| sp_io::hashing::blake2_256(&pubkey) == who) }, } } @@ -601,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 @@ -706,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) @@ -730,6 +730,7 @@ impl From for &'static str { Corruption => "State corrupt", Unavailable => "Resource unavailable", RootNotAllowed => "Root not allowed", + Trie(e) => e.into(), } } } @@ -777,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.rs b/substrate/primitives/runtime/src/proving_trie.rs new file mode 100644 index 0000000000000000000000000000000000000000..9a423f18284fd91c3eb5eb711edb48ec9f8bb77f --- /dev/null +++ b/substrate/primitives/runtime/src/proving_trie.rs @@ -0,0 +1,391 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types for a compact base-16 merkle trie used for checking and generating proofs within the +//! runtime. The `sp-trie` crate exposes all of these same functionality (and more), but this +//! library is designed to work more easily with runtime native types, which simply need to +//! implement `Encode`/`Decode`. It also exposes a runtime friendly `TrieError` type which can be +//! use inside of a FRAME Pallet. +//! +//! Proofs are created with latest substrate trie format (`LayoutV1`), and are not compatible with +//! proofs using `LayoutV0`. + +use crate::{Decode, DispatchError, Encode, MaxEncodedLen, TypeInfo}; +#[cfg(feature = "serde")] +use crate::{Deserialize, Serialize}; + +use sp_std::vec::Vec; +use sp_trie::{ + trie_types::{TrieDBBuilder, TrieDBMutBuilderV1, TrieError as SpTrieError}, + LayoutV1, MemoryDB, Trie, TrieMut, VerifyError, +}; + +type HashOf = ::Out; + +/// A runtime friendly error type for tries. +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TrieError { + /* From TrieError */ + /// Attempted to create a trie with a state root not in the DB. + InvalidStateRoot, + /// Trie item not found in the database, + IncompleteDatabase, + /// A value was found in the trie with a nibble key that was not byte-aligned. + ValueAtIncompleteKey, + /// Corrupt Trie item. + DecoderError, + /// Hash is not value. + InvalidHash, + /* From VerifyError */ + /// The statement being verified contains multiple key-value pairs with the same key. + DuplicateKey, + /// The proof contains at least one extraneous node. + ExtraneousNode, + /// The proof contains at least one extraneous value which should have been omitted from the + /// proof. + ExtraneousValue, + /// The proof contains at least one extraneous hash reference the should have been omitted. + ExtraneousHashReference, + /// The proof contains an invalid child reference that exceeds the hash length. + InvalidChildReference, + /// The proof indicates that an expected value was not found in the trie. + ValueMismatch, + /// The proof is missing trie nodes required to verify. + IncompleteProof, + /// The root hash computed from the proof is incorrect. + RootMismatch, + /// One of the proof nodes could not be decoded. + DecodeError, +} + +impl From> for TrieError { + fn from(error: SpTrieError) -> Self { + match error { + SpTrieError::InvalidStateRoot(..) => Self::InvalidStateRoot, + SpTrieError::IncompleteDatabase(..) => Self::IncompleteDatabase, + SpTrieError::ValueAtIncompleteKey(..) => Self::ValueAtIncompleteKey, + SpTrieError::DecoderError(..) => Self::DecoderError, + SpTrieError::InvalidHash(..) => Self::InvalidHash, + } + } +} + +impl From> for TrieError { + fn from(error: VerifyError) -> Self { + match error { + VerifyError::DuplicateKey(..) => Self::DuplicateKey, + VerifyError::ExtraneousNode => Self::ExtraneousNode, + VerifyError::ExtraneousValue(..) => Self::ExtraneousValue, + VerifyError::ExtraneousHashReference(..) => Self::ExtraneousHashReference, + VerifyError::InvalidChildReference(..) => Self::InvalidChildReference, + VerifyError::ValueMismatch(..) => Self::ValueMismatch, + VerifyError::IncompleteProof => Self::IncompleteProof, + VerifyError::RootMismatch(..) => Self::RootMismatch, + VerifyError::DecodeError(..) => Self::DecodeError, + } + } +} + +impl From for &'static str { + fn from(e: TrieError) -> &'static str { + match e { + TrieError::InvalidStateRoot => "The state root is not in the database.", + TrieError::IncompleteDatabase => "A trie item was not found in the database.", + TrieError::ValueAtIncompleteKey => + "A value was found with a key that is not byte-aligned.", + TrieError::DecoderError => "A corrupt trie item was encountered.", + TrieError::InvalidHash => "The hash does not match the expected value.", + TrieError::DuplicateKey => "The proof contains duplicate keys.", + TrieError::ExtraneousNode => "The proof contains extraneous nodes.", + TrieError::ExtraneousValue => "The proof contains extraneous values.", + TrieError::ExtraneousHashReference => "The proof contains extraneous hash references.", + TrieError::InvalidChildReference => "The proof contains an invalid child reference.", + TrieError::ValueMismatch => "The proof indicates a value mismatch.", + TrieError::IncompleteProof => "The proof is incomplete.", + TrieError::RootMismatch => "The root hash computed from the proof is incorrect.", + TrieError::DecodeError => "One of the proof nodes could not be decoded.", + } + } +} + +/// A helper structure for building a basic base-16 merkle trie and creating compact proofs for that +/// trie. Proofs are created with latest substrate trie format (`LayoutV1`), and are not compatible +/// with proofs using `LayoutV0`. +pub struct BasicProvingTrie +where + Hashing: sp_core::Hasher, +{ + db: MemoryDB, + root: HashOf, + _phantom: core::marker::PhantomData<(Key, Value)>, +} + +impl BasicProvingTrie +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + /// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs. + pub fn generate_for(items: I) -> Result + where + I: IntoIterator, + { + let mut db = MemoryDB::default(); + let mut root = Default::default(); + + { + let mut trie = TrieDBMutBuilderV1::new(&mut db, &mut root).build(); + for (key, value) in items.into_iter() { + key.using_encoded(|k| value.using_encoded(|v| trie.insert(k, v))) + .map_err(|_| "failed to insert into trie")?; + } + } + + Ok(Self { db, root, _phantom: Default::default() }) + } + + /// Access the underlying trie root. + pub fn root(&self) -> &HashOf { + &self.root + } + + /// Query a value contained within the current trie. Returns `None` if the + /// nodes within the current `MemoryDB` are insufficient to query the item. + pub fn query(&self, key: Key) -> Option + where + Value: Decode, + { + let trie = TrieDBBuilder::new(&self.db, &self.root).build(); + key.using_encoded(|s| trie.get(s)) + .ok()? + .and_then(|raw| Value::decode(&mut &*raw).ok()) + } + + /// Create a compact merkle proof needed to prove all `keys` and their values are in the trie. + /// Returns `None` if the nodes within the current `MemoryDB` are insufficient to create a + /// proof. + /// + /// This function makes a proof with latest substrate trie format (`LayoutV1`), and is not + /// compatible with `LayoutV0`. + /// + /// When verifying the proof created by this function, you must include all of the keys and + /// values of the proof, else the verifier will complain that extra nodes are provided in the + /// proof that are not needed. + pub fn create_proof(&self, keys: &[Key]) -> Result>, DispatchError> { + sp_trie::generate_trie_proof::, _, _, _>( + &self.db, + self.root, + &keys.into_iter().map(|k| k.encode()).collect::>>(), + ) + .map_err(|err| TrieError::from(*err).into()) + } + + /// Create a compact merkle proof needed to prove a single key and its value are in the trie. + /// Returns `None` if the nodes within the current `MemoryDB` are insufficient to create a + /// proof. + /// + /// This function makes a proof with latest substrate trie format (`LayoutV1`), and is not + /// compatible with `LayoutV0`. + pub fn create_single_value_proof(&self, key: Key) -> Result>, DispatchError> { + self.create_proof(&[key]) + } +} + +/// Verify the existence or non-existence of `key` and `value` in a given trie root and proof. +/// +/// Proofs must be created with latest substrate trie format (`LayoutV1`). +pub fn verify_single_value_proof( + root: HashOf, + proof: &[Vec], + key: Key, + maybe_value: Option, +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + sp_trie::verify_trie_proof::, _, _, _>( + &root, + proof, + &[(key.encode(), maybe_value.map(|value| value.encode()))], + ) + .map_err(|err| TrieError::from(err).into()) +} + +/// Verify the existence or non-existence of multiple `items` in a given trie root and proof. +/// +/// Proofs must be created with latest substrate trie format (`LayoutV1`). +pub fn verify_proof( + root: HashOf, + proof: &[Vec], + items: &[(Key, Option)], +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + let items_encoded = items + .into_iter() + .map(|(key, maybe_value)| (key.encode(), maybe_value.as_ref().map(|value| value.encode()))) + .collect::, Option>)>>(); + + sp_trie::verify_trie_proof::, _, _, _>(&root, proof, &items_encoded) + .map_err(|err| TrieError::from(err).into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::BlakeTwo256; + use sp_core::H256; + use sp_std::collections::btree_map::BTreeMap; + + // A trie which simulates a trie of accounts (u32) and balances (u128). + type BalanceTrie = BasicProvingTrie; + + // The expected root hash for an empty trie. + fn empty_root() -> H256 { + sp_trie::empty_trie_root::>() + } + + fn create_balance_trie() -> BalanceTrie { + // Create a map of users and their balances. + let mut map = BTreeMap::::new(); + for i in 0..100u32 { + map.insert(i, i.into()); + } + + // Put items into the trie. + let balance_trie = BalanceTrie::generate_for(map).unwrap(); + + // Root is changed. + let root = *balance_trie.root(); + assert!(root != empty_root()); + + // Assert valid keys are queryable. + assert_eq!(balance_trie.query(6u32), Some(6u128)); + assert_eq!(balance_trie.query(9u32), Some(9u128)); + assert_eq!(balance_trie.query(69u32), Some(69u128)); + // Invalid key returns none. + assert_eq!(balance_trie.query(6969u32), None); + + balance_trie + } + + #[test] + fn empty_trie_works() { + let empty_trie = BalanceTrie::generate_for(Vec::new()).unwrap(); + assert_eq!(*empty_trie.root(), empty_root()); + } + + #[test] + fn basic_end_to_end_single_value() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_single_value_proof(6u32).unwrap(); + + // Assert key is provable, all other keys are invalid. + for i in 0..200u32 { + if i == 6 { + assert_eq!( + verify_single_value_proof::( + root, + &proof, + i, + Some(u128::from(i)) + ), + Ok(()) + ); + // Wrong value is invalid. + assert_eq!( + verify_single_value_proof::( + root, + &proof, + i, + Some(u128::from(i + 1)) + ), + Err(TrieError::RootMismatch.into()) + ); + } else { + assert!(verify_single_value_proof::( + root, + &proof, + i, + Some(u128::from(i)) + ) + .is_err()); + assert!(verify_single_value_proof::( + root, + &proof, + i, + None:: + ) + .is_err()); + } + } + } + + #[test] + fn basic_end_to_end_multi_value() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid and invalid key. + let proof = balance_trie.create_proof(&[6u32, 69u32, 6969u32]).unwrap(); + let items = [(6u32, Some(6u128)), (69u32, Some(69u128)), (6969u32, None)]; + + assert_eq!(verify_proof::(root, &proof, &items), Ok(())); + } + + #[test] + fn proof_fails_with_bad_data() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_single_value_proof(6u32).unwrap(); + + // Correct data verifies successfully + assert_eq!( + verify_single_value_proof::(root, &proof, 6u32, Some(6u128)), + Ok(()) + ); + + // Fail to verify proof with wrong root + assert_eq!( + verify_single_value_proof::( + Default::default(), + &proof, + 6u32, + Some(6u128) + ), + Err(TrieError::RootMismatch.into()) + ); + + // Fail to verify proof with wrong data + assert_eq!( + verify_single_value_proof::(root, &[], 6u32, Some(6u128)), + Err(TrieError::IncompleteProof.into()) + ); + } +} 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/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 03c1af50240bbe229eaa28dfaf6f7b77458aff79..5e94524816a051ac7e1996d05f0875dea5b0df9b 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -525,23 +525,26 @@ pub trait DelegationInterface { /// This takes into account any pending slashes to `Agent` against the delegated balance. fn agent_balance(agent: Agent) -> Option; + /// Returns the total amount of funds that is unbonded and can be withdrawn from the `Agent` + /// account. `None` if not an `Agent`. + fn agent_transferable_balance(agent: Agent) -> Option; + /// Returns the total amount of funds delegated. `None` if not a `Delegator`. fn delegator_balance(delegator: Delegator) -> Option; - /// Delegate funds to `Agent`. - /// - /// Only used for the initial delegation. Use [`Self::delegate_extra`] to add more delegation. - fn delegate( - delegator: Delegator, + /// Register `Agent` such that it can accept delegation. + fn register_agent( agent: Agent, reward_account: &Self::AccountId, - amount: Self::Balance, ) -> DispatchResult; - /// Add more delegation to the `Agent`. + /// Removes `Agent` registration. /// - /// If this is the first delegation, use [`Self::delegate`] instead. - fn delegate_extra( + /// This should only be allowed if the agent has no staked funds. + fn remove_agent(agent: Agent) -> DispatchResult; + + /// Add delegation to the `Agent`. + fn delegate( delegator: Delegator, agent: Agent, amount: Self::Balance, @@ -616,7 +619,7 @@ pub trait DelegationMigrator { /// /// Also removed from [`StakingUnchecked`] as a Virtual Staker. Useful for testing. #[cfg(feature = "runtime-benchmarks")] - fn drop_agent(agent: Agent); + fn migrate_to_direct_staker(agent: Agent); } sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); 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/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/primitives/wasm-interface/Cargo.toml b/substrate/primitives/wasm-interface/Cargo.toml index 96ea8f4235d50a2f4d7e0403e9a9c8c4e7c37154..9d0310fd22e8ea675ade50110d002afa8f3adeb2 100644 --- a/substrate/primitives/wasm-interface/Cargo.toml +++ b/substrate/primitives/wasm-interface/Cargo.toml @@ -25,5 +25,9 @@ anyhow = { optional = true, workspace = true } [features] default = ["std"] -std = ["codec/std", "log/std"] +std = [ + "anyhow?/std", + "codec/std", + "log/std", +] wasmtime = ["anyhow", "dep:wasmtime"] diff --git a/substrate/test-utils/client/src/client_ext.rs b/substrate/test-utils/client/src/client_ext.rs index 9dc4739eb795437cff17fae69e5a0181cf150510..a4f91f2ec836af3f72b76d0d447c2d8ab388f90c 100644 --- a/substrate/test-utils/client/src/client_ext.rs +++ b/substrate/test-utils/client/src/client_ext.rs @@ -42,25 +42,22 @@ pub trait ClientExt: Sized { #[async_trait::async_trait] pub trait ClientBlockImportExt: Sized { /// Import block to the chain. No finality. - async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>; + async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>; /// Import a block and make it our best block if possible. - async fn import_as_best( - &mut self, - origin: BlockOrigin, - block: Block, - ) -> Result<(), ConsensusError>; + async fn import_as_best(&self, origin: BlockOrigin, block: Block) + -> Result<(), ConsensusError>; /// Import a block and finalize it. async fn import_as_final( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError>; /// Import block with justification(s), finalizes block. async fn import_justified( - &mut self, + &self, origin: BlockOrigin, block: Block, justifications: Justifications, @@ -94,7 +91,7 @@ where for<'r> &'r T: BlockImport, T: Send + Sync, { - async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { + async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); let mut import = BlockImportParams::new(origin, header); import.body = Some(extrinsics); @@ -104,7 +101,7 @@ where } async fn import_as_best( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -117,7 +114,7 @@ where } async fn import_as_final( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -131,7 +128,7 @@ where } async fn import_justified( - &mut self, + &self, origin: BlockOrigin, block: Block, justifications: Justifications, @@ -151,11 +148,11 @@ where impl ClientBlockImportExt for Client where Self: BlockImport, - RA: Send, + RA: Send + Sync, B: Send + Sync, E: Send + Sync, { - async fn import(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { + async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> { let (header, extrinsics) = block.deconstruct(); let mut import = BlockImportParams::new(origin, header); import.body = Some(extrinsics); @@ -165,7 +162,7 @@ where } async fn import_as_best( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -178,7 +175,7 @@ where } async fn import_as_final( - &mut self, + &self, origin: BlockOrigin, block: Block, ) -> Result<(), ConsensusError> { @@ -192,7 +189,7 @@ where } async fn import_justified( - &mut self, + &self, origin: BlockOrigin, block: Block, justifications: Justifications, diff --git a/substrate/test-utils/runtime/client/src/trait_tests.rs b/substrate/test-utils/runtime/client/src/trait_tests.rs index 6f6bb5c36ee486b3551df8efbbf44ada170a30a6..c3a5f173d14ee419e67be40ebd847fdb954bdcb8 100644 --- a/substrate/test-utils/runtime/client/src/trait_tests.rs +++ b/substrate/test-utils/runtime/client/src/trait_tests.rs @@ -46,7 +46,7 @@ where // B2 -> C3 // A1 -> D2 - let mut client = TestClientBuilder::with_backend(backend.clone()).build(); + let client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); let genesis_hash = client.chain_info().genesis_hash; @@ -221,7 +221,7 @@ where // B2 -> C3 // A1 -> D2 - let mut client = TestClientBuilder::with_backend(backend.clone()).build(); + let client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); let genesis_hash = client.chain_info().genesis_hash; @@ -390,7 +390,7 @@ where // A1 -> B2 -> B3 -> B4 // B2 -> C3 // A1 -> D2 - let mut client = TestClientBuilder::with_backend(backend.clone()).build(); + let client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); let genesis_hash = client.chain_info().genesis_hash; diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index d1a3eaa2daa921b8092cf58a2118122ea1228260..840081003b848aa4d5daf67779095f26b7ee3064 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 { @@ -1062,7 +1062,7 @@ mod tests { // This tests that the on-chain `HEAP_PAGES` parameter is respected. // Create a client devoting only 8 pages of wasm memory. This gives us ~512k of heap memory. - let mut client = TestClientBuilder::new().set_heap_pages(8).build(); + let client = TestClientBuilder::new().set_heap_pages(8).build(); let best_hash = client.chain_info().best_hash; // Try to allocate 1024k of memory on heap. This is going to fail since it is twice larger 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 030e9f69b9b6b9cc16d3c2e97d3e208b64e17579..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 @@ -356,7 +357,6 @@ mod tests { #[test] fn should_generate_empty_root() { // given - sp_tracing::init_for_tests(); let data: Vec<[u8; 1]> = Default::default(); // when @@ -372,7 +372,6 @@ mod tests { #[test] fn should_generate_single_root() { // given - sp_tracing::init_for_tests(); let data = vec![array_bytes::hex2array_unchecked::<_, 20>( "E04CC55ebEE1cBCE552f250e85c57B70B2E2625b", )]; @@ -390,7 +389,6 @@ mod tests { #[test] fn should_generate_root_pow_2() { // given - sp_tracing::init_for_tests(); let data = vec![ array_bytes::hex2array_unchecked::<_, 20>("E04CC55ebEE1cBCE552f250e85c57B70B2E2625b"), array_bytes::hex2array_unchecked::<_, 20>("25451A4de12dcCc2D166922fA938E900fCc4ED24"), @@ -408,7 +406,6 @@ mod tests { #[test] fn should_generate_root_complex() { - sp_tracing::init_for_tests(); let test = |root, data| { assert_eq!(array_bytes::bytes2hex("", &merkle_root::(data)), root); }; @@ -437,7 +434,6 @@ mod tests { #[test] fn should_generate_and_verify_proof_simple() { // given - sp_tracing::init_for_tests(); let data = vec!["a", "b", "c"]; // when @@ -445,7 +441,7 @@ mod tests { assert!(verify_proof::( &proof0.root, proof0.proof.clone(), - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf, )); @@ -454,7 +450,7 @@ mod tests { assert!(verify_proof::( &proof1.root, proof1.proof, - data.len(), + data.len() as _, proof1.leaf_index, &proof1.leaf, )); @@ -463,7 +459,7 @@ mod tests { assert!(verify_proof::( &proof2.root, proof2.proof, - data.len(), + data.len() as _, proof2.leaf_index, &proof2.leaf )); @@ -484,7 +480,7 @@ mod tests { ) .into(), proof0.proof, - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf )); @@ -492,7 +488,7 @@ mod tests { assert!(!verify_proof::( &proof0.root.into(), vec![], - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf )); @@ -501,17 +497,16 @@ mod tests { #[test] fn should_generate_and_verify_proof_complex() { // given - sp_tracing::init_for_tests(); let data = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - for l in 0..data.len() { + 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 )); @@ -521,7 +516,6 @@ mod tests { #[test] fn should_generate_and_verify_proof_large() { // given - sp_tracing::init_for_tests(); let mut data = vec![]; for i in 1..16 { for c in 'a'..'z' { @@ -530,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 )); @@ -548,20 +542,19 @@ mod tests { #[test] fn should_generate_and_verify_proof_large_tree() { // given - sp_tracing::init_for_tests(); let mut data = vec![]; for i in 0..6000 { data.push(format!("{}", i)); } - 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 )); @@ -571,7 +564,6 @@ mod tests { #[test] #[should_panic] fn should_panic_on_invalid_leaf_index() { - sp_tracing::init_for_tests(); merkle_proof::(vec!["a"], 5); } @@ -756,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, @@ -797,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/frame/benchmarking-cli/src/machine/hardware.rs b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs index 555e848f8ccc648cdb972213ab266c3cedb7a234..ee1d490b854790087953865fee76da76db221d5c 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -51,17 +51,36 @@ mod tests { assert_eq!( *SUBSTRATE_REFERENCE_HARDWARE, Requirements(vec![ - Requirement { metric: Metric::Blake2256, minimum: Throughput::from_mibs(1000.00) }, + Requirement { + metric: Metric::Blake2256, + minimum: Throughput::from_mibs(1000.00), + validator_only: false + }, + Requirement { + metric: Metric::Blake2256Parallel { num_cores: 8 }, + minimum: Throughput::from_mibs(1000.00), + validator_only: true, + }, Requirement { metric: Metric::Sr25519Verify, minimum: Throughput::from_kibs(637.619999744), + validator_only: false }, Requirement { metric: Metric::MemCopy, minimum: Throughput::from_gibs(11.4925205078125003), + validator_only: false, + }, + Requirement { + metric: Metric::DiskSeqWrite, + minimum: Throughput::from_mibs(950.0), + validator_only: false, + }, + Requirement { + metric: Metric::DiskRndWrite, + minimum: Throughput::from_mibs(420.0), + validator_only: false }, - Requirement { metric: Metric::DiskSeqWrite, minimum: Throughput::from_mibs(950.0) }, - Requirement { metric: Metric::DiskRndWrite, minimum: Throughput::from_mibs(420.0) }, ]) ); } diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs index 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 cfbbab4df7ca1fabafdefc906ee03d7a37cb65ee..47191981520630a8be7e7a5329b1da8bb8444ce6 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -243,7 +243,7 @@ impl PalletCmd { let state = &state_without_tracking; let runtime = self.runtime_blob(&state_without_tracking)?; let runtime_code = runtime.code()?; - let alloc_strategy = Self::alloc_strategy(runtime_code.heap_pages); + let alloc_strategy = self.alloc_strategy(runtime_code.heap_pages); let executor = WasmExecutor::<( sp_io::SubstrateHostFunctions, @@ -753,9 +753,9 @@ impl PalletCmd { } /// Allocation strategy for pallet benchmarking. - fn alloc_strategy(heap_pages: Option) -> HeapAllocStrategy { - heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { - extra_pages: p as _, + fn alloc_strategy(&self, runtime_heap_pages: Option) -> HeapAllocStrategy { + self.heap_pages.or(runtime_heap_pages).map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| { + HeapAllocStrategy::Static { extra_pages: p as _ } }) } diff --git a/substrate/utils/frame/omni-bencher/Cargo.toml b/substrate/utils/frame/omni-bencher/Cargo.toml index 89ef2a48e01dd4e89b0dc9d6a63d8e1cb0ef0be0..e2ffca8b47146bf5e9b7e49ca13db189bf70035e 100644 --- a/substrate/utils/frame/omni-bencher/Cargo.toml +++ b/substrate/utils/frame/omni-bencher/Cargo.toml @@ -6,6 +6,7 @@ authors.workspace = true edition.workspace = true repository.workspace = true license.workspace = true +readme = "README.md" [lints] workspace = true @@ -17,5 +18,5 @@ frame-benchmarking-cli = { workspace = true } sc-cli = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } sp-statement-store = { workspace = true, default-features = true } -sp-tracing = { workspace = true } +tracing-subscriber = { workspace = true } log = { workspace = true } diff --git a/substrate/utils/frame/omni-bencher/README.md b/substrate/utils/frame/omni-bencher/README.md new file mode 100644 index 0000000000000000000000000000000000000000..29bfaeb6450bdecaa986427fb65acc2448d9bd3b --- /dev/null +++ b/substrate/utils/frame/omni-bencher/README.md @@ -0,0 +1,60 @@ +# Polkadot Omni Benchmarking CLI + +The Polkadot Omni benchmarker allows to benchmark the extrinsics of any Polkadot runtime. It is +meant to replace the current manual integration of the `benchmark pallet` into every parachain node. +This reduces duplicate code and makes maintenance for builders easier. The CLI is currently only +able to benchmark extrinsics. In the future it is planned to extend this to some other areas. + +General FRAME runtimes could also be used with this benchmarker, as long as they don't utilize any +host functions that are not part of the Polkadot host specification. + +## Installation + +Directly via crates.io: + +```sh +cargo install frame-omni-bencher --profile=production +``` + +from GitHub: + +```sh +cargo install --git https://github.com/paritytech/polkadot-sdk frame-omni-bencher --profile=production +``` + +or locally from the sources: + +```sh +cargo install --path substrate/utils/frame/omni-bencher --profile=production +``` + +Check the installed version and print the docs: + +```sh +frame-omni-bencher --help +``` + +## Usage + +First we need to ensure that there is a runtime available. As example we will build the Westend +runtime: + +```sh +cargo build -p westend-runtime --profile production --features runtime-benchmarks +``` + +Now as an example, we benchmark the `balances` pallet: + +```sh +frame-omni-bencher v1 benchmark pallet \ +--runtime target/release/wbuild/westend-runtime/westend-runtime.compact.compressed.wasm \ +--pallet "pallet_balances" --extrinsic "" +``` + +The `--steps`, `--repeat`, `--heap-pages` and `--wasm-execution` arguments have sane defaults and do +not need be passed explicitly anymore. + +## Backwards Compatibility + +The exposed pallet sub-command is identical as the node-integrated CLI. The only difference is that +it needs to be prefixed with a `v1` to ensure drop-in compatibility. diff --git a/substrate/utils/frame/omni-bencher/src/command.rs b/substrate/utils/frame/omni-bencher/src/command.rs index f0159f4307d6108f54be028c2a5bd10773aa3a79..19177ed549b786092bd9f41b8f7bdaff0a1e5122 100644 --- a/substrate/utils/frame/omni-bencher/src/command.rs +++ b/substrate/utils/frame/omni-bencher/src/command.rs @@ -36,13 +36,19 @@ use sp_runtime::traits::BlakeTwo256; /// Directly via crates.io: /// /// ```sh -/// cargo install --locked frame-omni-bencher +/// cargo install frame-omni-bencher --profile=production /// ``` /// -/// or when the sources are locally checked out: +/// from GitHub: /// /// ```sh -/// cargo install --locked --path substrate/utils/frame/omni-bencher --profile=production +/// cargo install --git https://github.com/paritytech/polkadot-sdk frame-omni-bencher --profile=production +/// ``` +/// +/// or locally from the sources: +/// +/// ```sh +/// cargo install --path substrate/utils/frame/omni-bencher --profile=production /// ``` /// /// Check the installed version and print the docs: @@ -60,7 +66,7 @@ use sp_runtime::traits::BlakeTwo256; /// cargo build -p westend-runtime --profile production --features runtime-benchmarks /// ``` /// -/// Now as example we benchmark `pallet_balances`: +/// Now as an example, we benchmark the `balances` pallet: /// /// ```sh /// frame-omni-bencher v1 benchmark pallet \ diff --git a/substrate/utils/frame/omni-bencher/src/main.rs b/substrate/utils/frame/omni-bencher/src/main.rs index a8893b5a79af024bb5ea3ae474e67c4f2e403cd3..ef3450add8e400ec5c77cfd7d5a05e6ee1581b13 100644 --- a/substrate/utils/frame/omni-bencher/src/main.rs +++ b/substrate/utils/frame/omni-bencher/src/main.rs @@ -19,10 +19,22 @@ mod command; use clap::Parser; use sc_cli::Result; +use tracing_subscriber::EnvFilter; fn main() -> Result<()> { - sp_tracing::try_init_simple(); + setup_logger(); + log::warn!("The FRAME omni-bencher is not yet battle tested - double check the results.",); command::Command::parse().run() } + +/// Setup logging with `info` as default level. Can be set via `RUST_LOG` env. +fn setup_logger() { + let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); + + tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_writer(std::io::stderr) + .init(); +} diff --git a/substrate/utils/frame/remote-externalities/src/lib.rs b/substrate/utils/frame/remote-externalities/src/lib.rs index 40864085349b090a947743cfa32016e1bb016fc7..955e79008c8cac81cfbe5ef409d30c93eaa8dec6 100644 --- a/substrate/utils/frame/remote-externalities/src/lib.rs +++ b/substrate/utils/frame/remote-externalities/src/lib.rs @@ -55,7 +55,7 @@ type ChildKeyValues = Vec<(ChildInfo, Vec)>; type SnapshotVersion = Compact; const LOG_TARGET: &str = "remote-ext"; -const DEFAULT_HTTP_ENDPOINT: &str = "https://polkadot-try-runtime-node.parity-chains.parity.io:443"; +const DEFAULT_HTTP_ENDPOINT: &str = "https://try-runtime.polkadot.io:443"; const SNAPSHOT_VERSION: SnapshotVersion = Compact(4); /// The snapshot that we store on disk. diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs index c0333bb7dac0f19ed30adc108845f88c918c900f..c455d8d39b7dcd6627da34f17208d0e79f90a1de 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs @@ -21,8 +21,9 @@ use jsonrpsee::{ core::RpcResult, proc_macros::rpc, types::error::{ErrorCode, ErrorObject, ErrorObjectOwned}, + Extensions, }; -use sc_rpc_api::DenyUnsafe; +use sc_rpc_api::check_if_safe; use serde::{Deserialize, Serialize}; use sp_runtime::traits::Block as BlockT; use std::sync::Arc; @@ -134,7 +135,7 @@ pub trait StateMigrationApi { /// This call is performed locally without submitting any transactions. Thus executing this /// won't change any state. Nonetheless it is a VERY costly call that should be /// only exposed to trusted peers. - #[method(name = "state_trieMigrationStatus")] + #[method(name = "state_trieMigrationStatus", with_extensions)] fn call(&self, at: Option) -> RpcResult; } @@ -142,14 +143,13 @@ pub trait StateMigrationApi { pub struct StateMigration { client: Arc, backend: Arc, - deny_unsafe: DenyUnsafe, _marker: std::marker::PhantomData<(B, BA)>, } impl StateMigration { /// Create new state migration rpc for the given reference to the client. - pub fn new(client: Arc, backend: Arc, deny_unsafe: DenyUnsafe) -> Self { - StateMigration { client, backend, deny_unsafe, _marker: Default::default() } + pub fn new(client: Arc, backend: Arc) -> Self { + StateMigration { client, backend, _marker: Default::default() } } } @@ -159,8 +159,12 @@ where C: Send + Sync + 'static + sc_client_api::HeaderBackend, BA: 'static + sc_client_api::backend::Backend, { - fn call(&self, at: Option<::Hash>) -> RpcResult { - self.deny_unsafe.check_if_safe()?; + fn call( + &self, + ext: &Extensions, + at: Option<::Hash>, + ) -> RpcResult { + check_if_safe(ext)?; let hash = at.unwrap_or_else(|| self.client.info().best_hash); let state = self.backend.state_at(hash).map_err(error_into_rpc_err)?; diff --git a/substrate/utils/frame/rpc/system/src/lib.rs b/substrate/utils/frame/rpc/system/src/lib.rs index 8cb7b785bc7c0b895182d27938d02e6a78709cb4..9fcaa53a35d8f6ed0e904d343ac609b09648fa98 100644 --- a/substrate/utils/frame/rpc/system/src/lib.rs +++ b/substrate/utils/frame/rpc/system/src/lib.rs @@ -24,9 +24,9 @@ use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, types::error::ErrorObject, + Extensions, }; -use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; use sp_api::ApiExt; use sp_block_builder::BlockBuilder; @@ -49,7 +49,7 @@ pub trait SystemApi { async fn nonce(&self, account: AccountId) -> RpcResult; /// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult. - #[method(name = "system_dryRun", aliases = ["system_dryRunAt"])] + #[method(name = "system_dryRun", aliases = ["system_dryRunAt"], with_extensions)] async fn dry_run(&self, extrinsic: Bytes, at: Option) -> RpcResult; } @@ -74,14 +74,13 @@ impl From for i32 { pub struct System { client: Arc, pool: Arc

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

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

) -> Self { + Self { client, pool, _marker: Default::default() } } } @@ -115,10 +114,12 @@ where async fn dry_run( &self, + ext: &Extensions, extrinsic: Bytes, at: Option<::Hash>, ) -> RpcResult { - self.deny_unsafe.check_if_safe()?; + sc_rpc_api::check_if_safe(ext)?; + let api = self.client.runtime_api(); let best_hash = at.unwrap_or_else(|| // If the block hash is not supplied assume the best block. @@ -217,6 +218,7 @@ mod tests { use assert_matches::assert_matches; use futures::executor::block_on; + use sc_rpc_api::DenyUnsafe; use sc_transaction_pool::BasicPool; use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, @@ -224,6 +226,18 @@ mod tests { }; use substrate_test_runtime_client::{runtime::Transfer, AccountKeyring}; + fn deny_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::Yes); + ext + } + + fn allow_unsafe() -> Extensions { + let mut ext = Extensions::new(); + ext.insert(DenyUnsafe::No); + ext + } + #[tokio::test] async fn should_return_next_nonce_for_some_account() { sp_tracing::try_init_simple(); @@ -251,7 +265,7 @@ mod tests { let ext1 = new_transaction(1); block_on(pool.submit_one(hash_of_block0, source, ext1)).unwrap(); - let accounts = System::new(client, pool, DenyUnsafe::Yes); + let accounts = System::new(client, pool); // when let nonce = accounts.nonce(AccountKeyring::Alice.into()).await; @@ -270,10 +284,10 @@ mod tests { let pool = BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone()); - let accounts = System::new(client, pool, DenyUnsafe::Yes); + let accounts = System::new(client, pool); // when - let res = accounts.dry_run(vec![].into(), None).await; + let res = accounts.dry_run(&deny_unsafe(), vec![].into(), None).await; assert_matches!(res, Err(e) => { assert!(e.message().contains("RPC call is unsafe to be called externally")); }); @@ -289,7 +303,7 @@ mod tests { let pool = BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone()); - let accounts = System::new(client, pool, DenyUnsafe::No); + let accounts = System::new(client, pool); let tx = Transfer { from: AccountKeyring::Alice.into(), @@ -300,7 +314,10 @@ mod tests { .into_unchecked_extrinsic(); // when - let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful"); + let bytes = accounts + .dry_run(&allow_unsafe(), tx.encode().into(), None) + .await + .expect("Call is successful"); // then let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap(); @@ -317,7 +334,7 @@ mod tests { let pool = BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone()); - let accounts = System::new(client, pool, DenyUnsafe::No); + let accounts = System::new(client, pool); let tx = Transfer { from: AccountKeyring::Alice.into(), @@ -328,7 +345,10 @@ mod tests { .into_unchecked_extrinsic(); // when - let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful"); + let bytes = accounts + .dry_run(&allow_unsafe(), tx.encode().into(), None) + .await + .expect("Call is successful"); // then let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap(); diff --git a/templates/minimal/Cargo.toml b/templates/minimal/Cargo.toml deleted file mode 100644 index ba96e139bcf194694aea029ca67825f948f30624..0000000000000000000000000000000000000000 --- a/templates/minimal/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "minimal-template" -description = "A minimal template built with Substrate, part of Polkadot Sdk." -version = "0.0.0" -license = "Unlicense" -authors.workspace = true -homepage.workspace = true -repository.workspace = true -edition.workspace = true -publish = false - -[dependencies] -minimal-template-node = { workspace = true } -minimal-template-runtime = { workspace = true } -pallet-minimal-template = { workspace = true, default-features = true } -polkadot-sdk-docs = { workspace = true } - -frame = { workspace = true, default-features = true } - -# How we build docs in rust-docs -simple-mermaid = "0.1.1" -docify = { workspace = true } diff --git a/templates/minimal/README.md b/templates/minimal/README.md index b556a4536089004205f497ccbda871b3e811de7c..fe1317a033c7dab4c50aa552ee2a8e14e49d882a 100644 --- a/templates/minimal/README.md +++ b/templates/minimal/README.md @@ -37,12 +37,20 @@ 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: ```sh -cargo build --package minimal-template-node --release +cargo build --release ``` 🐳 Alternatively, build the docker image: diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml index 70b24c19f8e7ce1b02f4570e5f34b974d7be0d8a..956efca34532dc896a8d3c07a9dc70611b0acdab 100644 --- a/templates/minimal/node/Cargo.toml +++ b/templates/minimal/node/Cargo.toml @@ -21,40 +21,15 @@ futures-timer = { workspace = true } jsonrpsee = { features = ["server"], workspace = true } serde_json = { workspace = true, default-features = true } -sc-cli = { workspace = true, default-features = true } -sc-executor = { workspace = true, default-features = true } -sc-network = { workspace = true, default-features = true } -sc-service = { workspace = true, default-features = true } -sc-telemetry = { workspace = true, default-features = true } -sc-transaction-pool = { workspace = true, default-features = true } -sc-transaction-pool-api = { workspace = true, default-features = true } -sc-consensus = { workspace = true, default-features = true } -sc-consensus-manual-seal = { workspace = true, default-features = true } -sc-rpc-api = { workspace = true, default-features = true } -sc-basic-authorship = { workspace = true, default-features = true } -sc-offchain = { workspace = true, default-features = true } -sc-client-api = { workspace = true, default-features = true } - -sp-timestamp = { workspace = true, default-features = true } -sp-keyring = { workspace = true, default-features = true } -sp-api = { workspace = true, default-features = true } -sp-blockchain = { workspace = true, default-features = true } -sp-block-builder = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -sp-runtime = { workspace = true, default-features = true } - -substrate-frame-rpc-system = { workspace = true, default-features = true } - -# Once the native runtime is gone, there should be little to no dependency on FRAME here, and -# certainly no dependency on the runtime. -frame = { features = [ - "experimental", - "runtime", -], workspace = true, default-features = true } +polkadot-sdk = { workspace = true, features = ["experimental", "node"] } minimal-template-runtime = { workspace = true } [build-dependencies] -substrate-build-script-utils = { workspace = true, default-features = true } +polkadot-sdk = { workspace = true, features = ["substrate-build-script-utils"] } [features] -default = [] +default = ["std"] +std = [ + "minimal-template-runtime/std", + "polkadot-sdk/std", +] diff --git a/templates/minimal/node/build.rs b/templates/minimal/node/build.rs index fa7686e01099f2aed61bb140326ed8bb1b3a85c2..47ab77ae296909e4fc85069e727455e32d3ceeec 100644 --- a/templates/minimal/node/build.rs +++ b/templates/minimal/node/build.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; +use polkadot_sdk::substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; fn main() { generate_cargo_keys(); diff --git a/templates/minimal/node/src/chain_spec.rs b/templates/minimal/node/src/chain_spec.rs index 5b53b0f80ac00ec8315f449c62283da195ab89e5..0646460acef641ffd7155a35ae6fb0dba8742ef4 100644 --- a/templates/minimal/node/src/chain_spec.rs +++ b/templates/minimal/node/src/chain_spec.rs @@ -16,9 +16,12 @@ // limitations under the License. use minimal_template_runtime::{BalancesConfig, SudoConfig, WASM_BINARY}; -use sc_service::{ChainType, Properties}; +use polkadot_sdk::{ + sc_service::{ChainType, Properties}, + sp_keyring::AccountKeyring, + *, +}; use serde_json::{json, Value}; -use sp_keyring::AccountKeyring; /// This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; @@ -42,8 +45,8 @@ pub fn development_config() -> Result { /// Configure initial storage state for FRAME pallets. fn testnet_genesis() -> Value { - use frame::traits::Get; use minimal_template_runtime::interface::{Balance, MinimumBalance}; + use polkadot_sdk::polkadot_sdk_frame::traits::Get; let endowment = >::get().max(1) * 1000; let balances = AccountKeyring::iter() .map(|a| (a.to_account_id(), endowment)) diff --git a/templates/minimal/node/src/cli.rs b/templates/minimal/node/src/cli.rs index 22726b7eb9a36c0254c2f8423086e5206b730570..54107df75a3630d818df881d600e4ce18f886286 100644 --- a/templates/minimal/node/src/cli.rs +++ b/templates/minimal/node/src/cli.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use sc_cli::RunCmd; +use polkadot_sdk::{sc_cli::RunCmd, *}; #[derive(Debug, Clone)] pub enum Consensus { diff --git a/templates/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs index 504be1ccc36eddb49cb7a3c293cb66fa6ddde9b5..b09ea1fab2379d1e675b91393204002dfd971ebf 100644 --- a/templates/minimal/node/src/command.rs +++ b/templates/minimal/node/src/command.rs @@ -20,8 +20,7 @@ use crate::{ cli::{Cli, Subcommand}, service, }; -use sc_cli::SubstrateCli; -use sc_service::PartialComponents; +use polkadot_sdk::{sc_cli::SubstrateCli, sc_service::PartialComponents, *}; impl SubstrateCli for Cli { fn impl_name() -> String { diff --git a/templates/minimal/node/src/main.rs b/templates/minimal/node/src/main.rs index 3cf7d98311eaa3cde56813326117adde409b366d..8f36da5bf83a88acc6e518389ebdc2e4f347b8ed 100644 --- a/templates/minimal/node/src/main.rs +++ b/templates/minimal/node/src/main.rs @@ -24,6 +24,6 @@ mod command; mod rpc; mod service; -fn main() -> sc_cli::Result<()> { +fn main() -> polkadot_sdk::sc_cli::Result<()> { command::run() } diff --git a/templates/minimal/node/src/rpc.rs b/templates/minimal/node/src/rpc.rs index 451e7b21dd0c1ce34ae5fa51a10271e699b56e88..64497c4bcaca9268eaa2dea7bbbc699db76a4d6a 100644 --- a/templates/minimal/node/src/rpc.rs +++ b/templates/minimal/node/src/rpc.rs @@ -24,20 +24,19 @@ use jsonrpsee::RpcModule; use minimal_template_runtime::interface::{AccountId, Nonce, OpaqueBlock}; -use sc_transaction_pool_api::TransactionPool; -use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use polkadot_sdk::{ + sc_transaction_pool_api::TransactionPool, + sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}, + *, +}; use std::sync::Arc; -pub use sc_rpc_api::DenyUnsafe; - /// Full client dependencies. pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, } #[docify::export] @@ -57,11 +56,11 @@ where C::Api: substrate_frame_rpc_system::AccountNonceApi, P: TransactionPool + 'static, { - use substrate_frame_rpc_system::{System, SystemApiServer}; + use polkadot_sdk::substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { client, pool } = deps; - module.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool.clone()).into_rpc())?; Ok(module) } diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index 44b374fcc0a40300cef9c9fe198779aad13f9fb8..a42eb10ccec652ee66b89549009a59d09d37ef6d 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -17,12 +17,15 @@ use futures::FutureExt; use minimal_template_runtime::{interface::OpaqueBlock as Block, RuntimeApi}; -use sc_client_api::backend::Backend; -use sc_executor::WasmExecutor; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; -use sc_telemetry::{Telemetry, TelemetryWorker}; -use sc_transaction_pool_api::OffchainTransactionPoolFactory; -use sp_runtime::traits::Block as BlockT; +use polkadot_sdk::{ + sc_client_api::backend::Backend, + sc_executor::WasmExecutor, + sc_service::{error::Error as ServiceError, Configuration, TaskManager}, + sc_telemetry::{Telemetry, TelemetryWorker}, + sc_transaction_pool_api::OffchainTransactionPoolFactory, + sp_runtime::traits::Block as BlockT, + *, +}; use std::sync::Arc; use crate::cli::Consensus; @@ -58,7 +61,7 @@ pub fn new_partial(config: &Configuration) -> Result { }) .transpose()?; - let executor = sc_service::new_wasm_executor(config); + let executor = sc_service::new_wasm_executor(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( @@ -138,7 +141,7 @@ pub fn new_full::Ha import_queue, net_config, block_announce_validator_builder: None, - warp_sync_params: None, + warp_sync_config: None, block_relay: None, metrics, })?; @@ -168,9 +171,8 @@ pub fn new_full::Ha let client = client.clone(); let pool = transaction_pool.clone(); - Box::new(move |deny_unsafe, _| { - let deps = - crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; + Box::new(move |_| { + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone() }; crate::rpc::create_full(deps).map_err(Into::into) }) }; diff --git a/templates/minimal/pallets/template/Cargo.toml b/templates/minimal/pallets/template/Cargo.toml index 9d231fe7d7d45d3c4d1f35bee98d2bf7a57203a7..9a02d4daeaac39323773185b67448d00cdfb7bbf 100644 --- a/templates/minimal/pallets/template/Cargo.toml +++ b/templates/minimal/pallets/template/Cargo.toml @@ -13,18 +13,14 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { features = [ - "derive", -], workspace = true } -scale-info = { features = [ - "derive", -], workspace = true } -frame = { features = [ +codec = { features = ["derive"], workspace = true } +scale-info = { features = ["derive"], workspace = true } +polkadot-sdk = { workspace = true, default-features = false, features = [ "experimental", "runtime", -], workspace = true } +] } [features] default = ["std"] -std = ["codec/std", "frame/std", "scale-info/std"] +std = ["codec/std", "polkadot-sdk/std", "scale-info/std"] diff --git a/templates/minimal/pallets/template/src/lib.rs b/templates/minimal/pallets/template/src/lib.rs index 92b90ad4412b0263eac5fc26bea7d835f932030f..b8a8614932a63aa68ccf8c510dd1d31da2ff2852 100644 --- a/templates/minimal/pallets/template/src/lib.rs +++ b/templates/minimal/pallets/template/src/lib.rs @@ -5,7 +5,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame::prelude::*; +use polkadot_sdk::polkadot_sdk_frame as frame; // Re-export all pallet parts, this is needed to properly import the pallet into the runtime. pub use pallet::*; @@ -15,7 +15,7 @@ pub mod pallet { use super::*; #[pallet::config] - pub trait Config: frame_system::Config {} + pub trait Config: polkadot_sdk::frame_system::Config {} #[pallet::pallet] pub struct Pallet(_); diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml index 5d3cf8492e5227f2af4945d328acec216f1a5752..49ddf3987e96f09171a1a6e8cc457fd292dd77ce 100644 --- a/templates/minimal/runtime/Cargo.toml +++ b/templates/minimal/runtime/Cargo.toml @@ -12,47 +12,29 @@ publish = false [dependencies] codec = { workspace = true } scale-info = { workspace = true } - -# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. -frame = { features = [ +polkadot-sdk = { workspace = true, features = [ "experimental", + "pallet-balances", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", "runtime", -], workspace = true } - -# pallets that we want to use -pallet-balances = { workspace = true } -pallet-sudo = { workspace = true } -pallet-timestamp = { workspace = true } -pallet-transaction-payment = { workspace = true } -pallet-transaction-payment-rpc-runtime-api = { workspace = true } - -# genesis builder that allows us to interact with runtime genesis config -sp-genesis-builder = { workspace = true } -sp-runtime = { features = ["serde"], workspace = true } +] } # local pallet templates pallet-minimal-template = { workspace = true } [build-dependencies] -substrate-wasm-builder = { optional = true, workspace = true, default-features = true } +polkadot-sdk = { optional = true, workspace = true, features = [ + "substrate-wasm-builder", +] } [features] default = ["std"] std = [ "codec/std", - "scale-info/std", - - "frame/std", - - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "pallet-minimal-template/std", - - "sp-genesis-builder/std", - "sp-runtime/std", - "substrate-wasm-builder", + "polkadot-sdk/std", + "scale-info/std", ] diff --git a/templates/minimal/runtime/build.rs b/templates/minimal/runtime/build.rs index e6f92757e225475ff744a4a0cf931787463d1544..2cb2966b5d822b91b4bebca0b545a6e7fc7eaeda 100644 --- a/templates/minimal/runtime/build.rs +++ b/templates/minimal/runtime/build.rs @@ -18,6 +18,6 @@ fn main() { #[cfg(feature = "std")] { - substrate_wasm_builder::WasmBuilder::build_using_defaults(); + polkadot_sdk::substrate_wasm_builder::WasmBuilder::build_using_defaults(); } } diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 08ad537ecdd1487586f919bcce63fa259946e738..cce13c48af71a145d8458ce49a6d82692de3dd0e 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -26,20 +26,14 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); extern crate alloc; use alloc::{vec, vec::Vec}; -use frame::{ - deps::frame_support::{ - genesis_builder_helper::{build_state, get_preset}, - runtime, - weights::{FixedFee, NoFee}, - }, - prelude::*, - runtime::{ - apis::{ - self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, - ExtrinsicInclusionMode, OpaqueMetadata, - }, +use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; +use polkadot_sdk::{ + polkadot_sdk_frame::{ + self as frame, prelude::*, + runtime::{apis, prelude::*}, }, + *, }; /// The runtime version. @@ -52,7 +46,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -83,7 +77,7 @@ type SignedExtra = ( ); // Composes the runtime by adding all the used pallets and deriving necessary types. -#[runtime] +#[frame_construct_runtime] mod runtime { /// The main runtime type. #[runtime::runtime] @@ -171,8 +165,6 @@ type Header = HeaderFor; type RuntimeExecutive = Executive, Runtime, AllPalletsWithSystem>; -use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; - impl_runtime_apis! { impl apis::Core for Runtime { fn version() -> RuntimeVersion { @@ -296,7 +288,7 @@ impl_runtime_apis! { // https://github.com/paritytech/substrate/issues/10579#issuecomment-1600537558 pub mod interface { use super::Runtime; - use frame::deps::frame_system; + use polkadot_sdk::{polkadot_sdk_frame as frame, *}; pub type Block = super::Block; pub use frame::runtime::types_common::OpaqueBlock; diff --git a/templates/minimal/src/lib.rs b/templates/minimal/src/lib.rs deleted file mode 100644 index 68825d190bb2c0d7862cd8552c0fe211615ec60a..0000000000000000000000000000000000000000 --- a/templates/minimal/src/lib.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! # Minimal Template -//! -//! This is a minimal template for creating a blockchain using the Polkadot SDK. -//! -//! ## Components -//! -//! The template consists of the following components: -//! -//! ### Node -//! -//! A minimal blockchain [`node`](`minimal_template_node`) that is capable of running a -//! runtime. It uses a simple chain specification, provides an option to choose Manual or -//! InstantSeal for consensus and exposes a few commands to interact with the node. -//! -//! ### Runtime -//! -//! A minimal [`runtime`](`minimal_template_runtime`) (or a state transition function) that -//! is capable of being run on the node. It is built using the [`FRAME`](`frame`) framework -//! that enables the composition of the core logic via separate modules called "pallets". -//! FRAME defines a complete DSL for building such pallets and the runtime itself. -//! -//! #### Transaction Fees -//! -//! The runtime charges a transaction fee for every transaction that is executed. The fee is -//! calculated based on the weight of the transaction (accouting for the execution time) and -//! length of the call data. Please refer to -//! [`benchmarking docs`](`polkadot_sdk_docs::reference_docs::frame_benchmarking_weight`) for -//! more information on how the weight is calculated. -//! -//! This template sets the fee as independent of the weight of the extrinsic and fixed for any -//! length of the call data for demo purposes. -//! -//! ### Pallet -//! -//! A minimal [`pallet`](`pallet_minimal_template`) that is built using FRAME. It is a unit of -//! encapsulated logic that has a clearly defined responsibility and can be linked to other pallets. -//! -//! ## Getting Started -//! -//! To get started with the template, follow the steps below: -//! -//! ### Build the Node -//! -//! Build the node using the following command: -//! -//! ```bash -//! cargo build -p minimal-template-node --release -//! ``` -//! -//! ### Run the Node -//! -//! Run the node using the following command: -//! -//! ```bash -//! ./target/release/minimal-template-node --dev -//! ``` -//! -//! ### CLI Options -//! -//! The node exposes a few options that can be used to interact with the node. To see the list of -//! available options, run the following command: -//! -//! ```bash -//! ./target/release/minimal-template-node --help -//! ``` -//! -//! #### Consensus Algorithm -//! -//! In order to run the node with a specific consensus algorithm, use the `--consensus` flag. For -//! example, to run the node with ManualSeal consensus with a block time of 5000ms, use the -//! following command: -//! -//! ```bash -//! ./target/release/minimal-template-node --dev --consensus manual-seal-5000 -//! ``` diff --git a/templates/parachain/README.md b/templates/parachain/README.md index 802d8586b39e091b67c45f86226743552d4e0a70..3de85cbeb4dc77a386fc702f645c0bb4e7ffb96d 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -39,12 +39,20 @@ 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: ```sh -cargo build --package parachain-template-node --release +cargo build --release ``` 🐳 Alternatively, build the docker image: @@ -59,15 +67,22 @@ docker build . -t polkadot-sdk-parachain-template You can grab a [released binary](https://github.com/paritytech/zombienet/releases/latest) or use an [npm version](https://www.npmjs.com/package/@zombienet/cli). This template produces a parachain node. +You can install it in your environment by running: + +```sh +cargo install --path node +``` + You still need a relaychain node - you can download the `polkadot` (and the accompanying `polkadot-prepare-worker` and `polkadot-execute-worker`) binaries from [Polkadot SDK releases](https://github.com/paritytech/polkadot-sdk/releases/latest). -Make sure to bring the parachain node - as well as `polkadot`, `polkadot-prepare-worker`, `polkadot-execute-worker`, -and `zombienet` - into `PATH` like so: +In addition to the installed parachain node, make sure to bring +`zombienet`, `polkadot`, `polkadot-prepare-worker`, and `polkadot-execute-worker` +into `PATH`, for example: ```sh -export PATH="./target/release/:$PATH" +export PATH=":$PATH" ``` This way, we can conveniently use them in the following steps. diff --git a/templates/parachain/node/Cargo.toml b/templates/parachain/node/Cargo.toml index 7cf1f1fddc7b31e4adbb69f7e54f35cbcad28399..c782888a3e8913e7ec5bbbe831d7ac32093315e3 100644 --- a/templates/parachain/node/Cargo.toml +++ b/templates/parachain/node/Cargo.toml @@ -79,7 +79,12 @@ color-print = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "log/std", + "parachain-template-runtime/std", + "xcm/std", +] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", diff --git a/templates/parachain/node/src/chain_spec.rs b/templates/parachain/node/src/chain_spec.rs index 3fa91c0261622cd71dbf2b473eb70748f3a29b96..cd02bca466ff2f2fd8e0371f6730877f94e33ade 100644 --- a/templates/parachain/node/src/chain_spec.rs +++ b/templates/parachain/node/src/chain_spec.rs @@ -1,25 +1,11 @@ -use cumulus_primitives_core::ParaId; use parachain_template_runtime as runtime; -use runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec` for the normal parachain runtime. pub type ChainSpec = sc_service::GenericChainSpec; -/// The default XCM version to set in genesis config. -const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; - -/// Helper function to generate a crypto pair from seed -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - /// The extensions for the [`ChainSpec`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] pub struct Extensions { @@ -38,30 +24,6 @@ impl Extensions { } } -type AccountPublic = ::Signer; - -/// Generate collator keys from seed. -/// -/// This function's return type must always match the session keys of the chain in tuple format. -pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { - get_from_seed::(seed) -} - -/// Helper function to generate an account ID from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn template_session_keys(keys: AuraId) -> runtime::SessionKeys { - runtime::SessionKeys { aura: keys } -} - pub fn development_config() -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); @@ -80,35 +42,7 @@ pub fn development_config() -> ChainSpec { .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - )) + .with_genesis_config_preset_name("development") .build() } @@ -131,72 +65,8 @@ pub fn local_testnet_config() -> ChainSpec { .with_name("Local Testnet") .with_id("local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - )) + .with_genesis_config_preset_name("local_testnet") .with_protocol_id("template-local") .with_properties(properties) .build() } - -fn testnet_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - root: AccountId, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": EXISTENTIAL_DEPOSIT * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - template_session_keys(aura), // session keys - ) - }) - .collect::>(), - }, - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "sudo": { "key": Some(root) } - }) -} diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index eba7fdcdae7185186f2ec12f27efec5b1fca720b..610dbd7a686a9c5ac875a0b76a59b35767c97bad 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -1,5 +1,3 @@ -use std::net::SocketAddr; - use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; @@ -7,7 +5,7 @@ use log::info; use parachain_template_runtime::Block; use sc_cli::{ ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, - NetworkParams, Result, SharedParams, SubstrateCli, + NetworkParams, Result, RpcEndpoint, SharedParams, SubstrateCli, }; use sc_service::config::{BasePath, PrometheusConfig}; @@ -222,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(); @@ -297,7 +298,7 @@ impl CliConfiguration for RelayChainCli { .or_else(|| self.base_path.clone().map(Into::into))) } - fn rpc_addr(&self, default_listen_port: u16) -> Result> { + fn rpc_addr(&self, default_listen_port: u16) -> Result>> { self.base.base.rpc_addr(default_listen_port) } @@ -309,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/rpc.rs b/templates/parachain/node/src/rpc.rs index bb52b974f0ce61713904aec3783770dbc8f95aad..4937469e11e2d1b743940a633a2b979853abad5e 100644 --- a/templates/parachain/node/src/rpc.rs +++ b/templates/parachain/node/src/rpc.rs @@ -9,7 +9,6 @@ use std::sync::Arc; use parachain_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; -pub use sc_rpc::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; @@ -24,8 +23,6 @@ pub struct FullDeps { pub client: Arc, /// Transaction pool instance. pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, } /// Instantiate all RPC extensions. @@ -48,9 +45,9 @@ where use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcExtension::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { client, pool } = deps; - module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; Ok(module) } diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index 88f2710a5cb814a759e2c9d48a9f4c5a1a444414..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) = @@ -305,12 +306,9 @@ pub async fn start_parachain_node( let client = client.clone(); let transaction_pool = transaction_pool.clone(); - Box::new(move |deny_unsafe, _| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: transaction_pool.clone(), - deny_unsafe, - }; + Box::new(move |_| { + let deps = + crate::rpc::FullDeps { client: client.clone(), pool: transaction_pool.clone() }; crate::rpc::create_full(deps).map_err(Into::into) }) @@ -336,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/runtime/Cargo.toml b/templates/parachain/runtime/Cargo.toml index 939fa245d2a0c1b6e03ecd0541a2b9aa63af75bd..45c77d18e8167ba2a63e5549e4e8019f0887360d 100644 --- a/templates/parachain/runtime/Cargo.toml +++ b/templates/parachain/runtime/Cargo.toml @@ -27,6 +27,7 @@ scale-info = { features = [ ], workspace = true } smallvec = { workspace = true, default-features = true } docify = { workspace = true } +serde_json = { workspace = true, default-features = false } # Local pallet-parachain-template = { workspace = true } @@ -126,6 +127,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", + "serde_json/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs index 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/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..80b763d5bd854c81234645de161982db63e7d299 --- /dev/null +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -0,0 +1,169 @@ +use cumulus_primitives_core::ParaId; + +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; + +use crate::{AccountId, SessionKeys, Signature, EXISTENTIAL_DEPOSIT}; +use alloc::{format, vec, vec::Vec}; +use serde_json::Value; +use sp_core::{sr25519, Pair, Public}; +use sp_genesis_builder::PresetId; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// Preset configuration name for a local testnet environment. +pub const PRESET_LOCAL_TESTNET: &str = "local_testnet"; + +type AccountPublic = ::Signer; + +/// The default XCM version to set in genesis config. +const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { + get_from_seed::(seed) +} + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn template_session_keys(keys: AuraId) -> SessionKeys { + SessionKeys { aura: keys } +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + root: AccountId, + id: ParaId, +) -> Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), + }, + "parachainInfo": { + "parachainId": id, + }, + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": EXISTENTIAL_DEPOSIT * 16, + }, + "session": { + "keys": invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + template_session_keys(aura), // session keys + ) + }) + .collect::>(), + }, + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), + }, + "sudo": { "key": Some(root) } + }) +} + +fn local_testnet_genesis() -> Value { + testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + ) +} + +fn development_config_genesis() -> Value { + testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + ) +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(PRESET_LOCAL_TESTNET) => local_testnet_genesis(), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(PRESET_LOCAL_TESTNET), + ] +} diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index 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/parachain/zombienet.toml b/templates/parachain/zombienet.toml index 336ba1af4dde56a78208e555fdad1d732696db27..c47a4bdeb6ad7e386c02a0f4d2b8f84aed66b76f 100644 --- a/templates/parachain/zombienet.toml +++ b/templates/parachain/zombienet.toml @@ -1,16 +1,21 @@ [relaychain] default_command = "polkadot" -chain = "dev" +chain = "rococo-local" [[relaychain.nodes]] name = "alice" validator = true ws_port = 9944 +[[relaychain.nodes]] +name = "bob" +validator = true +ws_port = 9955 + [[parachains]] id = 1000 [parachains.collator] -name = "alice" +name = "charlie" ws_port = 9988 command = "parachain-template-node" diff --git a/templates/solochain/README.md b/templates/solochain/README.md index c5dc5db7f3b51c65fda0a6f74bb56a424e4e384e..c4ce5c7f3fbb4069f24586a1739735d0ca304dda 100644 --- a/templates/solochain/README.md +++ b/templates/solochain/README.md @@ -23,12 +23,20 @@ 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 --package solochain-template-node --release +cargo build --release ``` ### Embedded Docs diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 6eebf3694e3a19a5a1eeba31195985c697b0c910..8f74c6b3cb55bc190616292458e847002faf440b 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -36,7 +36,6 @@ sc-consensus = { workspace = true, default-features = true } sc-consensus-grandpa = { workspace = true, default-features = true } sp-consensus-grandpa = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } -sc-rpc-api = { workspace = true, default-features = true } sc-basic-authorship = { workspace = true, default-features = true } # substrate primitives @@ -66,7 +65,10 @@ solochain-template-runtime = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "solochain-template-runtime/std", +] # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", diff --git a/templates/solochain/node/src/rpc.rs b/templates/solochain/node/src/rpc.rs index fe2b6ca72ede5c8d9f8d878db88efa2da5c5ac97..1fc6eb0127e925b77baf4e089dab658b91ef96fc 100644 --- a/templates/solochain/node/src/rpc.rs +++ b/templates/solochain/node/src/rpc.rs @@ -14,16 +14,12 @@ use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; -pub use sc_rpc_api::DenyUnsafe; - /// Full client dependencies. pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, } /// Instantiate all full RPC extensions. @@ -43,9 +39,9 @@ where use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); - let FullDeps { client, pool, deny_unsafe } = deps; + let FullDeps { client, pool } = deps; - module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; // Extend this RPC with a custom API by using the following syntax. diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 5e84552f4ccdc942b84a6756949963f908195e7f..7d37c5ce87f8cd10f2103359f4c3a06b788b1648 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -4,7 +4,7 @@ use futures::FutureExt; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncConfig}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use solochain_template_runtime::{self, apis::RuntimeApi, opaque::Block}; @@ -48,7 +48,7 @@ pub fn new_partial(config: &Configuration) -> Result { }) .transpose()?; - let executor = sc_service::new_wasm_executor::(config); + let executor = sc_service::new_wasm_executor::(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( config, @@ -175,7 +175,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; @@ -201,7 +201,7 @@ pub fn new_full< ); } - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks: Option<()> = None; let name = config.network.node_name.clone(); @@ -212,9 +212,8 @@ pub fn new_full< let client = client.clone(); let pool = transaction_pool.clone(); - Box::new(move |deny_unsafe, _| { - let deps = - crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; + Box::new(move |_| { + let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone() }; crate::rpc::create_full(deps).map_err(Into::into) }) }; diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 6cbfbb879602e8cef718481ed199919eb49270e6..ce38c65479e5abc77017e54339f15a2cad529720 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -71,7 +71,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; mod block_times { diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 65ff9a81e474783192d958ed39fa1a3f75100583..b7c1c375094fff9a51f00b3137dbaa76850576b8 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -120,6 +120,9 @@ std = [ "pallet-recovery?/std", "pallet-referenda?/std", "pallet-remark?/std", + "pallet-revive-fixtures?/std", + "pallet-revive-mock-network?/std", + "pallet-revive?/std", "pallet-root-offences?/std", "pallet-root-testing?/std", "pallet-safe-mode?/std", @@ -304,6 +307,8 @@ runtime-benchmarks = [ "pallet-recovery?/runtime-benchmarks", "pallet-referenda?/runtime-benchmarks", "pallet-remark?/runtime-benchmarks", + "pallet-revive-mock-network?/runtime-benchmarks", + "pallet-revive?/runtime-benchmarks", "pallet-root-offences?/runtime-benchmarks", "pallet-safe-mode?/runtime-benchmarks", "pallet-salary?/runtime-benchmarks", @@ -330,6 +335,7 @@ runtime-benchmarks = [ "parachains-common?/runtime-benchmarks", "polkadot-cli?/runtime-benchmarks", "polkadot-node-metrics?/runtime-benchmarks", + "polkadot-parachain-lib?/runtime-benchmarks", "polkadot-parachain-primitives?/runtime-benchmarks", "polkadot-primitives?/runtime-benchmarks", "polkadot-runtime-common?/runtime-benchmarks", @@ -432,6 +438,7 @@ try-runtime = [ "pallet-recovery?/try-runtime", "pallet-referenda?/try-runtime", "pallet-remark?/try-runtime", + "pallet-revive?/try-runtime", "pallet-root-offences?/try-runtime", "pallet-root-testing?/try-runtime", "pallet-safe-mode?/try-runtime", @@ -459,6 +466,7 @@ try-runtime = [ "pallet-xcm-bridge-hub?/try-runtime", "pallet-xcm?/try-runtime", "polkadot-cli?/try-runtime", + "polkadot-parachain-lib?/try-runtime", "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains?/try-runtime", "polkadot-sdk-frame?/try-runtime", @@ -484,6 +492,7 @@ serde = [ "pallet-parameters?/serde", "pallet-referenda?/serde", "pallet-remark?/serde", + "pallet-revive?/serde", "pallet-state-trie-migration?/serde", "pallet-tips?/serde", "pallet-transaction-payment?/serde", @@ -527,37 +536,8 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ - "assets-common", - "binary-merkle-tree", - "bp-header-chain", - "bp-messages", - "bp-parachains", - "bp-polkadot", - "bp-polkadot-core", - "bp-relayers", - "bp-runtime", - "bp-test-utils", - "bp-xcm-bridge-hub", - "bp-xcm-bridge-hub-router", - "bridge-hub-common", - "bridge-runtime-common", - "cumulus-pallet-aura-ext", - "cumulus-pallet-dmp-queue", - "cumulus-pallet-parachain-system", - "cumulus-pallet-parachain-system-proc-macro", - "cumulus-pallet-session-benchmarking", - "cumulus-pallet-solo-to-para", - "cumulus-pallet-xcm", - "cumulus-pallet-xcmp-queue", - "cumulus-ping", - "cumulus-primitives-aura", - "cumulus-primitives-core", - "cumulus-primitives-parachain-inherent", - "cumulus-primitives-proof-size-hostfunction", - "cumulus-primitives-storage-weight-reclaim", - "cumulus-primitives-timestamp", - "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", @@ -571,134 +551,8 @@ runtime = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "pallet-alliance", - "pallet-asset-conversion", - "pallet-asset-conversion-ops", - "pallet-asset-conversion-tx-payment", - "pallet-asset-rate", - "pallet-asset-tx-payment", - "pallet-assets", - "pallet-assets-freezer", - "pallet-atomic-swap", - "pallet-aura", - "pallet-authority-discovery", - "pallet-authorship", - "pallet-babe", - "pallet-bags-list", - "pallet-balances", - "pallet-beefy", - "pallet-beefy-mmr", - "pallet-bounties", - "pallet-bridge-grandpa", - "pallet-bridge-messages", - "pallet-bridge-parachains", - "pallet-bridge-relayers", - "pallet-broker", - "pallet-child-bounties", - "pallet-collator-selection", - "pallet-collective", - "pallet-collective-content", - "pallet-contracts", - "pallet-contracts-proc-macro", - "pallet-contracts-uapi", - "pallet-conviction-voting", - "pallet-core-fellowship", - "pallet-delegated-staking", - "pallet-democracy", - "pallet-dev-mode", - "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking", - "pallet-elections-phragmen", - "pallet-fast-unstake", - "pallet-glutton", - "pallet-grandpa", - "pallet-identity", - "pallet-im-online", - "pallet-indices", - "pallet-insecure-randomness-collective-flip", - "pallet-lottery", - "pallet-membership", - "pallet-message-queue", - "pallet-migrations", - "pallet-mixnet", - "pallet-mmr", - "pallet-multisig", - "pallet-nft-fractionalization", - "pallet-nfts", - "pallet-nfts-runtime-api", - "pallet-nis", - "pallet-node-authorization", - "pallet-nomination-pools", - "pallet-nomination-pools-benchmarking", - "pallet-nomination-pools-runtime-api", - "pallet-offences", - "pallet-offences-benchmarking", - "pallet-paged-list", - "pallet-parameters", - "pallet-preimage", - "pallet-proxy", - "pallet-ranked-collective", - "pallet-recovery", - "pallet-referenda", - "pallet-remark", - "pallet-root-offences", - "pallet-root-testing", - "pallet-safe-mode", - "pallet-salary", - "pallet-scheduler", - "pallet-scored-pool", - "pallet-session", - "pallet-session-benchmarking", - "pallet-skip-feeless-payment", - "pallet-society", - "pallet-staking", - "pallet-staking-reward-curve", - "pallet-staking-reward-fn", - "pallet-staking-runtime-api", - "pallet-state-trie-migration", - "pallet-statement", - "pallet-sudo", - "pallet-timestamp", - "pallet-tips", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", - "pallet-transaction-storage", - "pallet-treasury", - "pallet-tx-pause", - "pallet-uniques", - "pallet-utility", - "pallet-vesting", - "pallet-whitelist", - "pallet-xcm", - "pallet-xcm-benchmarks", - "pallet-xcm-bridge-hub", - "pallet-xcm-bridge-hub-router", - "parachains-common", - "polkadot-core-primitives", - "polkadot-parachain-primitives", - "polkadot-primitives", - "polkadot-runtime-common", - "polkadot-runtime-metrics", - "polkadot-runtime-parachains", "polkadot-sdk-frame", "polkadot-sdk-frame?/runtime", - "sc-chain-spec-derive", - "sc-tracing-proc-macro", - "slot-range-helper", - "snowbridge-beacon-primitives", - "snowbridge-core", - "snowbridge-ethereum", - "snowbridge-outbound-queue-merkle-tree", - "snowbridge-outbound-queue-runtime-api", - "snowbridge-pallet-ethereum-client", - "snowbridge-pallet-ethereum-client-fixtures", - "snowbridge-pallet-inbound-queue", - "snowbridge-pallet-inbound-queue-fixtures", - "snowbridge-pallet-outbound-queue", - "snowbridge-pallet-system", - "snowbridge-router-primitives", - "snowbridge-runtime-common", - "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", @@ -745,21 +599,17 @@ runtime = [ "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", - "staging-parachain-info", - "staging-xcm", - "staging-xcm-builder", - "staging-xcm-executor", - "substrate-bip39", - "testnet-parachains-constants", - "tracing-gum-proc-macro", - "xcm-procedural", - "xcm-runtime-apis", ] -node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] +node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", "frame-support?/tuples-96", ] +riscv = [ + "pallet-revive-fixtures?/riscv", + "pallet-revive-mock-network?/riscv", + "pallet-revive?/riscv", +] [package.edition] workspace = true @@ -1332,6 +1182,26 @@ path = "../substrate/frame/remark" default-features = false optional = true +[dependencies.pallet-revive] +path = "../substrate/frame/revive" +default-features = false +optional = true + +[dependencies.pallet-revive-fixtures] +path = "../substrate/frame/revive/fixtures" +default-features = false +optional = true + +[dependencies.pallet-revive-proc-macro] +path = "../substrate/frame/revive/proc-macro" +default-features = false +optional = true + +[dependencies.pallet-revive-uapi] +path = "../substrate/frame/revive/uapi" +default-features = false +optional = true + [dependencies.pallet-root-offences] path = "../substrate/frame/root-offences" default-features = false @@ -2022,6 +1892,11 @@ path = "../substrate/frame/contracts/mock-network" default-features = false optional = true +[dependencies.pallet-revive-mock-network] +path = "../substrate/frame/revive/mock-network" +default-features = false +optional = true + [dependencies.pallet-transaction-payment-rpc] path = "../substrate/frame/transaction-payment/rpc" default-features = false @@ -2212,6 +2087,11 @@ path = "../polkadot/node/overseer" default-features = false optional = true +[dependencies.polkadot-parachain-lib] +path = "../cumulus/polkadot-parachain/polkadot-parachain-lib" +default-features = false +optional = true + [dependencies.polkadot-rpc] path = "../polkadot/rpc" default-features = false @@ -2608,5 +2488,5 @@ default-features = false optional = true [package.metadata.docs.rs] -features = ["node", "runtime"] +features = ["node", "runtime-full"] targets = ["x86_64-unknown-linux-gnu"] diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 07f1cbad12620ebd3d18af9e5df8009ed407ada1..b7b9c15fe5880f5c631355155312a588f10d2561 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -109,7 +109,7 @@ pub use cumulus_client_network; #[cfg(feature = "cumulus-client-parachain-inherent")] pub use cumulus_client_parachain_inherent; -/// Cumulus-specific networking protocol. +/// Parachain PoV recovery. #[cfg(feature = "cumulus-client-pov-recovery")] pub use cumulus_client_pov_recovery; @@ -272,7 +272,7 @@ pub use frame_system_benchmarking; #[cfg(feature = "frame-system-rpc-runtime-api")] pub use frame_system_rpc_runtime_api; -/// FRAME pallet for democracy. +/// Supporting types for try-runtime, testing and dry-running commands. #[cfg(feature = "frame-try-runtime")] pub use frame_try_runtime; @@ -576,6 +576,26 @@ pub use pallet_referenda; #[cfg(feature = "pallet-remark")] pub use pallet_remark; +/// FRAME pallet for PolkaVM contracts. +#[cfg(feature = "pallet-revive")] +pub use pallet_revive; + +/// Fixtures for testing and benchmarking. +#[cfg(feature = "pallet-revive-fixtures")] +pub use pallet_revive_fixtures; + +/// A mock network for testing pallet-revive. +#[cfg(feature = "pallet-revive-mock-network")] +pub use pallet_revive_mock_network; + +/// Procedural macros used in pallet_revive. +#[cfg(feature = "pallet-revive-proc-macro")] +pub use pallet_revive_proc_macro; + +/// Exposes all the host functions that a contract can import. +#[cfg(feature = "pallet-revive-uapi")] +pub use pallet_revive_uapi; + /// FRAME root offences pallet. #[cfg(feature = "pallet-root-offences")] pub use pallet_root_offences; @@ -689,7 +709,7 @@ pub use pallet_utility; #[cfg(feature = "pallet-vesting")] pub use pallet_vesting; -/// FRAME pallet for whitelisting call, and dispatch from specific origin. +/// FRAME pallet for whitelisting calls, and dispatching from a specific origin. #[cfg(feature = "pallet-whitelist")] pub use pallet_whitelist; @@ -882,6 +902,10 @@ pub use polkadot_node_subsystem_util; #[cfg(feature = "polkadot-overseer")] pub use polkadot_overseer; +/// Helper library that can be used to build a parachain node. +#[cfg(feature = "polkadot-parachain-lib")] +pub use polkadot_parachain_lib; + /// Types and utilities for creating and working with parachains. #[cfg(feature = "polkadot-parachain-primitives")] pub use polkadot_parachain_primitives;